
package org.tio.sitexxx.server.controller.wx;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.coremedia.iso.IsoFile;
import com.coremedia.iso.boxes.MovieHeaderBox;
import io.agora.media.RtcTokenBuilder2;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
import org.tio.core.Tio;
import org.tio.http.common.HttpRequest;
import org.tio.http.common.UploadFile;
import org.tio.http.server.annotation.RequestPath;
import org.tio.jfinal.kit.Ret;
import org.tio.jfinal.plugin.activerecord.Record;
import org.tio.sitexxx.im.common.ImPacket;
import org.tio.sitexxx.im.common.bs.wx.group.WxGroupOperNtf;
import org.tio.sitexxx.im.server.Ims;
import org.tio.sitexxx.im.server.TioSiteImServerStarter;
import org.tio.sitexxx.im.server.handler.wx.WxChatApi;
import org.tio.sitexxx.im.server.handler.wx.WxSynApi;
import org.tio.sitexxx.im.server.utils.ImUtils;
import org.tio.sitexxx.server.utils.WanJieChatClient;
import org.tio.sitexxx.server.utils.WebUtils;
import org.tio.sitexxx.server.vo.RequestAiResultVo;
import org.tio.sitexxx.service.Command;
import org.tio.sitexxx.service.ThreadUtil;
import org.tio.sitexxx.service.cache.CacheConfig;
import org.tio.sitexxx.service.cache.Caches;
import org.tio.sitexxx.service.model.main.*;
import org.tio.sitexxx.service.service.ConfService;
import org.tio.sitexxx.service.service.base.IpInfoService;
import org.tio.sitexxx.service.service.base.LoginLogService;
import org.tio.sitexxx.service.service.base.UserService;
import org.tio.sitexxx.service.service.chat.*;
import org.tio.sitexxx.service.service.chat.ChatMsgService.MsgTemplate;
import org.tio.sitexxx.service.utils.ImgUtils;
import org.tio.sitexxx.service.utils.OssUtils;
import org.tio.sitexxx.service.utils.RetUtils;
import org.tio.sitexxx.service.utils.UploadUtils;
import org.tio.sitexxx.service.vo.*;
import org.tio.sitexxx.service.vo.Const.GroupRole;
import org.tio.sitexxx.service.vo.RetCode.CommonCode;
import org.tio.sitexxx.service.vo.call.CallLogin;
import org.tio.sitexxx.service.vo.call.UserStatus;
import org.tio.sitexxx.service.vo.wx.SynRecordVo;
import org.tio.sitexxx.service.vo.wx.SysMsgVo;
import org.tio.sitexxx.service.vo.wx.WxCallItemVo;
import org.tio.sitexxx.service.vo.wx.WxForbiddenVo;
import org.tio.utils.cache.ICache;
import org.tio.utils.json.Json;
import org.tio.utils.lock.MapWithLock;
import org.tio.utils.lock.SetWithLock;
import org.tio.utils.resp.Resp;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;

/**
 * 新版聊天短链接入口
 * @author xufei
 * 2020年1月6日 下午7:41:13
 */
@RequestPath(value = "/chat")
public class ChatController {

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

    private final static FriendService friendService = FriendService.me;

    private final static ChatService chatService = ChatService.me;

    private final static GroupService groupService = GroupService.me;


    /**********************************************begin-query**************************************************/

    /**
     * 会话列表请求-此处未进行缓存处理，尽量不要频繁刷新该请求-已调整
     * @param request
     * @return
     * @author xufei
     * 2020年1月21日 下午5:11:45
     */
    @RequestPath(value = "/list")
    public Resp list(HttpRequest request) {
        Integer uid = WebUtils.currUserId(request);
        Ret ret = chatService.chatItemList(uid);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.getOkList(ret));
    }

    /**
     * 会话列表请求-更新群头像-已调整
     * @param request
     * @return
     * @author xufei
     * 2020年1月21日 下午5:11:45
     */
    @RequestPath(value = "/updateGroupAvatar")
    public Resp updateGroupAvatar(HttpRequest request,Long groupId,String url) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("未登录");
        }
        Ret ret = GroupService.me.modifyUploadAvatar(curr.getId(), groupId, url);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        //消息触发
        ThreadUtil.getBsExecutor().execute(() -> {
            try {
                WxChatApi.updateGroupAvatar(curr, groupId, url);
            } catch (Exception e){
                log.error("", e);
            }
        });
        return Resp.ok();
    }

    @RequestPath(value = "/requestAiResult")
    public Resp requestAiResult(HttpRequest request, String content) {
        if (StrUtil.isEmpty(content)) {
            return Resp.fail("参数为空");
        }
        WanJieChatClient client = new WanJieChatClient();
        String result = "";
        try {
            WanJieChatClient.ChatResponse response = client.sendChatMessage(content);
            result = response.toString();
            System.out.println("AI 回复: " + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
        RequestAiResultVo vo = new RequestAiResultVo();
        vo.content = result;
        vo.avatar = "";
        vo.name = "小蜜蜂";
        vo.time = System.currentTimeMillis();
        return Resp.ok(vo);
    }


    /**
     * 会话信息-已调整
     * @param request
     * @param chatlinkid
     * @return
     * @author xufei
     * 2020年2月25日 下午4:52:08
     */
    @RequestPath(value = "/chatInfo")
    public Resp chatInfo(HttpRequest request, Long chatlinkid) {
        Integer userId = WebUtils.currUserId(request);
        if (chatlinkid == null) {
            return Resp.fail("会话id参数为空");
        }
        if (chatlinkid <= 0) {
            //处理群的chatlinkid
            WxChatGroupItem groupItem = ChatIndexService.chatGroupIndex(userId, -chatlinkid);
            if (groupItem == null || groupItem.getChatlinkid() == null) {
                return Resp.fail("会话id为空");
            }
            chatlinkid = groupItem.getChatlinkid();
        }

        /**
         * 此处没有缓存,后续根据数据变更频率，可以替换成getBaseInfo
         */
        WxChatItems chatItems = chatService.getBaseChatItems(chatlinkid);
        if (chatItems == null) {
            return Resp.fail("会话不存在");
        }
        if (!Objects.equals(userId, chatItems.getUid())) {
            log.error("异常登录请求会话信息：登录uid：{}，请求会话的uid：{}", userId, chatItems.getUid());
            return Resp.fail(RetUtils.GRANT_ERROR);
        }
        return Resp.ok(chatItems);
    }

    /**
     * 群信息-5分钟缓存-已调整
     * @param request
     * @param groupid
     * @param userflag,用户标识-1:获取用户的群用户信息
     * @return
     * @author xufei
     * 2020年12月22日 下午2:50:32
     */
    @RequestPath(value = "/group")
    public Resp group(HttpRequest request, Long groupid, Short userflag) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Ret ret = groupService.groupInfo(groupid, Objects.equals(userflag, Const.YesOrNo.YES) ? curr.getId() : null);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(ret);
    }

    @RequestPath(value = "/searchMsg")
    public Resp searchMsg(HttpRequest request, String searchKey){
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("未登录");
        }
        List<Record> ret = ChatMsgService.me.msgList(curr.getId(), searchKey);
        return Resp.ok(ret);
    }

    /**
     * 是否是好友-已调整
     * @param request
     * @param touid
     * @return
     * @author xufei
     * 2020年2月5日 上午10:52:25
     */
    @RequestPath(value = "/isFriend")
    public Resp isFriend(HttpRequest request, Integer touid) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Ret ret = friendService.isFriend(curr.getId(), touid);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.getOkData(ret));
    }

    /**
     * 好友的申请列表-已调整
     * @param request
     * @return
     * @author xufei
     * 2020年2月4日 下午4:30:55
     */
    @RequestPath(value = "/applyList")
    public Resp applyList(HttpRequest request) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Ret ret = FriendApplyService.me.applyList(curr.getId());
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.getOkList(ret));
    }

    /**
     * 用户的被邀请好友的条数-已调整
     * @param request
     * @return
     * @author xufei
     * 2020年2月4日 下午4:37:08
     */
    @RequestPath(value = "/applyData")
    public Resp applyData(HttpRequest request) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Ret ret = FriendApplyService.me.applyData(curr.getId());
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.getOkData(ret));
    }

    /**
     * 申请信息-已调整
     * @param applyid
     * @return
     * @author xufei
     * 2020年2月25日 下午9:24:00
     */
    @RequestPath(value = "/applyInfo")
    public Resp applyInfo(Integer applyid) {
        Ret ret = FriendApplyService.me.applyInfo(applyid);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        Record record = RetUtils.getOkTData(ret);
        if (record == null) {
            return Resp.fail().msg("申请记录不存在");
        }
        return Resp.ok(record);
    }

    /**
     * 通讯录-无参数缓存-已调整
     * @param request
     * @param mode
     * @return
     * @author xufei
     * 2020年2月4日 下午2:07:14
     */
    @RequestPath(value = "/mailList")
    public Resp mailList(HttpRequest request, Short mode, String searchkey, Integer pageNumber) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("未登录");
        }
        Ret ret = chatService.mailList(curr.getId(), mode, searchkey, pageNumber);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.getOkData(ret));
    }

    /**
     * 搜索列表
     * @param request
     * @param mode
     * @return
     * @author xufei
     * 2020年2月4日 下午2:07:14
     */
    @RequestPath(value = "/searchList")
    public Resp searchList(HttpRequest request, Short mode, String search) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("未登录");
        }
        Ret ret = chatService.searchList(curr.getId(), mode, search);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.getOkData(ret));
    }

    /**
     * 通讯录 分组
     * @param request
     * @param mode
     * @param searchkey
     * @param pageNumber
     * @return
     * @throws Exception
     */
    @RequestPath(value = "/mailListByGroup")
    public Resp mailListByGroup(HttpRequest request, Short mode, String searchkey, Integer pageNumber) {
        Integer userId = WebUtils.currUserId(request);
        Ret ret = chatService.mailListByGroup(userId, mode, searchkey, pageNumber);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.getOkData(ret));
    }

    /**
     * 获取分组列表
     * @param request 请求
     * @return
     */
    @RequestPath(value = "/gList")
    public Resp gList(HttpRequest request, Integer type) {
        User curr = WebUtils.currUser(request);
        return chatService.gList(curr, type);
    }

    /**
     * 改变分割
     * @param request 请求
     * @param type 类型
     * @param gid 分组序号
     * @param id 序号
     * @return
     */
    @RequestPath(value = "/changeDivide")
    public Resp changeDivide(HttpRequest request, Integer type, Integer clazz, Long gid, Long id) {
        User curr = WebUtils.currUser(request);
        Ret ret = chatService.changeDivide(curr.getId(), type, clazz, gid, id);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.getOkData(ret));
    }

    @RequestPath(value = "/moveDivide")
    public Resp moveDivide(HttpRequest request, Integer clazz, Long gid, Long id) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("未登录");
        }
        Ret ret = chatService.moveDivide(curr.getId(), clazz, gid, id);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.getOkData(ret));
    }

    /**
     * 获取群可邀请的好友列表-已调整
     * @param request
     * @param searchkey
     * @param groupid
     * @return
     * @author xufei
     * 2020年2月20日 下午11:33:54
     */
    @RequestPath(value = "/applyGroupFdList")
    public Resp applyGroupFdList(HttpRequest request, String searchkey, Long groupid) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Ret ret = friendService.getOutGroupFdList(curr, searchkey, groupid);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.getOkData(ret));
    }

    /**
     * @param request
     * @return
     * @author xufei
     * 2021年1月5日 上午10:08:58
     */
    @RequestPath(value = "/forbidden")
    public Resp forbidden(HttpRequest request, WxForbiddenVo forbiddenVo) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        if (forbiddenVo == null || forbiddenVo.checkIsNull()) {
            return RetUtils.getInvalidResp();
        }
        Ret ret = groupService.forbidden(forbiddenVo, curr.getId());
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        ThreadUtil.getBsExecutor().execute(() -> {
            try {
                SysMsgVo vo;
                if (Objects.equals(forbiddenVo.getMode(), WxForbiddenVo.Mode.ALL)) {
                    vo = new SysMsgVo(curr.getNick(), Const.MsgTemplate.BAN, "", Const.MsgTemplateKey.GROUP_BAN);
                    if (Objects.equals(forbiddenVo.getOper(), WxForbiddenVo.Oper.CANCEL)) {
                        vo = new SysMsgVo(curr.getNick(), Const.MsgTemplate.CANCEL_BAN, "", Const.MsgTemplateKey.GROUP_CANCEL_BAN);
                    }
                    WxChatApi.sendGroupMsgEach(vo.toText(), Const.ContentType.TEXT, curr.getId(), forbiddenVo.getGroupid(), Const.YesOrNo.YES, vo);
                } else {
                    User user = UserService.ME.getById(forbiddenVo.getUid());
                    vo = new SysMsgVo(curr.getNick(), Const.MsgTemplate.CANCEL_FORBIDDEN, user.getNick(), Const.MsgTemplateKey.GROUP_CANCEL_FORBIDDEN);
                    if (!Objects.equals(forbiddenVo.getOper(), WxForbiddenVo.Oper.CANCEL)) {
                        vo = new SysMsgVo(curr.getNick(), Const.MsgTemplate.FORBIDDEN, user.getNick(), Const.MsgTemplateKey.GROUP_FORBIDDEN);
                    }
                    vo.setSrctext(user.getId()+"");
                    //WxChatApi.sendGroupMsgEach(request, vo.toText(), Const.ContentType.TEXT, curr.getId(), forbiddenVo.getGroupid(), Const.YesOrNo.YES, vo);
                }
            } catch (Exception e) {
                log.error("", e);
            }
        });
        return Resp.ok();
    }

    /**
     * 群用户针对当前用户的状态，该接口为了扩展，命名不符合规范
     * 1、grant：管理员权限：ture:是；
     * 2、rolegrant:角色管理权限
     * @param request
     * @param uid
     * @param groupid
     * @return
     * @author xufei
     * 2021年1月6日 上午11:39:44
     */
    @RequestPath(value = "/forbiddenFlag")
    public Resp forbiddenFlag(HttpRequest request, Integer uid, Long groupid) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Map<String, String> flagMap = new HashMap<>();
        flagMap.put("grant", Const.YesOrNo.NO + "");
        flagMap.put("rolegrant", Const.YesOrNo.NO + "");
        flagMap.put("kickgrant", Const.YesOrNo.NO + "");
        flagMap.put("userstatus", Const.YesOrNo.YES + "");
        if (Objects.equals(uid, curr.getId())) {
            return Resp.ok(flagMap);
        }
        WxGroup group = GroupService.me.getByGroupid(groupid);
        if (group == null) {
            return Resp.ok(flagMap);
        }
        WxChatGroupItem groupItem = ChatIndexService.chatGroupIndex(curr.getId(), groupid);
        if (!ChatService.groupChatLink(groupItem)) {
            return Resp.ok(flagMap);
        }
        WxGroupUser groupUser = GroupService.me.getGroupUser(groupItem.getGpulinkid());
        if (Objects.equals(groupUser.getGrouprole(), Const.GroupRole.MEMBER)) {
            return Resp.ok(flagMap);
        }
        WxChatGroupItem toGroupItem = ChatIndexService.chatGroupIndex(uid, groupid);
        if (!ChatService.groupChatLink(toGroupItem)) {
            flagMap.put("userstatus", Const.YesOrNo.NO + "");
            return Resp.ok(flagMap);
        }
        WxGroupUser toGroupUser = GroupService.me.getGroupUser(toGroupItem.getGpulinkid());
        if (Objects.equals(groupUser.getGrouprole(), Const.GroupRole.OWNER)) {
            flagMap.put("grouprole", toGroupUser.getGrouprole() + "");
            flagMap.put("rolegrant", Const.YesOrNo.YES + "");
        } else {
            if (!Objects.equals(toGroupUser.getGrouprole(), Const.GroupRole.MEMBER)) {
                return Resp.ok(flagMap);
            }
        }
        flagMap.put("grant", Const.YesOrNo.YES + "");
        flagMap.put("flag", toGroupUser.getForbiddenflag() + "");
        flagMap.put("kickgrant", Const.YesOrNo.YES + "");
        return Resp.ok(flagMap);
    }

    /**
     * 群用户列表-已调整
     * 1、精准查询无缓存
     * 2、无条件查询存在缓存
     * @param request
     * @param groupid
     * @param pageNumber
     * @return
     * @author xufei
     * 2020年4月7日 下午3:17:36
     */
    @RequestPath(value = "/groupUserList")
    public Resp groupUserList(HttpRequest request, Long groupid, Integer pageNumber, String searchkey) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("未登录");
        }
        Ret ret = groupService.groupUserList(curr.getId(), groupid, pageNumber, searchkey);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.getOkPage(ret));
    }

    /**
     * 禁言用户列表
     * @param groupid
     * @param pageNumber
     * @param searchkey
     * @return
     * @author xufei
     * 2021年1月5日 上午11:49:07
     */
    @RequestPath(value = "/forbiddenUserList")
    public Resp forbiddenUserList(Long groupid, Integer pageNumber, String searchkey) {
        Ret ret = groupService.forbiddenUserList(groupid, pageNumber, searchkey);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.getOkPage(ret));
    }

    /**
     * at使用的群用户列表-暂时没有算法处理-已调整
     * @param request
     * @param groupid
     * @param searchkey
     * @return
     * @author xufei
     * 2020年4月7日 下午5:55:13
     */
    @RequestPath(value = "/atGroupUserList")
    public Resp atGroupUserList(HttpRequest request, Long groupid, String searchkey) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Ret ret = groupService.atGroupUserList(groupid, searchkey, curr.getId());
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.getOkList(ret));
    }

    /**
     * 私聊的消息
     * @param request
     * @param chatlinkid
     * @return
     * @author xufei
     * 2020年2月1日 下午7:30:38
     * 查看p2pMsgListGzip
     */
    @Deprecated
    @RequestPath(value = "/p2pMsgList")
    public Resp p2pMsgList(HttpRequest request, Long chatlinkid, Long startmid, Long tomid, Long endmid) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Ret ret = ChatMsgService.me.p2pMsgList(chatlinkid, curr.getId(), startmid, endmid, tomid);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        List<WxFriendMsg> msg = RetUtils.getOkTData(ret);
        if (msg.size() <= 0 && startmid == null && endmid == null) {
            System.out.println("p2pMsgList chatId:"+chatlinkid+", curr:"+curr.getId());
        }
        return Resp.ok(msg);
    }

    /**
     * 群聊消息
     * @param request
     * @param chatlinkid
     * @param startmid
     * @return
     * @author xufei
     * 2020年2月13日 下午9:56:14
     */
    @RequestPath(value = "/groupMsgList")
    public Resp groupMsgList(HttpRequest request, Long chatlinkid, Long startmid) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Integer uid = curr.getId();
        //处理群的chatlinkid
        WxChatGroupItem groupItem = ChatIndexService.chatGroupIndex(uid, Math.abs(chatlinkid));
        if (groupItem == null || groupItem.getChatlinkid() == null) {
            return Resp.fail("会话为空");
        }
        chatlinkid = groupItem.getChatlinkid();
        Ret ret = ChatMsgService.me.groupMsgList(chatlinkid, groupItem.getGroupid(), uid, startmid, null, null);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        List<WxGroupMsg> msg = RetUtils.getOkTData(ret);
        if (msg.size() <= 0 && startmid == null) {
            System.out.println("groupMsgList chatId:"+chatlinkid+", curr:"+uid+", groupid:"+groupItem.getGroupid());
            msg = new ArrayList<>();
        }
        return Resp.ok(msg);
    }

    /**
     * 进群-异步触发（名片和其它申请的不可异步） - 已调整
     * @param request
     * @param uids
     * @param groupid
     * @param applyuid 群名片的实际用户
     * @return
     * @throws Exception
     * @author xufei
     * 2020年12月22日 下午4:10:55
     */
    @RequestPath(value = "/joinGroup")
    public Resp joinGroup(HttpRequest request, String uids, Long groupid, Integer applyuid, Boolean dealapply) throws Exception {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail(Const.ErrorCode.NOT_LOGIN);
        }
        if (dealapply == null) {
            dealapply = false;
        }
        String[] uidArr = StrUtil.splitToArray(uids, ",");
        if (!dealapply) {
            for (String string : uidArr) {
                if(StrUtil.isEmpty(string)) {
                    continue;
                }
                Integer value = Integer.valueOf(string);
                Ret ret = friendService.isFriend(curr.getId(), value);
                if (ret.isFail()) {
                    return Resp.fail().msg(RetUtils.getRetMsg(ret));
                }
            }
        }
        if (applyuid != null) {
            Ret ret = groupService.joinGroup(curr, groupid, uids, applyuid, dealapply);
            if (ret.isFail()) {
                return Resp.fail(RetUtils.getRetMsg(ret));
            }
            ThreadUtil.getBsExecutor().execute(() -> {
                Short joinnum = RetUtils.getOkTData(ret, "joinnum");
                if (joinnum != 0) {
                    User msgUser = UserService.ME.getById(applyuid);
                    try {
                        WxChatApi.joinGroup(msgUser, groupid, RetUtils.getOkTData(ret), RetUtils.getOkTData(ret, "rebind"), false);
                    } catch (Exception e) {
                        log.error("", e);
                    }
                }
            });
        } else {
            boolean dealapplyfinal = dealapply;
            //异步处理触发
            ThreadUtil.getBsExecutor().execute(() -> {
                try {
                    Ret ret = groupService.joinGroup(curr, groupid, uids, applyuid, dealapplyfinal);
                    if (ret.isFail()) {
                        WxChatApi.sendFriendErrorMsg(curr.getId(), curr.getId(), curr.getId(), -groupid, AppCode.GroupErrorCode.SYS_ERROR, RetUtils.getRetMsg(ret));
                        return;
                    }
                    Short joinnum = RetUtils.getOkTData(ret, "joinnum");
                    if (joinnum != null && joinnum != 0) {
                        WxChatApi.joinGroup(curr, groupid, RetUtils.getOkTData(ret), RetUtils.getOkTData(ret, "rebind"), false);
                    }
                } catch (Exception e) {
                    log.error("", e);
                }
            });
        }
        return Resp.ok(RetUtils.OPER_RIGHT);
    }

    /**
     * 邀请用户进群申请
     * @param request
     * @param uids
     * @param groupid
     * @param applymsg
     * @return
     * @throws Exception
     * @author xufei
     * 2021年1月13日 下午4:07:11
     */
    @RequestPath(value = "/joinGroupApply")
    public Resp joinGroupApply(HttpRequest request, String uids, Long groupid, String applymsg) throws Exception {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Ret checkRet = groupService.checkJoinGroup(curr.getId(), groupid, null, null, false, uids.split(",").length);
        if (checkRet.isOk()) {
            return joinGroup(request, uids, groupid, null, false);
        }
        String[] uidArr = StrUtil.splitToArray(uids, ",");
        for (String string : uidArr) {
            Integer value = Integer.valueOf(string);
            Ret ret = friendService.isFriend(curr.getId(), value);
            if (ret.isFail()) {
                return Resp.fail().msg(RetUtils.getRetMsg(ret));
            }
        }
        Ret ret = groupService.joinGroupApply(curr.getId(), groupid, uids, applymsg);

        if (ret.isFail()) {
            return Resp.fail(RetUtils.getRetMsg(ret));
        }
        //消息触发
        ThreadUtil.getBsExecutor().execute(() -> {
            try {
                WxGroupApply apply = RetUtils.getOkTData(ret, "apply");
                Integer count = RetUtils.getOkTData(ret);
                WxGroup group = RetUtils.getOkTData(ret, "group");
                Ret ret1 = WxChatApi.joinGroupApply(curr, group, count, apply);
                if (ret1.isFail()) {
                    log.error(RetUtils.getRetMsg(ret1));
                }
            } catch (Exception e) {
                log.error("", e);
            }
        });
        return Resp.ok(RetUtils.OPER_RIGHT);
    }

    /**
     * 获取群申请信息
     * @param aid
     * @return
     * @author xufei
     * 2021年1月19日 上午10:07:37
     */
    @RequestPath(value = "/groupApplyInfo")
    public Resp groupApplyInfo(Integer aid) {
        Ret ret = groupService.groupApplyInfo(aid);
        if (ret.isFail()) {
            return Resp.fail(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.getOkData(ret));
    }

    /**
     * 群审核列表
     * @param request 请求
     * @return
     */
    @RequestPath(value = "/groupApplyList")
    public Resp groupApplyList(HttpRequest request, Long groupId) {
        Integer curr = WebUtils.currUserId(request);
        Ret ret = groupService.groupApplyList(curr, groupId);
        if (ret.isFail()) {
            return Resp.fail(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.getOkData(ret));
    }

    /**
     * 处理邀请用户进群申请
     * @param request
     * @param aid
     * @return
     * @throws Exception
     * @author xufei
     * 2021年1月13日 下午5:42:10
     */
    @RequestPath(value = "/dealGroupApply")
    public Resp dealGroupApply(HttpRequest request, Integer aid, Long mid) throws Exception {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Ret ret = groupService.dealGroupApply(curr, aid, mid);
        if (ret.isFail()) {
            return Resp.fail(RetUtils.getRetMsg(ret));
        }
        Long newMid = RetUtils.getOkTData(ret, "mid");
        Short dealflag = RetUtils.getOkTData(ret);
        if (Objects.equals(dealflag, Const.YesOrNo.NO)) {
            WxGroupApply apply = RetUtils.getOkTData(ret, "apply");
            String uids = RetUtils.getOkTData(ret, "uids");
            joinGroup(request, uids, apply.getGroupid(), apply.getOperuid(), true);
            return Resp.ok(newMid);
        }
        return Resp.ok(newMid);
    }

    /**
     * 群邀请开关修改-已调整
     * @param request
     * @param groupid
     * @param mode
     * @return
     * @author xufei
     * 2020年3月9日 下午4:32:55
     */
    @RequestPath(value = "/modifyApply")
    public Resp modifyApply(HttpRequest request, Long groupid, Short mode) {
        String text = "";
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        SysMsgVo sysMsgVo = new SysMsgVo(curr.getNick(), "", "", "");
        switch (mode) {
            case Const.YesOrNo.YES:
                text = MsgTemplate.applyopen;
                sysMsgVo.setMsgbody(text);
                sysMsgVo.setMsgkey("applyopen");
                break;
            case Const.YesOrNo.NO:
                text = MsgTemplate.applyclose;
                sysMsgVo.setMsgbody(text);
                sysMsgVo.setMsgkey("applyclose");
                break;
            default:
                return Resp.fail().msg("无效入群方式");
        }
        Ret ret = groupService.modifyApply(curr, groupid, mode);

        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        //消息触发
        ThreadUtil.getBsExecutor().execute(() -> {
            try {
                WxChatApi.sendGroupMsgEach(sysMsgVo.toText(), Const.ContentType.TEXT, curr.getId(), groupid, Const.YesOrNo.YES, sysMsgVo);
            } catch (Exception e) {
                log.error("", e);
            }
        });
        return Resp.ok(RetUtils.OPER_RIGHT);
    }

    /**
     * 修改群昵称-已调整
     * @param request
     * @param groupid
     * @param nick
     * @return
     * @author xufei
     * 2020年4月8日 上午11:38:47
     */
    @RequestPath(value = "/modifyGroupNick")
    public Resp modifyGroupNick(HttpRequest request, Long groupid, String nick) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Ret ret = groupService.modifyGroupNick(groupid, nick, curr.getId());
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        ThreadUtil.getBsExecutor().execute(() -> {
            try {
                WxChatApi.synGroupInfo(curr.getId(), groupid, RetUtils.getOkTData(ret));
            } catch (Exception e) {
                log.error("", e);
            }
        });
        return Resp.ok(RetUtils.OPER_RIGHT);
    }

    /**
     * TODO-XUFEI-750-主动离开群通知-已调整
     * 主动离开群
     * @param request
     * @param groupid
     * @return
     * @throws Exception
     * @author xufei
     * 2020年2月25日 下午5:49:30
     */
    @RequestPath(value = "/leaveGroup")
    public Resp leaveGroup(HttpRequest request, Integer uid, Long groupid) throws Exception {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Ret ret = groupService.leaveGroup(curr, groupid);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        Tio.unbindGroup(TioSiteImServerStarter.tioServerConfigWs, curr.getId() + "", groupid + "");
        //自己退群操作
        WxGroupOperNtf leaveNtf = new WxGroupOperNtf();
        leaveNtf.setC("自动退群");
        leaveNtf.setMid(null);
        leaveNtf.setT(System.currentTimeMillis());
        leaveNtf.setUid(curr.getId());
        leaveNtf.setG(groupid);
        leaveNtf.setChatlinkid(-groupid);
        leaveNtf.setOper(Const.WxGroupOper.LEAVE_GROUP);
        ImPacket imPacket = new ImPacket(Command.WxGroupOperNtf, Json.toJson(leaveNtf));
        Ims.sendToUser(curr.getId(), imPacket);
        //消息触发
        ThreadUtil.getBsExecutor().execute(() -> {
            try {
                WxChatGroupItem newowner = RetUtils.getOkTData(ret, "owner");
                WxChatGroupItem leave = RetUtils.getOkTData(ret, "leave");
                WxChatApi.leaveGroup(curr, leave, newowner, false);
            } catch (Exception e) {
                log.error("", e);
            }
        });
        return Resp.ok(RetUtils.OPER_RIGHT);
    }

    /**
     * 踢人-已调整
     * @param request
     * @param uids
     * @param groupid
     * @return
     * @throws Exception
     * @author xufei
     * 2020年2月25日 下午5:57:34
     */
    @RequestPath(value = "/kickGroup")
    public Resp kickGroup(HttpRequest request, String uids, Long groupid) throws Exception {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("未登录");
        }
        Ret ret = groupService.kickGroup(curr.getId(), groupid, uids);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        Short joinnum = RetUtils.getOkTData(ret, "joinnum");
        if (joinnum != 0) {
            //消息触发
            ThreadUtil.getBsExecutor().execute(() -> {
                try {
                    List<WxChatGroupItem> kickGroupItem = RetUtils.getOkTData(ret, "kick");
                    String nickStr = RetUtils.getOkTData(ret);
                    WxChatApi.kickGroup(curr, groupid, kickGroupItem, nickStr, false);
                } catch (Exception e) {
                    log.error("", e);
                }
            });
        }
        return Resp.ok(RetUtils.OPER_RIGHT);
    }

    /**
     * 创建群-已调整
     * 1、群邀请人数过多只依赖前端控制
     * 2、群消息不是同步发送的，会出现延迟消息
     * @param request
     * @param wxGroup
     * @param uidList
     * @return
     * @throws Exception
     * @author xufei
     * 2020年2月13日 下午5:24:05
     */
    @RequestPath(value = "/createGroup")
    public Resp createGroup(HttpRequest request, WxGroup wxGroup, String uidList,String md5Uid) throws Exception {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        if (Objects.equals(curr.getRealnameflag(), Const.YesOrNo.NO)) {
            return Resp.fail("创建群组需实名认证");
        }
        String name = "";
        if (StrUtil.isNotBlank(wxGroup.getName())) {
            name = wxGroup.getName();
            name = name.length() > 30 ? name.substring(0, 30) : name;
            wxGroup.setName(name);
            wxGroup.setAutoflag(Const.YesOrNo.NO);
        }

        String realUidList = uidList;
        String[] uidArr = StrUtil.splitToArray(uidList, ",");
        for (String string : uidArr) {
            Integer value = Integer.valueOf(string);
            Ret ret = friendService.isFriend(curr.getId(), value);
            if (ret.isFail()) {
                return Resp.fail().msg(RetUtils.getRetMsg(ret));
            }
        }
        Ret checkRet = WxChatApi.checkCreateGroupRegLimit(curr);
        if (checkRet.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(checkRet));
        }
        Ret imgRet = getGroupImg(uidArr, curr, name);
        String nicks = RetUtils.getOkTData(imgRet, "nicks");
        if (StrUtil.isBlank(name)) {
            name = RetUtils.getOkTData(imgRet, "name");
            wxGroup.setName(name);
            wxGroup.setAutoflag(Const.YesOrNo.YES);
        }
        Short devicetype = WebUtils.getRequestExt(request).getDeviceType();
        String sessionid = request.getHttpSession().getId();
        String ip = request.getClientIp();
        short joinnum = (short) (uidArr.length + 1);
        wxGroup.setCreatetime(new Date());
        Ret ret = groupService.createGroup(curr, wxGroup, nicks, devicetype, sessionid, ip, joinnum, WebUtils.getRequestExt(request).getAppVersion());
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        //消息触发
        ThreadUtil.getBsExecutor().execute(() -> {
            try {
                WxGroupMsg msg = RetUtils.getOkTData(ret, "msg");
                SysMsgVo sysMsgVo = RetUtils.getOkTData(ret, "sysmsgvo");
                //发送自己的群信息
                WxChatApi.creatGroupOwner(curr, wxGroup, msg, sysMsgVo);
                //其它人的消息触发
                WxChatApi.creatGroup(curr, realUidList, nicks, wxGroup, msg, sysMsgVo);
            } catch (Exception e) {
                log.error("", e);
            }
        });
        return Resp.ok(wxGroup);
    }

    /**
     * 	好友申请-已调整
     * @param request
     * @param touid
     * @param greet
     * @return
     * @author xufei
     * 2020年1月15日 下午5:22:23
     */
    @RequestPath(value = "/friendApply")
    public Resp friendApply(HttpRequest request, Integer touid, String greet) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        if (Objects.equals(curr.getIsBack(), 0)) {
            if (Objects.equals(curr.getRealnameflag(), Const.YesOrNo.NO)) {
                return Resp.fail("添加好友需实名认证");
            }
        }
        Ret ret = friendService.addApply(curr, touid, greet);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        WxFriendApplyItems applyItems = RetUtils.getOkTData(ret);
        //发送申请
        ThreadUtil.getBsExecutor().execute(() -> {
            try {
                if (WxSynApi.isSynVersion()) {
                    WxSynApi.synFriendApply(touid, applyItems, SynRecordVo.SynType.ADD);
                } else {
                    WxChatApi.useSysChatNtf(touid, Const.WxSysCode.APPLY_REQUEST, curr.getNick() + " 想要成为你的好友：" + applyItems.getGreet(), applyItems.getId() + "");
                }

            } catch (Exception e) {
                log.error("", e);
            }
        });
        return Resp.ok(applyItems);
    }

    /**
     * 	主动加好友-已调整
     * 不激活聊天列表-也不发送通知
     * @param request
     * @param touid
     * @return
     * @throws Exception
     * @author xufei
     * 2020年1月15日 下午5:48:55
     */
    @RequestPath(value = "/addFriend")
    public Resp addFriend(HttpRequest request, Integer touid) throws Exception {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        if (Objects.equals(curr.getIsBack(), 0)) {
            if (Objects.equals(curr.getRealnameflag(), Const.YesOrNo.NO)) {
                return Resp.fail("添加好友需实名认证");
            }
        }
        Ret ret = friendService.addFriend(curr, touid);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        //消息处理
        ThreadUtil.getBsExecutor().execute(() -> {
            try {
                WxFriend friend = RetUtils.getOkTData(ret, "friend");
                WxFriend toFriend = RetUtils.getOkTData(ret, "tofriend");
                WxChatApi.friendChangeAddNtf(friend, toFriend);
            } catch (Exception e) {
                log.error("", e);
            }
        });
        return Resp.ok(RetUtils.getOkData(ret));
    }

    /**
     * 	主动加好友-已调整
     * 不激活聊天列表-也不发送通知
     * @param uid
     * @param touid
     * @return
     * @throws Exception
     * @author xufei
     * 2020年1月15日 下午5:48:55
     */
    public Resp addDefaultFriend(Integer uid, Integer touid) throws Exception {
        User curr = User.dao.findById(uid);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Ret ret = friendService.addFriend(curr, touid);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        //消息处理
        ThreadUtil.getBsExecutor().execute(() -> {
            try {
                WxFriend friend = RetUtils.getOkTData(ret, "friend");
                WxFriend toFriend = RetUtils.getOkTData(ret, "tofriend");
                WxChatApi.friendChangeAddNtf(friend, toFriend);
            } catch (Exception e) {
                log.error("", e);
            }
        });
        return Resp.ok(RetUtils.getOkData(ret));
    }

    /**
     * 删除好友-已调整
     * @param request
     * @param touid
     * @return
     * @throws Exception
     * @author xufei
     * 2020年1月22日 下午4:23:20
     */
    @SuppressWarnings("deprecation")
    @RequestPath(value = "/delFriend")
    public Resp delFriend(HttpRequest request, Integer touid) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Integer uid = curr.getId();
        if (Objects.equals(uid, touid)) {
            return Resp.fail("不能删除自己");
        }
        IpInfo ipInfo = IpInfoService.ME.save(request.getClientIp());
        Ret ret = friendService.delFriend(curr, touid, ipInfo.getId());
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        Long chatlinkid = RetUtils.getOkTData(ret, "chatlinkid");
        Long tochatlinkid = RetUtils.getOkTData(ret, "tochatlinkid");
        if (WxSynApi.isSynVersion()) {
            WxSynApi.synFriendDel(uid, touid, chatlinkid, tochatlinkid, RetUtils.getOkTData(ret, "fid"));
        }
        else {
            if (chatlinkid != null) {
                WxChatApi.userChatOper(uid, chatlinkid, Const.WxUserOper.DEL_FRIEND, "删除好友", "", null);
            }
            if (tochatlinkid != null) {
                WxChatApi.userChatOper(touid, tochatlinkid, Const.WxUserOper.TO_DEL_FRIEND, "被删除好友", "", null);
            }
            WxChatApi.friendChangeDelNtf(uid, RetUtils.getOkTData(ret, "fid"));
        }
        return Resp.ok(RetUtils.getOkData(ret));
    }

    /**
     * 删除群-已调整
     * 未进行信息用户处理-TODO:XUFEI
     * @param request
     * @param uid
     * @param groupid
     * @return
     * @throws Exception
     * @author xufei
     * 2020年2月24日 上午10:28:20
     */
    @RequestPath(value = "/delGroup")
    public Resp delGroup(HttpRequest request, Integer uid, Long groupid) throws Exception {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        if (!Objects.equals(curr.getId(), uid)) {
            return Resp.fail(RetUtils.LOGIN_ERROR);
        }
        Ret ret = groupService.delGroup(curr, groupid);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        //消息处理
        ThreadUtil.getBsExecutor().execute(() -> {
            try {
                WxChatGroupItem groupItem = RetUtils.getOkTData(ret, "groupitem");
                WxGroup group = RetUtils.getOkTData(ret, "group");
                WxGroupUser groupUser = RetUtils.getOkTData(ret, "groupuser");
                WxChatApi.delGroup(request, curr, groupItem, group, groupUser);
            } catch (Exception e) {
                log.error("", e);
            }
        });
        return Resp.ok(RetUtils.okOper());
    }

    /**
     * 转让群-已调整
     * @param request
     * @param groupid
     * @return
     * @author xufei
     * 2020年2月24日 下午2:33:22
     */
    @RequestPath(value = "/changeOwner")
    public Resp changeOwner(HttpRequest request, Long groupid, Integer otheruid) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        if (Objects.equals(otheruid, curr.getId())) {
            return Resp.fail().msg("不能对自己操作哦");
        }
        User user = UserService.ME.getById(otheruid);
        if (user == null) {
            return Resp.fail().msg("无效转让用户");
        }
        Ret ret = groupService.changeOwner(curr.getId(), user.getId(), groupid);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        //消息处理
        ThreadUtil.getBsExecutor().execute(() -> {
            try {
                WxChatGroupItem owner = RetUtils.getOkTData(ret, "owner");
                WxChatGroupItem other = RetUtils.getOkTData(ret, "other");
                WxChatApi.changeOwner(curr, owner, other, false);
            } catch (Exception e) {
                log.error("", e);
            }
        });
        return Resp.ok(RetUtils.okOper());
    }

    /**
     * @param request
     * @param groupid
     * @param touid
     * @param freeflag
     * @return
     * @author xufei
     * 2021年1月26日 上午11:12:09
     */
    @RequestPath(value = "/msgfreeflag")
    public Resp msgfreeflag(HttpRequest request, Long groupid, Integer touid, Short freeflag) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Integer uid = curr.getId();
        Ret ret;
        if (groupid != null) {
            ret = groupService.modifyGroupPush(groupid, freeflag, uid);
        } else {
            ret = friendService.modifyFdPush(touid, freeflag, uid);
        }
        if (ret == null || ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        Short chatsyn = RetUtils.getOkTData(ret);
        if (Objects.equals(chatsyn, Const.YesOrNo.YES)) {
            Long chatlinkid = RetUtils.getOkTData(ret, "chatlinkid");
            ChatIndexService.removeChatItemsCache(chatlinkid);
            ThreadUtil.getBsExecutor().execute(() -> {
                try {
                    WxChatItems chatItems = chatService.getBaseChatItems(chatlinkid);
                    if (WxSynApi.isSynVersion()) {
                        WxSynApi.synChatSession(uid, chatItems, SynRecordVo.SynType.UPDATE);
                    } else {
                        WxChatApi.userChatOper(uid, groupid != null ? -groupid : chatlinkid, Const.WxUserOper.CHAT_MSGFREE, "", null, chatItems);
                    }
                } catch (Exception e) {
                    log.error("", e);
                }
            });
        }
        return Resp.ok(RetUtils.okOper());
    }

    /**
     * 上传文件
     * @param request
     * @param uploadFile
     * @return
     * @throws Exception
     * @author xufei
     * 2021年6月7日 上午10:20:34
     */
    @SuppressWarnings("deprecation")
    @RequestPath(value = "/uploadImg")
    public Resp uploadImg(HttpRequest request, UploadFile uploadFile) throws Exception {
        if (uploadFile == null) {
            return Resp.fail("上传信息为空");
        }
        User curr = WebUtils.currUser(request);
        Resp ret = null;
        if (curr == null) {
            ret = Resp.fail("您尚未登录或登录超时").code(AppCode.ForbidOper.NOTLOGIN);
        }
        byte[] imageBytes = uploadFile.getData();
        int maxsize = ConfService.getInt("user.upload.avatar.maxsize", 512);
        if (imageBytes.length > 1024 * maxsize) {
            ret = Resp.fail("文件尺寸不能大于" + maxsize + "KB");
        }
        Img img = ImgUtils.processImg(Const.UPLOAD_DIR.USER_AVATAR, uploadFile);
        img.setComefrom(Img.ComeFrom.WX_UPLOAD);
        img.setStatus((short) 1);
        img.setSession(request.getHttpSession().getId());

        boolean f = img.save();
        if (ret != null) {
            return ret;
        }
        if (f) {
            return Resp.ok(img.getUrl());
        } else {
            return Resp.fail("服务器异常");
        }
    }

    /**
     * 收藏
     * @param request
     * @param mid
     * @return
     * @author xufei
     * 2021年1月26日 下午4:48:24
     */
    @RequestPath(value = "/favorite")
    public Resp favorite(HttpRequest request, Long chatlinkid, Long mid) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        short chatmode = Const.ChatMode.P2P;
        if (chatlinkid <= 0) {
            chatmode = Const.ChatMode.GROUP;
        }
        Ret ret = ChatMsgService.me.msgFavorite(curr, mid, chatmode);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.OPER_RIGHT);
    }

    /**
     * @param request
     * @param type
     * @return
     * @author xufei
     * 2021年1月26日 下午5:10:19
     */
    @RequestPath(value = "/favoritelist")
    public Resp favoritelist(HttpRequest request, Short type, Integer pageNumber) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Ret ret = ChatMsgService.me.favoriteList(curr.getId(), type, pageNumber);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.getOkPage(ret));
    }

    /**
     * 取消收藏
     * @return
     * @author xufei
     * 2021年1月26日 下午5:07:14
     */
    @RequestPath(value = "/cancelfavorite")
    public Resp cancelfavorite(Integer fid) {
        Ret ret = ChatMsgService.me.cancelMsgFavorite(fid);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.OPER_RIGHT);
    }

    /**
     * 操作-已调整
     * 用户拉黑/删除聊天会话/恢复用户拉黑/清空聊天记录
     * @param request
     * @param touid
     * @param oper
     * @param chatlinkid
     * @return
     * @author xufei
     * 2020年1月19日 下午2:20:38
     */
    @RequestPath(value = "/oper")
    public Resp oper(HttpRequest request, Integer touid, Short oper, Long chatlinkid) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Integer uid = curr.getId();
        //处理群的chatlinkid
        Long group = null;
        Long oldChatLinkid = chatlinkid;
        Long  groupId = null;
        Long  toUid = null;
        if (chatlinkid != null && chatlinkid <= 0) {
            group = -chatlinkid;
            WxChatGroupItem groupItem = ChatIndexService.chatGroupIndex(curr.getId(), -chatlinkid);
            if (groupItem == null || groupItem.getChatlinkid() == null) {
                WxChatItems delChatItems = new WxChatItems();
                delChatItems.setChatlinkid(oldChatLinkid);
                WxSynApi.synChatSession(uid, delChatItems, SynRecordVo.SynType.DEL);
                return Resp.ok();
            }
            chatlinkid = groupItem.getChatlinkid();
            groupId = groupItem.getGroupid();
        }else {
            WxChatUserItem chatUserIndex = ChatIndexService.chatUserIndex(chatlinkid);
            if (chatUserIndex != null) {
                toUid = chatUserIndex.getBizid();
            }
        }
        Ret ret = chatService.chatUserOper(oper, uid, touid, chatlinkid);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        final Long fGroup = group;

        final Long fGroupId = groupId;

        final Long ftoUid = toUid;
        //消息处理
        ThreadUtil.getBsExecutor().execute(() -> {
            try {
                WxChatItems chatItems = RetUtils.getOkTData(ret, "chat");
                if (Objects.equals(oper, Const.WxUserOper.DEL_ITEM_REACT) || Objects.equals(oper, Const.WxUserOper.HIDE_CHAT)) {
                    if (chatItems != null && Objects.equals(chatItems.getChatmode(), Const.ChatMode.GROUP)) {
                        Tio.unbindGroup(TioSiteImServerStarter.tioServerConfigWs, uid + "", chatItems.getBizid() + "");
                    }
                }
                if (WxSynApi.isSynVersion()) {
                    switch (oper) {
                        case Const.WxUserOper.BLACK:
                            break;
                        case Const.WxUserOper.REMOVE_BLACK:
                            break;
                        case Const.WxUserOper.DEL_ITEM_REACT:
                            WxChatItems delChatItems = new WxChatItems();
                            delChatItems.setChatlinkid(oldChatLinkid);
                            WxSynApi.synChatSession(uid, delChatItems, SynRecordVo.SynType.DEL);
                            break;
                        case Const.WxUserOper.CLEAR_CHAT_MSG:
                            WxSynApi.synMsgClear(uid, oldChatLinkid);
                            //如果群ID不为空 删除群信息
                            if(fGroupId != null) {
                                WxGroupMsg.dao.deleteByGroupId(fGroupId,uid);
                            }else
                                //如果用户ID不为空 删除群信息
                                if(ftoUid != null) {
                                    friendService.delAllMsg(uid,ftoUid);
                                }
                            break;
                        case Const.WxUserOper.CHAT_TOP:
                            WxChatItems topChatItems = new WxChatItems();
                            topChatItems.setChatlinkid(oldChatLinkid);
                            topChatItems.setTopflag(Const.YesOrNo.YES);
                            topChatItems.setChatuptime(new Date());
                            WxSynApi.synChatSession(uid, topChatItems, SynRecordVo.SynType.UPDATE);
                            break;
                        case Const.WxUserOper.CHAT_CANCEL_TOP:
                            WxChatItems cancelTop = new WxChatItems();
                            cancelTop.setChatlinkid(oldChatLinkid);
                            cancelTop.setTopflag(Const.YesOrNo.NO);
                            cancelTop.setChatuptime(new Date());
                            WxSynApi.synChatSession(uid, cancelTop, SynRecordVo.SynType.UPDATE);
                            break;
                        case Const.WxUserOper.HIDE_CHAT:
                            WxChatItems hidChatItems = new WxChatItems();
                            hidChatItems.setChatlinkid(oldChatLinkid);
                            WxSynApi.synChatSession(uid, hidChatItems, SynRecordVo.SynType.DEL);
                            break;
                    }
                } else {
                    String operBizData = "";
                    Long chatlinkid1 = RetUtils.getOkTData(ret, "chatlinkid");
                    switch (oper) {
                        case Const.WxUserOper.BLACK:
                            break;
                        case Const.WxUserOper.REMOVE_BLACK:
                            if (chatlinkid1 != null) {
                                WxChatItems removeBlack = chatService.getAllChatItems(chatlinkid1);
                                WxChatApi.userChatOper(uid, chatlinkid1, oper, "", operBizData, removeBlack);
                            }
                            break;
                        case Const.WxUserOper.CHAT_TOP:
                            WxChatItems topChatItems = new WxChatItems();
                            topChatItems.setChatlinkid(oldChatLinkid);
                            topChatItems.setTopflag(Const.YesOrNo.YES);
                            topChatItems.setChatuptime(new Date());
                            operBizData = Json.toJson(topChatItems);
                            break;
                        case Const.WxUserOper.CHAT_CANCEL_TOP:
                            WxChatItems cancelTop = new WxChatItems();
                            cancelTop.setChatlinkid(oldChatLinkid);
                            cancelTop.setTopflag(Const.YesOrNo.NO);
                            cancelTop.setChatuptime(new Date());
                            operBizData = Json.toJson(cancelTop);
                            break;
                    }
                    if (Objects.equals(oper, Const.WxUserOper.HIDE_CHAT)) {
                        WxChatApi.userChatOper(uid, fGroup == null ? RetUtils.getOkTData(ret, "chatlinkid") : -fGroup, Const.WxUserOper.DEL_ITEM_REACT, "",
                                operBizData, null);
                    } else {
                        WxChatApi.userChatOper(uid, fGroup == null ? RetUtils.getOkTData(ret, "chatlinkid") : -fGroup, oper, "", operBizData, null);
                    }
                }
            } catch (Exception e) {
                log.error("", e);
            }
        });
        return Resp.ok(RetUtils.getOkData(ret));
    }

    /**
     * 打开聊天界面
     * 如果是激活操作，发送通知
     * @param request
     * @param touid
     * @return
     * @author xufei
     * 2020年1月20日 下午4:53:34
     */
    @RequestPath(value = "/actChat")
    public Resp actChat(HttpRequest request, Integer touid, Long groupid) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Integer uid = curr.getId();
        Ret ret;
        if (groupid == null) {
            if (touid == null) {
                return RetUtils.getInvalidResp();
            }
            ret = chatService.actFdChatItems(uid, touid);
        } else {
            ret = chatService.actGroupChatItems(groupid, uid);
        }
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        Short act = RetUtils.getOkTData(ret, "actflag");
        Ret sendRet = ret;
        if (act != null && Objects.equals(act, Const.YesOrNo.YES)) {//发送激活通知
            //消息处理
            ThreadUtil.getBsExecutor().execute(() -> {
                try {
                    WxChatItems chatItems = RetUtils.getOkTData(sendRet, "chat");
                    if (WxSynApi.isSynVersion()) {
                        WxSynApi.synChatSession(uid, chatItems, SynRecordVo.SynType.ADD);
                    } else {
                        WxChatApi.userActOper(uid, chatItems);
                    }
                } catch (Exception e) {
                    log.error("", e);
                }
            });
        }
        return Resp.ok(ret);
    }

    /**
     * 	处理申请-已调整
     * @param request
     * @param applyid
     * @param remarkname
     * @return
     * @throws Exception
     * @author xufei
     * 2020年1月15日 下午5:55:41
     */
    @RequestPath(value = "/dealApply")
    public Resp dealApply(HttpRequest request, Integer applyid, String remarkname) throws Exception {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        WxFriendApplyItems items = FriendApplyService.me.getById(applyid);
        if (items == null) {
            return RetUtils.getFailResp(CommonCode.BIZ_NOT_EXIST.value);
        }
        Ret ret = friendService.dealApply(curr, items, remarkname);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        //消息处理
        ThreadUtil.getBsExecutor().execute(() -> {
            try {
                Integer uid = items.getFromuid();
                Integer touid = items.getTouid();
                String send = RetUtils.retKey(ret, "send");
                if (StrUtil.isNotBlank(send)) {
                    String greet = RetUtils.retKey(ret, "greet");
                    //操作者的聊天列表
                    Long tochatlinkid = RetUtils.retLongKey(ret, "tochatlinkid");
                    if (send.equals("double")) {
                        boolean applysigle = RetUtils.getOkTData(ret, "applysigle");
                        //申请者的聊天列表
                        Long chatlinkid = RetUtils.retLongKey(ret, "chatlinkid");
                        WxChatApi.addFriendEachOfPassApply(uid, touid, chatlinkid, tochatlinkid, greet, applysigle);
                    } else {
                        WxChatApi.addFriendSigleOfPassApply(uid, touid, tochatlinkid, greet);
                    }
                }
                WxFriend friend = RetUtils.getOkTData(ret, "friend");
                WxFriend toFriend = RetUtils.getOkTData(ret, "tofriend");
                WxChatApi.friendChangeAddNtf(friend, toFriend);
            } catch (Exception e) {
                log.error("", e);
            }
        });
        return Resp.ok(RetUtils.getOkData(ret));
    }

    /**
     * 消息操作-已调整
     * @param request
     * @param oper
     * @return
     * @author xufei
     * 2020年2月11日 下午12:03:26
     */
    @RequestPath(value = "/msgOper")
    public Resp msgOper(HttpRequest request, Long chatlinkid, String mids, Short oper) {
        if (chatlinkid == null) {
            return Resp.fail("会话id参数为空");
        }
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        short chatmode = Const.ChatMode.P2P;
        if (chatlinkid <= 0) {
            //处理群的chatlinkid
            WxChatGroupItem groupItem = ChatIndexService.chatGroupIndex(curr.getId(), -chatlinkid);
            if (groupItem == null || groupItem.getChatlinkid() == null) {
                return Resp.fail("会话id为空");
            }
            chatlinkid = groupItem.getChatlinkid();
            chatmode = Const.ChatMode.GROUP;
        }
        Ret ret = ChatMsgService.me.msgOper(curr, chatlinkid, oper, mids, chatmode);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        Long sendChatLinkid = chatlinkid;
        Short sendMode = chatmode;
        WxChatItems chatItems = RetUtils.getOkTData(ret, "chatItems");
        //消息处理
        ThreadUtil.getBsExecutor().execute(() -> {
            try {
                Object msg = RetUtils.getOkTData(ret, "msg");
                switch (oper) {
                    case Const.WxMsgOper.DEL:
                        WxChatApi.delMsg(curr, sendChatLinkid, sendMode, mids, chatItems);
                        break;
                    case Const.WxMsgOper.BACK:
                        WxChatApi.backMsg(curr, sendChatLinkid, sendMode, msg);
                        break;
                    default:
                        break;
                }
            } catch (Exception e) {
                log.error("", e);
            }
        });
        return Resp.ok(RetUtils.OPER_RIGHT);
    }

    /**
     * 消息转发-已调整
     * @param request
     * @param mids
     * @param groupids
     * @param uids
     * @return
     * @author xufei
     * 2020年3月12日 下午5:11:26
     */
    @RequestPath(value = "/msgForward")
    public Resp msgForward(HttpRequest request, Long chatlinkid, String mids, String groupids, String uids,Integer type) {
        if (chatlinkid == null) {
            return Resp.fail("会话id参数为空");
        }
        if (type == null) {
            type = 0;
        }
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        short chatmode = Const.ChatMode.P2P;
        if (chatlinkid <= 0) {
            //处理群的chatlinkid
            WxChatGroupItem groupItem = ChatIndexService.chatGroupIndex(curr.getId(), -chatlinkid);
            if (groupItem == null || groupItem.getChatlinkid() == null) {
                return Resp.fail("会话id为空");
            }
            chatmode = Const.ChatMode.GROUP;
        }
        Ret ret = msgForward(request, curr, chatmode, mids, groupids, uids, type);
        if (ret.isFail()) {
            log.error(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.OPER_RIGHT);
    }

    /**
     * 消息转发通知-已调整
     * @param request
     * @param user
     * @param chatmode
     * @param mids
     * @param groupids
     * @param uids
     * @return
     * @author xufei
     * 2020年3月12日 下午5:57:57
     * @param type
     */
    public Ret msgForward(HttpRequest request, User user, Short chatmode, String mids, String groupids, String uids, Integer type) {
        if (StrUtil.isBlank(mids)) {
            return RetUtils.failMsg("转发消息为空");
        }
        if (StrUtil.isBlank(uids) && StrUtil.isBlank(groupids)) {
            return RetUtils.failMsg("转发目标为空");
        }
        Integer uid = user.getId();
        String[] midArr = mids.split(",");
        String sessionid = request.getHttpSession().getId();
        String ip = request.getClientIp();
        if (Objects.equals(chatmode, Const.ChatMode.P2P)) {
            p2pMsgForward(request, sessionid, ip, uid, chatmode, midArr, groupids, uids,type);
        } else {
            groupMsgForward(request, sessionid, ip, uid, chatmode, midArr, groupids, uids,type);
        }
        return RetUtils.okOper();
    }

    /**
     * 私聊消息转发通知-已调整
     * @param request
     * @param sessionid
     * @param ip
     * @param uid
     * @param chatmode
     * @param midArr
     * @param groupids
     * @param uids
     * @throws Exception
     * @author xufei
     * 2020年3月12日 下午7:18:45
     * @param type
     */
    public void p2pMsgForward(HttpRequest request, String sessionid, String ip, Integer uid, Short chatmode,
                              String[] midArr, String groupids, String uids,
                              Integer type) {
        RequestExt ext = (RequestExt) request.getAttribute(RequestKey.REQUEST_EXT);
        String appversion = ext.getAppVersion();
        Short devicetype = ext.getDeviceType();
        //实例化一个set集合
        Set<String> set = new LinkedHashSet<>();
        //遍历数组并存入集合,如果元素已存在则不会重复存入
        for (int i = 0; i < midArr.length; i++) {
            set.add(midArr[i]);
        }
        if(set.size() != midArr.length) {
            System.out.println("当前转发ID 重复"+midArr);
        }
        Object[] array = set.toArray();

        //1 是合并转发   0 逐条转发
        if(array.length > 0 && type == 1) {
            String join = StringUtils.join(array,",");
            List<WxFriendMsg> list = WxFriendMsg.dao.find("select m.*,u.avatar,u.nick from wx_friend_msg m  left join user u on m.uid = u.id where m.id  in ("+join+")");
            //分享到好友
            if (StrUtil.isNotBlank(uids)) {
                String[] uidArr = uids.split(",");
                //实例化一个set集合
                Set<String> uidset = new LinkedHashSet<String>();
                //遍历数组并存入集合,如果元素已存在则不会重复存入
                for (int i = 0; i < uidArr.length; i++) {
                    uidset.add(uidArr[i]);
                }
                Object[] uidObArr = uidset.toArray();
                for (Object uidStr : uidObArr) {
                    Integer otherUid = Integer.parseInt(uidStr.toString());
                    WxChatUserItem userItem = ChatIndexService.fdUserIndex(uid, otherUid);
                    if (!ChatService.existTwoFriend(userItem)) {
                        log.error("分享好友消息异常：好友不存在，uid:{},touid:{}", uid, otherUid);
                        continue;
                    }
                    if (userItem.getChatlinkid() == null) {
                        Ret ret = ChatService.me.actFdChatItems(uid, otherUid);
                        if (ret.isFail()) {
                            log.error("分享好友消息异常：自己的会话激活失败，uid:{},touid:{}", uid, otherUid);
                            continue;
                        }
                        ThreadUtil.getBsExecutor().execute(() -> {
                            if (WxSynApi.isSynVersion()) {
                                WxSynApi.synChatSession(uid, RetUtils.getOkTData(ret, "chat"), SynRecordVo.SynType.ADD);
                            } else {
                                WxChatApi.userActOper(uid, RetUtils.getOkTData(ret, "chat"));
                            }
                        });
                    }
                    ThreadUtil.getBsExecutor().execute(() -> {
                        try {
                            Ret ret = WxChatApi.sendFdMsgEach(JSON.toJSONString(list),(short)66, uid, otherUid, (short)2, "", 0L, chatmode, null);
                            if (ret.isFail()) {
                                log.error(RetUtils.getRetMsg(ret));
                            }
                        } catch (Exception e) {
                            log.error(e.getMessage());
                        }
                    });
                }
            }

            if (StrUtil.isNotBlank(groupids)) {
                String[] groupArr = groupids.split(",");
                for (String groupStr : groupArr) {
                    Long groupid = Long.parseLong(groupStr);
                    Ret check = GroupService.checkGroupMsg(groupid, uid);
                    if (check.isFail()) {
                        log.error("分享到群消息异常：{}，uid:{},groupid:{}",RetUtils.getRetMsg(check), uid, groupid);
                        continue;
                    }
                    ThreadUtil.getBsExecutor().execute(() -> {
                        try {
                            Ret ret = WxChatApi.sendGroupMsgEach(JSON.toJSONString(list), (short)66, uid, groupid, (short)2, null, 0L, chatmode, "");
                            if (ret.isFail()) {
                                log.error(RetUtils.getRetMsg(ret));
                            }
                        } catch (Exception e) {
                            log.error(e.getMessage());
                        }
                    });
                }
            }
        }
        else {
            for (Object midStr : array) {
                Long mid = Long.parseLong(midStr.toString());
                WxFriendMsg msg = WxFriendMsg.dao.findById(mid);
                if (msg == null) {
                    log.error("分享消息异常：消息不存在，uid:{},mid:{},chatmode:{}", uid, mid, chatmode);
                    continue;
                }
                //分享到好友
                if (StrUtil.isNotBlank(uids)) {
                    String[] uidArr = uids.split(",");
                    //实例化一个set集合
                    Set<String> uidset = new HashSet<String>();
                    //遍历数组并存入集合,如果元素已存在则不会重复存入
                    for (int i = 0; i < uidArr.length; i++) {
                        uidset.add(uidArr[i]);
                    }
                    Object[] uidObArr = uidset.toArray();
                    if(uidset.size() != uidArr.length) {
                        System.out.println("当前转发ID 重复"+uidArr);
                    }
                    for (Object uidStr : uidObArr) {
                        Integer otherUid = Integer.parseInt(uidStr.toString());
                        WxChatUserItem userItem = ChatIndexService.fdUserIndex(uid, otherUid);
                        if (!ChatService.existTwoFriend(userItem)) {
                            log.error("分享好友消息异常：好友不存在，uid:{},touid:{}", uid, otherUid);
                            continue;
                        }
                        if (userItem.getChatlinkid() == null) {
                            Ret ret = ChatService.me.actFdChatItems(uid, otherUid);
                            if (ret.isFail()) {
                                log.error("分享好友消息异常：自己的会话激活失败，uid:{},touid:{}", uid, otherUid);
                                continue;
                            }
                            ThreadUtil.getBsExecutor().execute(() -> {
                                if (WxSynApi.isSynVersion()) {
                                    WxSynApi.synChatSession(uid, RetUtils.getOkTData(ret, "chat"), SynRecordVo.SynType.ADD);
                                } else {
                                    WxChatApi.userActOper(uid, RetUtils.getOkTData(ret, "chat"));
                                }
                            });
                        }
                        ThreadUtil.getBsExecutor().execute(() -> {
                            try {
                                Ret ret = WxChatApi.sendFdMsgEach(msg.getText(), msg.getContenttype(), uid, otherUid, msg.getSendbysys(), "", msg.getId(), chatmode, null);
                                if (ret.isFail()) {
                                    log.error(RetUtils.getRetMsg(ret));
                                }
                            } catch (Exception e) {
                                log.error(e.getMessage());
                            }
                        });
                    }
                }
                if (StrUtil.isNotBlank(groupids)) {
                    String[] groupArr = groupids.split(",");
                    for (String groupStr : groupArr) {
                        Long groupid = Long.parseLong(groupStr);
                        Ret check = GroupService.checkGroupMsg(groupid, uid);
                        if (check.isFail()) {
                            log.error("分享到群消息异常：{}，uid:{},groupid:{}",RetUtils.getRetMsg(check), uid, groupid);
                            continue;
                        }
                        ChatService.me.updateReadFlag(groupid, uid);
                        ThreadUtil.getBsExecutor().execute(() -> {
                            try {
                                Ret ret = WxChatApi.sendGroupMsgEach(msg.getText(), msg.getContenttype(), uid, groupid, msg.getSendbysys(), null, msg.getId(), chatmode, "");
                                if (ret.isFail()) {
                                    log.error(RetUtils.getRetMsg(ret));
                                }
                            } catch (Exception e) {
                                log.error(e.getMessage());
                            }
                        });
                    }
                }
            }
        }
    }

    /**
     * 群聊消息转发通知-已调整
     * @param request
     * @param sessionid
     * @param ip
     * @param uid
     * @param chatmode
     * @param midArr
     * @param groupids
     * @param uids
     * @author xufei
     * 2020年3月12日 下午7:18:49
     * @param type
     */
    public void groupMsgForward(HttpRequest request, String sessionid, String ip, Integer uid, Short chatmode, String[] midArr,
                                String groupids, String uids, Integer type) {
        //实例化一个set集合
        Set<String> set = new HashSet<>();
        //遍历数组并存入集合,如果元素已存在则不会重复存入
        for (int i = 0; i < midArr.length; i++) {
            set.add(midArr[i]);
        }
        if(set.size() != midArr.length) {
            System.out.println("当前转发ID 重复"+midArr);
        }
        Object[] array = set.toArray();

        if(set.size() > 1 && type != null && type== 1) {
            String join = StringUtils.join(array,",");
            List<WxGroupMsg> list = WxGroupMsg.dao.find("select m.*,u.avatar,u.nick from wx_group_msg m  left join user u on m.uid = u.id where m.id  in ("+join+")");
            //分享到好友
            if (StrUtil.isNotBlank(uids)) {
                String[] uidArr = uids.split(",");
                //实例化一个set集合
                Set<String> uidset = new HashSet<String>();
                //遍历数组并存入集合,如果元素已存在则不会重复存入
                for (int i = 0; i < uidArr.length; i++) {
                    uidset.add(uidArr[i]);
                }
                Object[] uidObArr = uidset.toArray();
                for (Object uidStr : uidObArr) {
                    Integer otherUid = Integer.parseInt(uidStr.toString());
                    WxChatUserItem userItem = ChatIndexService.fdUserIndex(uid, otherUid);
                    if (!ChatService.existTwoFriend(userItem)) {
                        log.error("合并分享好友消息异常：好友不存在，uid:{},touid:{}", uid, otherUid);
                        continue;
                    }
                    if (userItem.getChatlinkid() == null) {
                        Ret ret = ChatService.me.actFdChatItems(uid, otherUid);
                        if (ret.isFail()) {
                            log.error("合并分享好友消息异常：自己的会话激活失败，uid:{},touid:{}", uid, otherUid);
                            continue;
                        }
                        ThreadUtil.getBsExecutor().execute(() -> {
                            if (WxSynApi.isSynVersion()) {
                                WxSynApi.synChatSession(uid, RetUtils.getOkTData(ret, "chat"), SynRecordVo.SynType.ADD);
                            } else {
                                WxChatApi.userActOper(uid, RetUtils.getOkTData(ret, "chat"));
                            }
                        });
                    }
                    ThreadUtil.getBsExecutor().execute(() -> {
                        try {
                            Ret ret = WxChatApi.sendFdMsgEach(JSON.toJSONString(list), (short)66, uid, otherUid,(short)2, "", 0L, chatmode, null);
                            if (ret.isFail()) {
                                log.error(RetUtils.getRetMsg(ret));
                            }
                        } catch (Exception e) {
                            log.error(e.getMessage());
                        }
                    });
                }
            }
            if (StrUtil.isNotBlank(groupids)) {
                String[] groupArr = groupids.split(",");
                for (String groupStr : groupArr) {
                    Long groupid = Long.parseLong(groupStr);
                    Ret check = GroupService.checkGroupMsg(groupid, uid);
                    if (check.isFail()) {
                        log.error("合并分享到群消息异常：{}，uid:{},groupid:{}",RetUtils.getRetMsg(check), uid, groupid);
                        continue;
                    }
                    ThreadUtil.getBsExecutor().execute(() -> {
                        try {
                            Ret ret = WxChatApi.sendGroupMsgEach(JSON.toJSONString(list), (short)66, uid, groupid, (short)2, null, 0L, chatmode, "");
                            if (ret.isFail()) {
                                log.error(RetUtils.getRetMsg(ret));
                            }
                        } catch (Exception e) {
                            log.error(e.getMessage());
                        }
                    });
                }
            }
        }

        else {
            for (String midStr : midArr) {
                Long mid = Long.parseLong(midStr);
                WxGroupMsg msg = WxGroupMsg.dao.findById(mid);
                if (msg == null) {
                    log.error("分享消息异常：消息不存在，uid:{},mid:{},chatmode:{}", uid, mid, chatmode);
                    continue;
                }
                //分享到好友
                if (StrUtil.isNotBlank(uids)) {
                    String[] uidArr = uids.split(",");
                    //实例化一个set集合
                    Set<String> uidset = new HashSet<>();
                    //遍历数组并存入集合,如果元素已存在则不会重复存入
                    for (int i = 0; i < uidArr.length; i++) {
                        uidset.add(uidArr[i]);
                    }
                    Object[] uidObArr = uidset.toArray();
                    for (Object uidStr : uidObArr) {
                        Integer otherUid = Integer.parseInt(uidStr.toString());
                        WxChatUserItem userItem = ChatIndexService.fdUserIndex(uid, otherUid);
                        if (!ChatService.existTwoFriend(userItem)) {
                            log.error("分享好友消息异常：好友不存在，uid:{},touid:{}", uid, otherUid);
                            continue;
                        }
                        if (userItem.getChatlinkid() == null) {
                            Ret ret = ChatService.me.actFdChatItems(uid, otherUid);
                            if (ret.isFail()) {
                                log.error("分享好友消息异常：自己的会话激活失败，uid:{},touid:{}", uid, otherUid);
                                continue;
                            }
                            ThreadUtil.getBsExecutor().execute(() -> {
                                if (WxSynApi.isSynVersion()) {
                                    WxSynApi.synChatSession(uid, RetUtils.getOkTData(ret, "chat"), SynRecordVo.SynType.ADD);
                                } else {
                                    WxChatApi.userActOper(uid, RetUtils.getOkTData(ret, "chat"));
                                }
                            });
                        }
                        ThreadUtil.getBsExecutor().execute(() -> {
                            try {
                                Ret ret = WxChatApi.sendFdMsgEach(msg.getText(), msg.getContenttype(), uid, otherUid, msg.getSendbysys(), "", msg.getId(), Const.ChatMode.P2P, null);
                                if (ret.isFail()) {
                                    log.error(RetUtils.getRetMsg(ret));
                                }
                            } catch (Exception e) {
                                log.error(e.getMessage());
                            }
                        });
                    }
                }
                if (StrUtil.isNotBlank(groupids)) {
                    String[] groupArr = groupids.split(",");
                    for (String groupStr : groupArr) {
                        Long groupid = Long.parseLong(groupStr);
                        Ret check = GroupService.checkGroupMsg(groupid, uid);
                        if (check.isFail()) {
                            log.error("分享到群消息异常：{}，uid:{},groupid:{}",RetUtils.getRetMsg(check), uid, groupid);
                            continue;
                        }
                        ThreadUtil.getBsExecutor().execute(() -> {
                            try {
                                Ret ret = WxChatApi.sendGroupMsgEach(msg.getText(), msg.getContenttype(), uid, groupid, msg.getSendbysys(), null, msg.getId(), Const.ChatMode.P2P, "");
                                if (ret.isFail()) {
                                    log.error(RetUtils.getRetMsg(ret));
                                }
                            } catch (Exception e) {
                                log.error(e.getMessage());
                            }
                        });
                    }
                }
            }
        }
    }

    /**
     * 收藏转发
     * @param request
     * @return
     * @author xufei
     * 2021年1月26日 下午5:35:19
     */
    @RequestPath(value = "/favoriteForward")
    public Resp favoriteForward(HttpRequest request, String fids, String groupids, String uids) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        String sessionid = request.getHttpSession().getId();
        String ip = request.getClientIp();
        if (StrUtil.isBlank(fids)) {
            return Resp.fail("收藏信息为空");
        }
        if (StrUtil.isBlank(uids) && StrUtil.isBlank(groupids)) {
            return Resp.fail("转发目标为空");
        }
        ThreadUtil.getBsExecutor().execute(()->{
            try {
                String[] fidArr = fids.split(",");
                Ret ret = WxChatApi.favoriteForward(curr.getId(), fidArr, groupids, uids);
                if (ret.isFail()) {
                    log.error(RetUtils.getRetMsg(ret));
                }
            } catch (Exception e){
                log.error(e.getMessage());
            }
        });
        return Resp.ok(RetUtils.OPER_RIGHT);
    }
    /**
     * 分享名片-已调整
     * @param request
     * @param groupids
     * @param uids
     * @return
     * @author xufei
     * 2020年4月1日 下午2:10:33
     */
    @RequestPath(value = "/shareCard")
    public Resp shareCard(HttpRequest request, Long cardid, String groupids, String uids, Short chatmode) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }

        Ret ret = WxChatApi.sharChard(curr, cardid, chatmode, groupids, uids);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.OPER_RIGHT);
    }

    /************************************************end-oper**************************************************/

    /**********************************************begin-check**************************************************/

    /**
     * 	检查是否可以加好友-已调整
     * @param request
     * @param touid
     * @return
     * @author xufei
     * 2020年1月15日 下午5:20:11
     */
    @RequestPath(value = "/checkAddFriend")
    public Resp checkAddFriend(HttpRequest request, Integer touid) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Ret ret = friendService.checkAddFriend(curr, touid);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.getCode(ret));
    }

    /**
     * 检查群名片进去条件-已调整
     * @param request
     * @param groupid
     * @return
     * @author xufei
     * 2020年3月11日 上午10:25:12
     */
    @RequestPath(value = "/checkCardJoinGroup")
    public Resp checkCardJoinGroup(HttpRequest request, Long groupid, Integer applyuid, Date sendtime) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Ret ret = groupService.checkJoinGroup(applyuid, groupid, curr.getId(), sendtime, false, 1);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok(RetUtils.getOkTData(ret, "joinflag"));
    }

    /**
     * check发送名片-已调整
     * @param request
     * @param groupid
     * @return
     * @author xufei
     * 2020年3月11日 上午10:25:11
     */
    @RequestPath(value = "/checkSendCard")
    public Resp checkCard(HttpRequest request, Long groupid) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Ret ret = groupService.checkSendCard(curr.getId(), groupid);
        if (ret.isFail()) {
            return Resp.fail().msg(RetUtils.getRetMsg(ret));
        }
        return Resp.ok();
    }

    /**
     * 检查是否是有效群成员-已调整
     * @param groupid
     * @param uid
     * @return
     * @author xufei
     * 2020年3月31日 下午6:29:22
     */
    @RequestPath(value = "/checkGroupUser")
    public Resp checkGroupUser(Long groupid, Integer uid) {
        WxChatGroupItem groupItem = ChatIndexService.chatGroupIndex(uid, groupid);
        if (!ChatService.groupChatLink(groupItem)) {
            return Resp.ok(Const.YesOrNo.NO).msg("不是群成员");
        }
        return Resp.ok(Const.YesOrNo.YES);
    }

    /**
     * 已读ack
     * @param request
     * @return
     * @author xufei
     * 2021年1月4日 下午12:23:36
     * TODO 需检测
     */
    @RequestPath(value = "/readAck")
    public Resp readAck(HttpRequest request, Long chatlinkid) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        if (chatlinkid == null) {
            log.error("消息已读ack异常:会话为空");
            return Resp.fail("ack参数异常");
        }
        if (chatlinkid <= 0) {
            return Resp.ok(Const.YesOrNo.YES);
        }
        WxChatUserItem userItem = ChatIndexService.chatUserIndex(chatlinkid);
        if (userItem == null) {
            return Resp.fail("ack索引异常");
        }
        return Resp.ok(Const.YesOrNo.YES);
    }

    /************************************************end-check**************************************************/

    /**********************************************begin-upload-msg**************************************************/
    /**
     * 发送文件-已调整
     * @param request
     * @param uploadFile
     * @return
     * @throws Exception
     * @author xufei
     * 2020年2月5日 下午7:01:16
     */
    @RequestPath(value = "/file")
    public Resp file(HttpRequest request, UploadFile uploadFile, Long chatlinkid,String url,String filename,Long size) throws Exception {
        try {
            User curr = WebUtils.currUser(request);
            if (curr == null) {
                return Resp.fail("请先登录");
            }
            if (Objects.equals(curr.getIsBack(), 0)) {
                if (Objects.equals(curr.getRealnameflag(), Const.YesOrNo.NO)) {
                    return Resp.fail("需实名认证");
                }
            }
            if (chatlinkid == null) {
                return Resp.fail("会话id参数为空");
            }
            if (chatlinkid <= 0) {
                Ret check = GroupService.checkGroupMsg(-chatlinkid, curr.getId());
                if (check.isFail()) {
                    return Resp.fail(RetUtils.getRetMsg(check));
                }
                chatlinkid = RetUtils.getOkTData(check);
            }
            WxChatItems chatItems = chatService.getBaseChatItems(chatlinkid);
            if (chatItems == null) {
                return RetUtils.getInvalidResp();
            }
            if (Objects.equals(chatItems.getChatmode(), Const.ChatMode.P2P)) {
                WxChatUserItem touserItem = ChatIndexService.fdUserIndex(chatItems.getBizid().intValue(), chatItems.getUid());
                if (!ChatService.existTwoFriend(touserItem)) {
                    return Resp.fail("对方不是你的好友");
                }
                if (touserItem.getUid().equals(curr.getId())) {
                    return Resp.fail("不能给自己发送消息");
                }
                if (!ChatService.checkBlack(0, touserItem.getUid(), curr.getId())){
                    return Resp.fail("对方已拉黑你");
                }
            }
            String sessionid = request.getHttpSession().getId();
            File dbFile;
            if(url != null) {
                dbFile = innerObsFile(curr, filename, url, sessionid,size);
            }else {
                int isOss = ConfService.getInt(Const.OssConfig.IS_OSS, 1);
                if (isOss == 1) {
                    dbFile = innerUploadFile(curr, uploadFile, sessionid);
                } else {
                    dbFile = innerOssFile(curr, uploadFile, sessionid);
                }
            }

            String text = Json.toJson(dbFile);
            ThreadUtil.getBsExecutor().execute(()->{
                try {
                    Ret ret;
                    if (Objects.equals(chatItems.getChatmode(), Const.ChatMode.P2P)) {
                        ret = WxChatApi.sendFdMsgEach(text, Const.ContentType.FILE, chatItems.getUid(), chatItems.getBizid().intValue(), Const.YesOrNo.NO);
                    } else {
                        ret = WxChatApi.sendGroupMsgEach(text, Const.ContentType.FILE, chatItems.getUid(), chatItems.getBizid(), Const.YesOrNo.NO, null);
                    }
                    if (ret.isFail()) {
                        log.error(RetUtils.getRetMsg(ret));
                    }
                } catch (Exception e){
                    log.error(e.getMessage());
                }
            });
            return Resp.ok(RetUtils.OPER_RIGHT);
        } catch (Exception e) {
            log.error("", e);
            return RetUtils.getSysErrorResp();
        }
    }

    /**
     * 发送视频-已调整
     * @param request
     * @param uploadFile
     * @param chatlinkid
     * @return
     * @author xufei
     * 2020年2月5日 下午7:43:00
     */
    @RequestPath(value = "/video")
    public Resp video(HttpRequest request, UploadFile uploadFile, Long chatlinkid,String url,String filename,Long width,Long height,String coverUrl) {
        try {
            if (chatlinkid == null) {
                return Resp.fail("会话id参数为空");
            }
            User curr = WebUtils.currUser(request);
            if (curr == null) {
                return Resp.fail("请先登录");
            }
            if (Objects.equals(curr.getIsBack(), 0)) {
                if (Objects.equals(curr.getRealnameflag(), Const.YesOrNo.NO)) {
                    return Resp.fail("需实名认证");
                }
            }
            if (chatlinkid <= 0) {
                Ret check = GroupService.checkGroupMsg(-chatlinkid, curr.getId());
                if (check.isFail()) {
                    return Resp.fail(RetUtils.getRetMsg(check));
                }
                chatlinkid = RetUtils.getOkTData(check);
            }
            WxChatItems chatItems = chatService.getBaseChatItems(chatlinkid);
            if (chatItems == null) {
                return RetUtils.getInvalidResp();
            }
            if (Objects.equals(chatItems.getChatmode(), Const.ChatMode.P2P)) {
                WxChatUserItem touserItem = ChatIndexService.fdUserIndex(chatItems.getBizid().intValue(), chatItems.getUid());
                if (!ChatService.existTwoFriend(touserItem)) {
                    return Resp.fail("对方不是你的好友");
                }
                if (touserItem.getUid().equals(curr.getId())) {
                    return Resp.fail("不能给自己发送消息");
                }
                if (!ChatService.checkBlack(0, touserItem.getUid(), curr.getId())){
                    return Resp.fail("对方已拉黑你");
                }
            }
            Resp resp;
            if(url != null) {
                resp = processObsVideo(request, chatItems, url, filename, coverUrl,width,height);
            }else {
                resp = processOssVideo(request, uploadFile, chatItems);
            }
            if (!resp.isOk()) {
                return resp;
            }
            Video video = (Video) resp.getData();
            ThreadUtil.getBsExecutor().execute(()->{
                try {
                    String text = Json.toJson(video);
                    Ret ret;
                    if (Objects.equals(chatItems.getChatmode(), Const.ChatMode.P2P)) {
                        ret = WxChatApi.sendFdMsgEach(text, Const.ContentType.VIDEO, chatItems.getUid(),
                                chatItems.getBizid().intValue(), Const.YesOrNo.NO);
                    } else {
                        ret = WxChatApi.sendGroupMsgEach(text, Const.ContentType.VIDEO, chatItems.getUid(), chatItems.getBizid(), Const.YesOrNo.NO, null);
                    }
                    if (ret.isFail()) {
                        log.error(RetUtils.getRetMsg(ret));
                    }
                } catch (Exception e){
                    log.error("");
                }
            });
            return Resp.ok(RetUtils.OPER_RIGHT);
        } catch (Exception e) {
            log.error("", e);
            return RetUtils.getSysErrorResp();
        }
    }

    /**
     * 发送图片-已调整
     * @param request
     * @param uploadFile
     * @return
     * @author xufei
     * 2020年2月5日 下午7:44:17
     */
    @RequestPath(value = "/img")
    public Resp img(HttpRequest request, UploadFile uploadFile, Long chatlinkid,String url,String filename,Long width,Long height) {
        try {
            if (chatlinkid == null) {
                return Resp.fail("会话id参数为空");
            }
            User curr = WebUtils.currUser(request);
            if (curr == null) {
                return Resp.fail("请先登录");
            }
            if (Objects.equals(curr.getIsBack(), 0)) {
                if (Objects.equals(curr.getRealnameflag(), Const.YesOrNo.NO)) {
                    return Resp.fail("需实名认证");
                }
            }
            if (chatlinkid <= 0) {
                Ret check = GroupService.checkGroupMsg(-chatlinkid, curr.getId());
                if (check.isFail()) {
                    return Resp.fail(RetUtils.getRetMsg(check));
                }
                chatlinkid = RetUtils.getOkTData(check);
            }
            WxChatItems chatItems = chatService.getBaseChatItems(chatlinkid);
            if (chatItems == null) {
                return RetUtils.getInvalidResp();
            }
            if (Objects.equals(chatItems.getChatmode(), Const.ChatMode.P2P)) {
                WxChatUserItem touserItem = ChatIndexService.fdUserIndex(chatItems.getBizid().intValue(), chatItems.getUid());
                if (!ChatService.existTwoFriend(touserItem)) {
                    return Resp.fail("对方不是你的好友");
                }
                if (touserItem.getUid().equals(curr.getId())) {
                    return Resp.fail("不能给自己发送消息");
                }
                if (!ChatService.checkBlack(0, touserItem.getUid(), curr.getId())){
                    return Resp.fail("对方已拉黑你");
                }
            }
            Resp resp;
            if(url != null) {
                resp = chatService.processObsImg(curr.getId(),request.getHttpSession().getId(), url, filename,width,height);
            }else {
                int isOss = ConfService.getInt(Const.OssConfig.IS_OSS, 1);
                if (width == null) {
                    width = 160L;
                }
                if (height == null) {
                    height = 90L;
                }
                if (isOss == 1) {
                    resp = processUploadedImg(request, uploadFile, width.intValue(), height.intValue());
                }  else {
                    resp = processOssImg(request, uploadFile, width.intValue(), height.intValue());
                }
            }
            if (!resp.isOk()) {
                return resp;
            }
            Img img = (Img) resp.getData();
            String text = Json.toJson(img);
            ThreadUtil.getBsExecutor().execute(() -> {
                try {
                    Ret ret;
                    if (Objects.equals(chatItems.getChatmode(), Const.ChatMode.P2P)) {
                        ret = WxChatApi.sendFdMsgEach(text, Const.ContentType.IMG, chatItems.getUid(), chatItems.getBizid().intValue(), Const.YesOrNo.NO);
                    } else {
                        ret = WxChatApi.sendGroupMsgEach(text, Const.ContentType.IMG, chatItems.getUid(), chatItems.getBizid(), Const.YesOrNo.NO, null);
                    }
                    if (ret.isFail()) {
                        log.error(RetUtils.getRetMsg(ret));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            return Resp.ok(RetUtils.OPER_RIGHT);
        } catch (Exception e) {
            log.error("", e);
            return RetUtils.getSysErrorResp();
        }
    }

    /**
     * 音频上传-已调整
     * @param request
     * @param uploadFile
     * @param chatlinkid
     * @return
     * @author xufei
     * 2020年4月13日 下午2:18:45
     */
    @RequestPath(value = "/audio")
    public Resp audio(HttpRequest request, UploadFile uploadFile, Long chatlinkid,String url,String filename,Long second) {
        try {
            if (chatlinkid == null) {
                return Resp.fail("会话id参数为空");
            }
            User curr = WebUtils.currUser(request);
            if (curr == null) {
                return Resp.fail("请先登录");
            }
            if (Objects.equals(curr.getIsBack(), 0)) {
                if (Objects.equals(curr.getRealnameflag(), Const.YesOrNo.NO)) {
                    return Resp.fail("需实名认证");
                }
            }
            if (chatlinkid <= 0) {
                Ret check = GroupService.checkGroupMsg(-chatlinkid, curr.getId());
                if (check.isFail()) {
                    return Resp.fail(RetUtils.getRetMsg(check));
                }
                chatlinkid = RetUtils.getOkTData(check);
            }
            WxChatItems chatItems = chatService.getBaseChatItems(chatlinkid);
            if (chatItems == null) {
                return RetUtils.getInvalidResp();
            }
            if (Objects.equals(chatItems.getChatmode(), Const.ChatMode.P2P)) {
                WxChatUserItem touserItem = ChatIndexService.fdUserIndex(chatItems.getBizid().intValue(), chatItems.getUid());
                if (!ChatService.existTwoFriend(touserItem)) {
                    return Resp.fail("对方不是你的好友");
                }
                if (touserItem.getUid().equals(curr.getId())) {
                    return Resp.fail("不能给自己发送消息");
                }
                if (!ChatService.checkBlack(0, touserItem.getUid(), curr.getId())){
                    return Resp.fail("对方已拉黑你");
                }
            }
            Resp resp;
            if(url != null) {
                resp = processObsAudio(request, url, filename, chatItems,second);
            }else {
                resp = processUploadedAudio(request, uploadFile,chatItems);
            }
            if (!resp.isOk()) {
                return resp;
            }
            Audio audio = (Audio) resp.getData();
            String text = Json.toJson(audio);
            ThreadUtil.getBsExecutor().execute(()->{
                try {
                    Ret ret;
                    if (Objects.equals(chatItems.getChatmode(), Const.ChatMode.P2P)) {
                        ret = WxChatApi.sendFdMsgEach(text, Const.ContentType.AUDIO, chatItems.getUid(), chatItems.getBizid().intValue(), Const.YesOrNo.NO);
                    } else {
                        ret = WxChatApi.sendGroupMsgEach(text, Const.ContentType.AUDIO, chatItems.getUid(), chatItems.getBizid(), Const.YesOrNo.NO, null);
                    }
                    if (ret.isFail()) {
                        log.error(RetUtils.getRetMsg(ret));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            return Resp.ok(RetUtils.OPER_RIGHT);
        } catch (Exception e) {
            log.error("", e);
            return RetUtils.getSysErrorResp();
        }
    }

    /**
     * 上传文件-已调整
     * 可抽象TODO:XUFEI
     * @param curr
     * @param uploadFile
     * @param sessionid
     * @return
     * @author xufei
     * 2020年2月5日 下午7:25:45
     */
    private static File innerUploadFile(User curr, UploadFile uploadFile, String sessionid) {
        byte[] bs = uploadFile.getData();
        String filename = uploadFile.getName();
        String ext = FileUtil.extName(filename);
        String urlWithoutExt = UploadUtils.newFile(Const.UPLOAD_DIR.WX_FILE, curr.getId(), uploadFile.getName()); //不带后缀的文件路径
        String url = urlWithoutExt + "." + ext;

        java.io.File file = new java.io.File(Const.RES_ROOT, url);
        FileUtil.writeBytes(bs, file);
        File dbFile = new File();
        dbFile.setExt(ext);
        dbFile.setFilename(uploadFile.getName());
        dbFile.setSession(sessionid);
        dbFile.setSize((long) bs.length);
        dbFile.setUid(curr.getId());
        dbFile.setUrl(url);
        dbFile.save();
        return dbFile;
    }

    /**
     * 上传文件-已调整
     * 可抽象TODO:XUFEI
     * @param curr
     * @param uploadFile
     * @param sessionid
     * @return
     * @author xufei
     * 2020年2月5日 下午7:25:45
     */
    private static File innerOssFile(User curr, UploadFile uploadFile, String sessionid) {
        byte[] bs = uploadFile.getData();
        String filename = uploadFile.getName();
        String ext = FileUtil.extName(filename);

        OssConfig ossConfig = ConfService.getOssConfig();
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bs);
        String uploadPath = OssUtils.upload(ossConfig, filename, byteArrayInputStream, "file/", "");

        File dbFile = new File();
        dbFile.setExt(ext);
        dbFile.setFilename(uploadFile.getName());
        dbFile.setSession(sessionid);
        dbFile.setSize((long) bs.length);
        dbFile.setUid(curr.getId());
        dbFile.setUrl(uploadPath);
        dbFile.save();
        return dbFile;
    }
    /**
     * 上传文件-已调整
     * 可抽象TODO:XUFEI
     * @param curr
     * @param sessionid
     * @return
     * @author xufei
     * 2020年2月5日 下午7:25:45
     * @param size
     */
    private static File innerObsFile(User curr,String filename,String url,String sessionid, Long size) {
        String ext = FileUtil.extName(filename);
        File dbFile = new File();
        dbFile.setExt(ext);
        dbFile.setFilename(filename);
        dbFile.setSession(sessionid);
        dbFile.setSize(size);
        dbFile.setUid(curr.getId());
        dbFile.setUrl(url);
        dbFile.save();
        return dbFile;
    }

    /**
     * 用户上传的视频- oss
     * @param request
     * @param uploadFile
     * @param chatItems
     * @return
     */
    private Resp processOssVideo(HttpRequest request, UploadFile uploadFile, WxChatItems chatItems) {
        User curr = WebUtils.currUser(request);
        byte[] bs = uploadFile.getData();
        String filename = uploadFile.getName();
        Resp ret = null;
        if (curr == null) {
            ret = Resp.fail("您尚未登录或登录超时").code(AppCode.ForbidOper.NOTLOGIN);
        }
        Integer uid;
        if (curr != null) {
            uid = curr.getId();
        } else {
            uid = 1;
        }
        OssConfig ossConfig = ConfService.getOssConfig();
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bs);
        String uploadPath = OssUtils.upload(ossConfig, filename, byteArrayInputStream, "ltimg/", "");

        Video video = new Video();
        video.setUrl(uploadPath);
        video.setUid(uid);
        video.setComefrom(Video.ComeFrom.IM_UPLOAD);
        video.setCoverurl(uploadPath);
        video.setStatus((short) 1);
        if(chatItems != null) {
            video.setTitle("聊天者uid:" + chatItems.getUid() + "-模式：" + chatItems.getChatmode() + "-业务id：" + chatItems.getBizid());
        } else {
            video.setTitle(filename);
        }
        video.setFilename(filename);
        video.setSession(request.getHttpSession().getId());
        boolean f = video.save();
        //不管成功与否都保存视频
        if (ret != null) {
            return ret;
        }
        if (f) {
            return Resp.ok(video);
        } else {
            return Resp.fail().data(video);
        }
    }

    /**
     * 用户上传的视频-已调整
     * 可抽象TODO:XUFEI
     * @param request
     * @return
     * @author tanyaowu
     * @param height
     * @param width
     */
    private Resp processObsVideo(HttpRequest request,WxChatItems chatItems,String url,String filename,String coverUrl, Long width, Long height) {
        User curr = WebUtils.currUser(request);
        Resp ret = null;
        if (curr == null) {
            ret = Resp.fail("您尚未登录或登录超时").code(AppCode.ForbidOper.NOTLOGIN);
        }
        Integer uid;
        if (curr != null) {
            uid = curr.getId();
        } else {
            uid = 1;
        }
        Video video = new Video();
        video.setUrl(url);
        video.setUid(uid);
        video.setFormatedseconds("200");
        if (height == null) {
            height = 120L;
        }
        video.setCoverheight(height.intValue());
        if (width == null) {
            width = 90L;
        }
        video.setCoverwidth(width.intValue());
        video.setCoversize(1024);
        video.setHeight(height.intValue());
        video.setSize(1024L);
        video.setWidth(width.intValue());
        video.setComefrom(Video.ComeFrom.IM_UPLOAD);
        video.setCoverurl(coverUrl);
        video.setStatus((short) 1);
        if(chatItems != null) {
            video.setTitle("聊天者uid:" + chatItems.getUid() + "-模式：" + chatItems.getChatmode() + "-业务id：" + chatItems.getBizid());
        } else {
            video.setTitle(filename);
        }
        video.setFilename(filename);
        video.setSession(request.getHttpSession().getId());
        boolean f = video.save();
        //不管成功与否都保存视频
        if (ret != null) {
            return ret;
        }
        if (f) {
            return Resp.ok(video);
        } else {
            return Resp.fail().data(video);
        }
    }
    /**
     * 用户上传的音频-已调整
     * 可抽象TODO:XUFEI
     * @param request
     * @param uploadFile
     * @return
     * @author xufei
     * 2020年4月13日 下午2:20:45
     */
    @SuppressWarnings("resource")
    private Resp processUploadedAudio(HttpRequest request, UploadFile uploadFile, WxChatItems chatItems) {
        User curr = WebUtils.currUser(request);
        byte[] bs = uploadFile.getData();
        String filename = uploadFile.getName();
        String extName = FileUtil.extName(filename);
        Resp ret = null;

        if (curr == null) {
            ret = Resp.fail("您尚未登录或登录超时").code(AppCode.ForbidOper.NOTLOGIN);
        }
        int uid = (curr != null) ? curr.getId() : 1;

        String urlWithoutExt = UploadUtils.newFile(Const.UPLOAD_DIR.WX_AUDIO, uid, filename);
        String url = urlWithoutExt + "." + extName;
        String filePath = Const.RES_ROOT + url;
        FileUtil.writeBytes(bs, filePath);

        // 确保文件已经成功写入
        java.io.File m4aFile = new java.io.File(filePath);
        if (!m4aFile.exists() || !m4aFile.isFile()) {
            return Resp.fail("音频文件不存在或无法读取");
        }

        // 使用 mp4parser 处理 M4A 文件
        int seconds = 0;
        try {
            IsoFile isoFile = new IsoFile(filePath);
            MovieHeaderBox movieHeaderBox = isoFile.getMovieBox().getMovieHeaderBox();
            long duration = movieHeaderBox.getDuration();
            seconds = (int) (duration / movieHeaderBox.getTimescale());
        } catch (IOException e) {
            e.printStackTrace();
            ret = Resp.fail("处理音频文件时发生错误");
        }

        if (ret != null) {
            return ret;
        }

        Audio audio = new Audio();
        audio.setSeconds(seconds);
        audio.setUid(uid);
        audio.setUrl(url);
        audio.setFilename(filename);
        audio.save();

        return Resp.ok(audio);
    }
    /**
     * 用户上传的音频-已调整
     * 可抽象TODO:XUFEI
     * @param request
     * @return
     * @author xufei
     * 2020年4月13日 下午2:20:45
     */
    @SuppressWarnings("resource")
    private Resp processObsAudio(HttpRequest request,String url,String filename,WxChatItems chatItems,Long second) {
        User curr = WebUtils.currUser(request);
        String extName = FileUtil.extName(filename);
        Resp ret = null;
        if (curr == null) {
            ret = Resp.fail("您尚未登录或登录超时").code(AppCode.ForbidOper.NOTLOGIN);
        }
        Integer uid;
        if (curr != null) {
            uid = curr.getId();
        } else {
            uid = 1;
        }
        Audio audio = new Audio();
        audio.setSeconds(second.intValue());
        audio.setUid(uid);
        audio.setUrl(url);
        audio.setFilename(filename);
        audio.save();
        if (ret != null) {
            return ret;
        }
        return Resp.ok(audio);
    }
    /**
     * 用户上传的图片-已调整
     * 可抽象TODO:XUFEI
     * @param request
     * @param uploadFile
     * @return
     * @throws Exception
     * @author xufei
     * 2020年2月5日 下午7:44:47
     */
    private Resp processUploadedImg(HttpRequest request, UploadFile uploadFile, Integer width, Integer height) throws Exception {
        User curr = WebUtils.currUser(request);
        byte[] imageBytes = uploadFile.getData();
        Resp ret = null;
        if (curr == null) {
            ret = Resp.fail("您尚未登录或登录超时").code(AppCode.ForbidOper.NOTLOGIN);
        }
        Integer uid;
        if (curr != null) {
            uid = curr.getId();
        } else {
            uid = 1;
        }
        BufferedImage bi = ImgUtil.toImage(imageBytes);
        float scale = ImgUtils.calcScaleWithWidth(400, bi);
        Img img = ImgUtils.processImg(Const.UPLOAD_DIR.WX_IMG, uid, uploadFile, scale);
        img.setUrl(img.getUrl());
        img.setComefrom(Img.ComeFrom.WX_UPLOAD);
        img.setStatus((short) 1);
        img.setSession(request.getHttpSession().getId());
        img.setWidth(width);
        img.setHeight(height);
        img.setCoverwidth(width);
        img.setCoverheight(height);
        boolean f = img.save();
        if (ret != null) {
            return ret;
        }
        if (f) {
            return Resp.ok(img);
        } else {
            return Resp.fail("服务器异常");
        }
    }

    /**
     * 用户上传的图片-已调整
     * 可抽象TODO:XUFEI
     * @param request
     * @param uploadFile
     * @return
     * @throws Exception
     * @author xufei
     * 2020年2月5日 下午7:44:47
     */
    private Resp processOssImg(HttpRequest request, UploadFile uploadFile, Integer width, Integer height) {
        User curr = WebUtils.currUser(request);
        byte[] imageBytes = uploadFile.getData();
        String filename = uploadFile.getName();
        Resp ret = null;
        if (curr == null) {
            ret = Resp.fail("您尚未登录或登录超时").code(AppCode.ForbidOper.NOTLOGIN);
        }
        Integer uid;
        if (curr != null) {
            uid = curr.getId();
        } else {
            uid = 1;
        }

        OssConfig ossConfig = ConfService.getOssConfig();
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(imageBytes);
        String uploadPath = OssUtils.upload(ossConfig, filename, byteArrayInputStream, "ltimg/", "");

        Img img = new Img();
        img.setUid(uid);
        img.setFilename(filename);
        img.setTitle(filename);
        img.setCoverurl(uploadPath);
        img.setWidth(width);
        img.setHeight(height);
        img.setCoverwidth(width);
        img.setCoverheight(height);
        img.setCoversize(uploadFile.getSize());
        img.setSize((long) uploadFile.getSize());
        img.setUrl(uploadPath);
        img.setComefrom(Img.ComeFrom.WX_UPLOAD);
        img.setStatus((short) 1);
        img.setSession(request.getHttpSession().getId());
        boolean f = img.save();
        if (ret != null) {
            return ret;
        }
        if (f) {
            return Resp.ok(img);
        } else {
            return Resp.fail("服务器异常");
        }
    }

    /************************************************end-upload-msg**************************************************/

    /**
     * 获取群用户的的头像-已调整
     * @param uidArr
     * @param curr
     * @return
     * @throws Exception
     * @author xufei
     * 2020年2月13日 下午4:06:50
     */
    private static Ret getGroupImg(String[] uidArr, User curr, String name) {
        boolean createName = StrUtil.isBlank(name);
        if (createName) {
            name = curr.getNick();
        }
        String nicks = "";
        int c = 0;
        for (String uidStr : uidArr) {
            if (StrUtil.isNotBlank(uidStr)) {
                int uid = Integer.parseInt(uidStr);
                if (Objects.equals(uid, curr.getId())) { //前面已经把当前用户加到了该群，所以此处略过
                    continue;
                }
                User user = UserService.ME.getById(uid);
                if (user != null) {
                    if (createName) {
                        String newName = name + "、" + user.getNick();
                        if (newName.length() < 30) {
                            name = newName;
                        }
                    }
                    nicks += "、" + user.getNick();
                    if (c >= 8) {
                        continue;
                    }
                    c++;
                }
            }
        }
        if (createName) {
            return Ret.ok().set("nicks", nicks.substring(1)).set("name", name);
        } else {
            return Ret.ok().set("nicks", nicks.substring(1));
        }
    }

    @RequestPath(value = "/endCall")
    public Resp endCall(HttpRequest request,Long chatlinkid,Short calltype,Short hanguptype,Integer handupuid,Short  devicetype,Long duration) {
        WxChatItems chatItems = chatService.getBaseChatItems(chatlinkid);
        WxCallItemVo wxCallItemVo = new WxCallItemVo();
        wxCallItemVo.setCalltype(calltype);
        wxCallItemVo.setHanguptype(hanguptype);
        wxCallItemVo.setHangupuid(handupuid);
        wxCallItemVo.setDevicetype(devicetype);
        wxCallItemVo.setDuration(duration);
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        Integer uid = curr.getId();
        Long  touid;
        if(handupuid != null) {
            touid = handupuid.longValue();
        }else if(chatItems != null) {
            touid = chatItems.getBizid();
            log.error("结束消息 "+touid+"  "+chatItems.getLinkid());
        }else {
            return Resp.fail();
        }
        WxChatApi.sendFdMsgEach(JSON.toJSONString(wxCallItemVo), calltype, uid, touid.intValue(), Const.YesOrNo.NO,"");
        return Resp.ok();
    }

    /**
     * 获取呼叫状态
     * @param uidList 用户序号
     * @return
     */
    @RequestPath(value = "/callStatus")
    public Resp callStatus(String uidList) {
        if (Validator.isEmpty(uidList)) {
            return Resp.ok();
        }
        ICache cache = Caches.getCache(CacheConfig.WX_CALL_STATUS);
        List<UserStatus> status = new ArrayList<>();
        String[] uid = uidList.split(",");
        for (String id : uid) {
            String userStatus = cache.get(id, String.class);
            UserStatus bean = new UserStatus();
            bean.setUserID(id);
            if (Validator.isEmpty(userStatus)) {
                bean.setCustomStatus(UserStatus.NONE);
            } else {
                bean.setCustomStatus(userStatus);
            }
            bean.setStatusType(1);
            status.add(bean);
        }
        return Resp.ok(status);
    }

    /**
     * 更新自己的状态
     * @param uid 用户序号
     * @param status 状态
     * @return
     */
    @RequestPath(value = "/callSelfStatus")
    public Resp callSelfStatus(String uid, String status) {
        ICache cache = Caches.getCache(CacheConfig.WX_CALL_STATUS);
        cache.put(uid, status);
        return Resp.ok();
    }

    /**
     * 更新邀请通知
     * @param data 数据
     * @return
     */
    @RequestPath(value = "/callInvite")
    public Resp callInvite(HttpRequest request, String data) {
        //发送申请
        ThreadUtil.getBsExecutor().execute(() -> {
            try {
                User curr = WebUtils.currUser(request);
                if (curr != null) {
                    WxChatApi.useCallNewInviteNtf(curr.getId(), data);
                }
            } catch (Exception e) {
                log.error("", e);
            }
        });
        return Resp.ok();
    }

    /**
     * 更新同意
     * @param callId 呼叫序号
     * @return
     */
    @RequestPath(value = "/callAccept")
    public Resp callAccept(HttpRequest request, String callId) {
        ThreadUtil.getBsExecutor().execute(() -> {
            User curr = WebUtils.currUser(request);
            if (curr != null) {
                WxChatApi.useCallAcceptNtf(String.valueOf(curr.getId()), callId);
            }
        });
        return Resp.ok();
    }

    @RequestPath(value = "/callLogin")
    public Resp callLogin(String userId, String roomId) {
        CallLogin login = new CallLogin();
        try {
            String appId = ConfService.getString("rtcAppId", "c2f8f6292d5e47089a61793bacaac225");
            String secret = ConfService.getString("rtcSecret", "f21922f4fd184d2fb496b53b542fea03");
            RtcTokenBuilder2 token = new RtcTokenBuilder2();
            String result;
            try {
                result = token.buildTokenWithUid(appId, secret, roomId,
                        Integer.parseInt(userId), 3600, 3600, 3600,
                        3600, 3600);
            } catch (Exception e) {
                result = token.buildTokenWithUid(appId, secret, roomId,
                        Integer.parseInt(userId), 3600, 3600, 3600,
                        3600, 3600);
            }
            login.setAppId(appId);
            login.setToken(result);
        } catch (Exception ignored) {
        }
        return Resp.ok(login);
    }

    /**
     * 更新拒绝
     * @param callId 呼叫序号
     * @return
     */
    @RequestPath(value = "/callRejected")
    public Resp callRejected(HttpRequest request, String callId) {
        ThreadUtil.getBsExecutor().execute(() -> {
            User curr = WebUtils.currUser(request);
            if (curr != null) {
                WxChatApi.useCallRejectedNtf(String.valueOf(curr.getId()), callId);
            }
        });
        return Resp.ok();
    }

    /**
     * 更新取消
     * @param callId 呼叫序号
     * @return
     */
    @RequestPath(value = "/callCancel")
    public Resp callCancel(HttpRequest request, String callId) {
        ThreadUtil.getBsExecutor().execute(() -> {
            User curr = WebUtils.currUser(request);
            if (curr != null) {
                WxChatApi.useCallCancelNtf(callId);
            }
        });
        return Resp.ok();
    }

    @RequestPath(value = "/isOnline")
    public Resp isOnline(Integer uid,Long chatlinkid) {
        if(chatlinkid != null) {
            WxChatItems chatItems = chatService.getBaseChatItems(chatlinkid);
            uid = chatItems.getBizid().intValue();
        }
        boolean online = WxChatApi.isOnline(uid);
        return Resp.ok(online);
    }

    @RequestPath(value = "/isOnlineV2")
    public Resp isOnlineV2(Integer uid,Long chatlinkid, Integer type) {
        if(chatlinkid != null) {
            WxChatItems chatItems = chatService.getBaseChatItems(chatlinkid);
            uid = chatItems.getBizid().intValue();
        }
        Date time = LoginLogService.me.selectLastTimeByUid(uid);
        OnlineBean login = new OnlineBean();
        if (time != null) {
            if (type != null && type == Const.YesOrNo.NO) {
                login.setOnlineTime(DateUtil.formatDateTime(time));
            } else {
                login.setOnlineTime(DateUtil.formatDate(time));
            }
        }
        return Resp.ok(login);
    }

    @RequestPath(value = "/gameOnline")
    public Resp gameOnline(Integer uid,Long chatlinkid) {
        Set<String>  result = new HashSet<>();
        MapWithLock<String,SetWithLock<ChannelContext>> map = TioSiteImServerStarter.tioServerConfigApp.users.getMap();
        Map<String, SetWithLock<ChannelContext>> obj = map.getObj();
        Set<Entry<String,SetWithLock<ChannelContext>>> entrySet = obj.entrySet();
        for (Entry<String, SetWithLock<ChannelContext>> entry : entrySet) {
            String key = entry.getKey();
            SetWithLock<ChannelContext> contextSet = entry.getValue();
            if (contextSet != null) {
                ReadLock readLock = contextSet.readLock();
                readLock.lock();
                try {
                    Set<ChannelContext> online = contextSet.getObj();
                    if (online != null) {
                        for (ChannelContext channelContext : online) {
                            Devicetype devicetype = ImUtils.getDevicetype(channelContext);
                            if (devicetype == null) {
                                continue;
                            }
                            if (Objects.equals(Devicetype.IOS.getValue(), devicetype.getValue()) || Objects.equals(Devicetype.ANDROID.getValue(), devicetype.getValue())) {
                                result.add(key);
                            }
                        }
                    }
                } catch (Throwable e) {
                    log.error(e.getMessage(), e);
                } finally {
                    readLock.unlock();
                }
            }
        }
        return Resp.ok(result);
    }

    /**
     * 删除本群所有信息
     */
    @RequestPath(value = "/deleteAllMsg")
    public  Resp  deleteAllMsg(HttpRequest request,Long  groupId) {
        User curr = WebUtils.currUser(request);
        if (curr == null) {
            return Resp.fail("请先登录");
        }
        WxGroupUser groupUser = groupService.getGroupUser(curr.getId(), groupId);
        if(groupUser.getGrouprole().equals(GroupRole.MEMBER)) {
            return Resp.fail();
        }
        WxGroupOperNtf groupOper = new WxGroupOperNtf();
        groupOper.setOper(Const.WxGroupOper.DEL_ALL_MSG);
        groupOper.setChatlinkid(groupId);
        groupOper.setG(groupId);
        groupOper.setBizdata(groupId + "");
        //发送操作消息
        ImPacket operPacket = new ImPacket(Command.WxGroupOperNtf, Json.toJson(groupOper));
        Ims.sendToGroup(groupId, operPacket);
        WxGroupMsg.dao.deleteByGroupId(groupId,null);
        ICache cache = Caches.getCache(CacheConfig.WX_GROUP_CHAT_6);
        cache.remove(groupId+"");
        return Resp.ok();

    }
}
