package com.cyksj.service.corp.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.cyksj.common.exception.BusinessRuntimeException;
import com.cyksj.common.util.IoKit;
import com.cyksj.common.util.J11HttpC;
import com.cyksj.common.util.Jsons;
import com.cyksj.common.util.StringUtil;
import com.cyksj.config.corp.WeChatCorpFactory;
import com.cyksj.config.corp.YHLXWxCorpConfig;
import com.cyksj.dto.CorpOauth2UserInfo;
import com.cyksj.dto.RedisKey;
import com.cyksj.model.entity.WxCorpApp;
import com.cyksj.model.kf.*;
import com.cyksj.model.request.corp.CorpFollowContact;
import com.cyksj.model.request.corp.CorpGroupConfig;
import com.cyksj.model.request.corp.CorpMsgTemplate;
import com.cyksj.model.request.corp.CorpXmlMessage;
import com.cyksj.model.request.corp.link.WxCpLinkAdd;
import com.cyksj.model.request.corp.link.WxCpLinkAddResp;
import com.cyksj.model.request.corp.link.WxCpLinkUpd;
import com.cyksj.model.request.corp.message.WxCorpAgentMessage;
import com.cyksj.model.response.OffiaccountJsTicket;
import com.cyksj.model.response.UserExternalContactList;
import com.cyksj.model.response.UserWithExternalPermission;
import com.cyksj.model.response.WechatAccessToken;
import com.cyksj.model.response.corp.*;
import com.cyksj.redis.RedisService;
import com.cyksj.service.corp.WxCorpOps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.imageio.stream.FileImageOutputStream;
import java.io.File;
import java.net.URLEncoder;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;

/*
 *项目名: netflix
 *文件名: WxCorpOpsImpl
 *创建者: JavaZou
 *创建时间:2022/10/13 16:46
 */
@Service
@Slf4j
public class WxCorpOpsImpl implements WxCorpOps {
	@Autowired
	private YHLXWxCorpConfig yhlXWxCorpConfig;

	@Autowired
	private RedisService redisService;

	//微信客服满意度
	private static final String SATISFACTION = "{\"touser\":\"EXTERNAL_USERID\",\"open_kfid\":\"OPEN_KFID\",\"msgid\":\"MSGID\",\"msgtype\":\"msgmenu\",\"msgmenu\":{\"head_content\":\"本次服务已结束，如有其他需要可重新发起对话。\\n您对本次服务是否满意呢? \",\"list\":[{\"type\":\"click\",\"click\":{\"id\":\"101-%s\",\"content\":\"满意\"}},{\"type\":\"click\",\"click\":{\"id\":\"102-%s\",\"content\":\"一般\"}},{\"type\":\"click\",\"click\":{\"id\":\"103-%s\",\"content\":\"不满意\"}}]}}";

	@Override
	public CorpExternalContactInfo getExternalContact(String corpId, String userId) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get?access_token=" + getAccessToken(corpId) + "&external_userid=" + userId;
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofGet()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("企业微信获取客户详情错误: " + res.body());
		}
		log.info("企业微信客户详情:{}", res.body());
		return Jsons.parseObject(res.body(), CorpExternalContactInfo.class);
	}

	@Override
	public String getAccessToken(String corpId) throws Exception {
		corpId = Optional.ofNullable(corpId).orElse(yhlXWxCorpConfig.getCorpId());
		String accessToken = redisService.getStr(RedisKey.CORP_ACCESS_TOKEN_KEY + corpId);
		if (StringUtils.isNotBlank(accessToken)) {
			return accessToken;
		}
		return access_token(corpId);
	}
	@Override
	public String getMsgAccessToken(String corpId) throws Exception {
		corpId = Optional.ofNullable(corpId).orElse(yhlXWxCorpConfig.getCorpId());
		String accessToken = redisService.getStr(RedisKey.CORP_MSG_ACCESS_TOKEN_KEY + corpId);
		if (StringUtils.isNotBlank(accessToken)) {
			return accessToken;
		}
		return getKfAccessToken(corpId);
	}

	@Override
	public CorpUserExternalTagGroupList getCorpTagList(String corpId, List<String> tagIds, List<String> groupIds) throws Exception {
		JSONObject params = new JSONObject();
		if (!tagIds.isEmpty()) {
			params.putOpt("tag_id", tagIds);
		}
		if (!groupIds.isEmpty()) {
			params.putOpt("group_id", groupIds);
		}

		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get_corp_tag_list?access_token=" + getAccessToken(corpId);
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(params.toString(), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("企业微信获取企业标签库错误: " + res.body());
		}

		return Jsons.parseObject(res.body(), CorpUserExternalTagGroupList.class);
	}

	@Override
	public List<String> getFollowUserList(String corpId) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get_follow_user_list?access_token=" + getAccessToken(corpId);
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofGet()
						.url(url)
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("企业微信获取客户联系功能的成员列表错误:" + res.body());
		}

		return Jsons.parseObject(res.body(), UserWithExternalPermission.class).getFollowers();
	}

	@Override
	public CorpUserInfo getFollowUser(String corpId, String userId) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=" + getAccessToken(corpId) + "&userid=" + userId;
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofGet()
						.url(url)
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("企业微信获取客户联系功能的成员列表错误:" + res.body());
		}

		return Jsons.parseObject(res.body(), CorpUserInfo.class);
	}

	@Override
	public List<String> listExternalContacts(String corpId, String userId) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/list?access_token=" + getAccessToken(corpId) + "&userid=" + userId;
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofGet()
						.url(url)
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("企业微信获取客户列表错误:" + res.body());
		}

		return Jsons.parseObject(res.body(), UserExternalContactList.class).getExternalUserId();
	}

	@Override
	public String createUrl(String corpId, String state) {
		WxCorpApp wxCorpApp = WeChatCorpFactory.getCorpConfigStrategy(corpId);
		return "https://open.weixin.qq.com/connect/oauth2/authorize" +
				"?appid=" + wxCorpApp.getCorpId() +
				"&redirect_uri=" + URLEncoder.encode(wxCorpApp.getDomain() + "/manage/corp/redirect", IoKit.Charsets.UTF_8.getCharset()) +
				"&response_type=code" +
				"&scope=snsapi_base" +
				"&state=" + state +
				"#wechat_redirect";
	}

	@Override
	public CorpOauth2UserInfo getUserInfo(String corpId, String code) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo" +
				"?access_token=" + getAccessToken(corpId) +
				"&code=" + code;

		HttpResponse<String> res =
				J11HttpC.custom()
						.ofGet()
						.url(url)
						.cacheURI()
						.send(HttpResponse.BodyHandlers.ofString());

		log.info("企业微信根据code获取成员信息 res:{}", res.body());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("企业微信根据code获取成员信息失败: " + res.body());
		}

		return Jsons.parseObject(res.body(), CorpOauth2UserInfo.class);
	}

	@Override
	public String getJsTicket(String corpId) throws Exception {
		String jsTicket = redisService.getStr(RedisKey.CORP_JS_TICKET_KEY + corpId);
		if (StrUtil.isBlank(jsTicket)) {
			return js_ticket(corpId);
		}
		return jsTicket;
	}

	@Override
	public String getAgentJsTicket(String corpId, String agentId) throws Exception {
		String ticket = redisService.getStr(RedisKey.CORP_AGENT_JS_TICKET_KEY + corpId);
		if (StrUtil.isBlank(ticket)) {
			return agentJsTicket(corpId);
		}
		return ticket;
	}

	@Override
	public String getCorpChatGroups(String corpId, String configId) throws Exception {
		corpId = Optional.ofNullable(corpId).orElse(yhlXWxCorpConfig.getCorpId());
		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/groupchat/get_join_way?access_token=" + getAccessToken(corpId);
		JSONObject jsonObject = new JSONObject();
		jsonObject.putOpt("config_id", configId);
		HttpResponse<byte[]> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.cacheURI()
						.body(HttpRequest.BodyPublishers.ofString(jsonObject.toString(), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofByteArray());
		String body = IoKit.toString(res.body());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("获取access_token失败: " + body);
		}
		JSONObject re = Jsons.parseObject(body, JSONObject.class);
		Integer errcode = re.getInt("errcode");
		if (null != errcode && 0 != errcode) {
			throw BusinessRuntimeException.getInstance("获取企业微信access_token失败: " + re.getStr("errmsg") + "[" + errcode + "]");
		}
		JSONObject codeRe = Jsons.parseObject(re.getStr("join_way"), JSONObject.class);
		String qr_code = codeRe.getStr("qr_code");
		return qr_code;
	}

	@Override
	public String setGroupsConfig(List<String> chatIds) throws Exception {
		String corpId = yhlXWxCorpConfig.getCorpId();
		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/groupchat/add_join_way?access_token=" + getAccessToken(corpId);
		JSONObject jsonObject = new JSONObject();
		//scene	是	场景。
		//1 - 群的小程序插件
		//2 - 群的二维码插件
		jsonObject.putOpt("scene", 2);
		jsonObject.putOpt("chat_id_list", chatIds);
		jsonObject.putOpt("room_base_name", "Ai银河课堂 MJ SD教程分享");
		//auto_create_room当群满了后，是否自动新建群。0-否；1-是。 默认为1
		HttpResponse<byte[]> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.cacheURI()
						.body(HttpRequest.BodyPublishers.ofString(jsonObject.toString(), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofByteArray());
		String body = IoKit.toString(res.body());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("获取access_token失败: " + body);
		}
		log.info("配置企业微信群返回信息body:{}", body);
		return body;
	}

	@Override
	public String getGroupsDetail(String chatId) throws Exception {
		String corpId = yhlXWxCorpConfig.getCorpId();
		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/groupchat/get?access_token=" + getAccessToken(corpId);
		JSONObject jsonObject = new JSONObject();
		jsonObject.putOpt("chat_id", chatId);
		HttpResponse<byte[]> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.cacheURI()
						.body(HttpRequest.BodyPublishers.ofString(jsonObject.toString(), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofByteArray());
		String body = IoKit.toString(res.body());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("获取access_token失败: " + body);
		}
		return body;
	}

	@Override
	public String corpFollowContactConfig(CorpFollowContact corpFollowContact) throws Exception {
		//配置
		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/add_contact_way?access_token=" + getAccessToken(null);
		HttpResponse<byte[]> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.cacheURI()
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(corpFollowContact), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofByteArray());
		String body = IoKit.toString(res.body());
		JSONObject jsonObject = Jsons.parseObject(body, JSONObject.class);
		if (jsonObject.getInt("errcode") != 0) {
			throw BusinessRuntimeException.getInstance("配置客服活码失败");
		}
		return body;
	}

	@Override
	public void updateCorpFollowContactConfig(CorpFollowContact corpFollowContact) throws Exception {
		//配置
		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/update_contact_way?access_token=" + getAccessToken(null);
		HttpResponse<byte[]> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.cacheURI()
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(corpFollowContact), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofByteArray());
		String body = IoKit.toString(res.body());
		JSONObject jsonObject = Jsons.parseObject(body, JSONObject.class);
		if (jsonObject.getInt("errcode") != 0) {
			throw BusinessRuntimeException.getInstance("更新客服活码失败");
		}
	}

	@Override
	public void sendAgentMessage(String corpId, WxCorpAgentMessage agentMessage) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + getAccessToken(corpId);

		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(agentMessage), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("发送应用消息: " + res.body());
		}

		JSONObject response = Jsons.parseObject(res.body(), JSONObject.class);
		if (response.getInt("errcode") != 0) {
			throw BusinessRuntimeException.getInstance("发送应用消息: " + response.getStr("errmsg"));
		}
	}

	@Override
	public CorpGroupChatListDetail getGroupChatList(String corpId, String cursor, Integer limit) throws Exception {
		String url = String.format("https://qyapi.weixin.qq.com/cgi-bin/externalcontact/groupchat/list?access_token=%s", getAccessToken(corpId));
		JSONObject params = new JSONObject();
		params.putOpt("cursor", cursor);
		params.putOpt("limit", limit);
		HttpResponse<String> response = J11HttpC.custom()
				.ofPost()
				.headers(J11HttpC.ReqType.raw_json)
				.body(HttpRequest.BodyPublishers.ofString(params.toString(), IoKit.Charsets.UTF_8.getCharset()))
				.url(url)
				.send(HttpResponse.BodyHandlers.ofString());
		CorpGroupChatListDetail chatListDetail = Jsons.parseObject(response.body(), CorpGroupChatListDetail.class);
		if (response.statusCode() != 200 || chatListDetail.getErrcode() != 0) {
			throw BusinessRuntimeException.getInstance("企业微信获取客户群列表错误:" + chatListDetail.getErrmsg());
		}
		return chatListDetail;
	}

	@Override
	public CorpGroupChatDetail getCorpGroupChatDetail(String corpId, CorpXmlMessage message) throws Exception {
		String url = String.format("https://qyapi.weixin.qq.com/cgi-bin/externalcontact/groupchat/get?access_token=%s", getAccessToken(corpId));
		JSONObject params = new JSONObject();
		params.putOpt("chat_id", message.getChatId());
		//是否需要返回群成员的名字group_chat.member_list.name。0-不返回；1-返回。默认不返回
		params.putOpt("need_name", 1);
		HttpResponse<String> response = J11HttpC.custom()
				.ofPost()
				.headers(J11HttpC.ReqType.raw_json)
				.body(HttpRequest.BodyPublishers.ofString(params.toString(), IoKit.Charsets.UTF_8.getCharset()))
				.url(url)
				.send(HttpResponse.BodyHandlers.ofString());
		JSONObject jsonObject = Jsons.parseObject(response.body(), JSONObject.class);
		if (response.statusCode() != 200 || jsonObject.getInt("errcode") != 0) {
			throw BusinessRuntimeException.getInstance("企业微信获取客户群详情错误:" + jsonObject.get("errmsg"));
		}
		return Jsons.parseObject(jsonObject.get("group_chat"), CorpGroupChatDetail.class);
	}

	@Override
	public CorpUserExternalTagGroupInfo addCorpTag(CorpUserExternalTagGroupInfo tagGroup, String corpId) throws Exception {
		corpId = Optional.ofNullable(corpId).orElse(yhlXWxCorpConfig.getCorpId());
		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/add_corp_tag?access_token=" + getAccessToken(corpId);

		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(tagGroup.getTagGroup()), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());

		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("企业微信添加企业客户标签错误: " + res.body());
		}

		CorpUserExternalTagGroupInfo corpUserExternalTagGroupInfo = Jsons.parseObject(res.body(), CorpUserExternalTagGroupInfo.class);
		if (corpUserExternalTagGroupInfo.getErrcode() != 0) {
			throw BusinessRuntimeException.getInstance("新增标签错误:{0}", corpUserExternalTagGroupInfo.getErrmsg());
		}

		return corpUserExternalTagGroupInfo;
	}

	@Override
	public List<CorpUserExternalTagGroupInfo.TagGroup> getAllCorpTagByGroupsId(String groupId, String corpId) throws Exception {
		corpId = Optional.ofNullable(corpId).orElse(yhlXWxCorpConfig.getCorpId());
		//获取该标签组下的所有标签
		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get_corp_tag_list?access_token=" + getAccessToken(corpId);
		JSONObject jsonObject = new JSONObject();
		jsonObject.putOpt("group_id", List.of(groupId));
		HttpResponse<String> res = J11HttpC.custom()
				.ofPost()
				.url(url)
				.headers(J11HttpC.ReqType.raw_json)
				.body(HttpRequest.BodyPublishers.ofString(jsonObject.toString(), IoKit.Charsets.UTF_8.getCharset()))
				.send(HttpResponse.BodyHandlers.ofString());
		JSONObject re = Jsons.parseObject(res.body(), JSONObject.class);
		List<CorpUserExternalTagGroupInfo.TagGroup> tagGroups = Jsons.parseList(re.get("tag_group"), CorpUserExternalTagGroupInfo.TagGroup.class);
		if (re.getInt("errcode") != 0) {
			throw BusinessRuntimeException.getInstance("获取标签组下所有标签错误");
		}
		return tagGroups;
	}

	@Override
	public void updateGroupsConfig(CorpGroupConfig config) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/groupchat/update_join_way?access_token=" + getAccessToken(null);

		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(config), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("更新企业微信客户群配置失败: " + res.body());
		}
	}

	@Override
	public CorpBaseResult editCorpTag(JSONObject params) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/edit_corp_tag?access_token=" + getAccessToken(null);

		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(params.toString(), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());

		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("企业微信编辑企业客户标签错误: " + res.body());
		}

		CorpBaseResult corpBaseResult = Jsons.parseObject(res.body(), CorpBaseResult.class);
		if (corpBaseResult.getErrcode() != 0) {
			throw BusinessRuntimeException.getInstance("修改标签错误:{0}", corpBaseResult.getErrmsg());
		}
		return corpBaseResult;
	}

	@Override
	public CorpBaseResult delCorpTag(String[] tagIds, String[] groupIds) throws Exception {
		JSONObject params = new JSONObject();
		if (tagIds != null && tagIds.length > 0) {
			params.putOpt("tag_id", tagIds);
		}
		if (groupIds != null && groupIds.length > 0) {
			params.putOpt("group_id", groupIds);
		}
		//https://qyapi.weixin.qq.com/cgi-bin/externalcontact/del_corp_tag?access_token=ACCESS_TOKEN

		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/del_corp_tag?access_token=" + getAccessToken(null);

		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(params.toString(), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());

		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("企业微信删除企业客户标签错误: " + res.body());
		}

		CorpUserExternalTagGroupInfo corpUserExternalTagGroupInfo = Jsons.parseObject(res.body(), CorpUserExternalTagGroupInfo.class);
		if (corpUserExternalTagGroupInfo.getErrcode() != 0) {
			throw BusinessRuntimeException.getInstance("企业微信删除企业客户标签错误:{0}", corpUserExternalTagGroupInfo.getErrmsg());
		}

		return Jsons.parseObject(res.body(), CorpBaseResult.class);
	}

	@Override
	public CorpBaseResult markTag(String externalUserid, String userid, String[] tags, String[] removeTags) throws Exception {
		JSONObject params = new JSONObject();
		params.putOpt("external_userid", externalUserid);
		params.putOpt("userid", userid);
		if (tags != null && tags.length > 0) {
			params.putOpt("add_tag", tags);
		}
		if (removeTags != null && removeTags.length > 0) {
			params.putOpt("remove_tag", removeTags);
		}
		//https://qyapi.weixin.qq.com/cgi-bin/externalcontact/del_corp_tag?access_token=ACCESS_TOKEN

		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/mark_tag?access_token=" + getAccessToken(null);

		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(params.toString(), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());

		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("企业微信编辑企业客户标签错误: " + res.body());
		}

		return Jsons.parseObject(res.body(), CorpBaseResult.class);
	}

	@Override
	public CorpMsgTemplateAddResult addMsgTemplate(String corpId, CorpMsgTemplate msgTemplate) throws Exception {
		corpId = Optional.ofNullable(corpId).orElse(yhlXWxCorpConfig.getCorpId());
		String accessToken = getAccessToken(corpId);
		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/add_msg_template?access_token=" + accessToken;
		log.info("发送的内容为: {}",Jsons.toJson(msgTemplate));
		log.info("企业微信群发消息 corpId:{},userCount:{}", corpId, msgTemplate.getExternalUserid().size());
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(msgTemplate), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("企业微信创建企业群发错误: " + res.body());
		}
		log.info("企业微信创建群发 result:{}",res.body());

		return Jsons.parseObject(res.body(),CorpMsgTemplateAddResult.class);
	}

	@Override
	public String getMediaIdByFileName(String msgType, String url, String corpId) {
		//临时解决
		if ("miniprogram".equals(msgType)) {
			msgType = "image";
		}
		String[] split = url.split("/");
		String filename = split[split.length - 1];
		String mediaId = redisService.getStr(String.format(RedisService.key.CORP_WECLOME_MEDIA.getName(), corpId) + filename.split("\\.")[0]);

		if (StringUtils.isBlank(mediaId)) {
			File file = new File("./" + filename);
			HttpUtil.downloadFile(url, file);

			Map<String, Object> paramMap = new HashMap<>();
			//文件上传只需将参数中的键指定（默认file），值设为文件对象即可，对于使用者来说，文件上传与普通表单提交并无区别
			paramMap.put("media", FileUtil.file(file.getAbsolutePath()));

			try {
				String result = HttpUtil.post("https://qyapi.weixin.qq.com/cgi-bin/media/upload?type=" + msgType + "&access_token=" + getAccessToken(corpId), paramMap);

				CorpMediaUploadResult corpMediaUploadResult = JSONUtil.toBean(result, CorpMediaUploadResult.class);
				if (StringUtils.isBlank(corpMediaUploadResult.getMediaId())) {
					throw BusinessRuntimeException.getInstance("企微上传素材错误。回现素材为空.");
				}
				FileUtil.del(file);
				long time = (corpMediaUploadResult.getCreatedAt() + 60 * 60 * 24 * 3 - 60 * 60) - System.currentTimeMillis() / 1000;
				redisService.set(String.format(RedisService.key.CORP_WECLOME_MEDIA.getName(), corpId) + filename, corpMediaUploadResult.getMediaId(), time);
				return corpMediaUploadResult.getMediaId();
			} catch (Exception e) {
				FileUtil.del(file);
				throw BusinessRuntimeException.getInstance("上传临时素材出错,errorMsg:" + StringUtil.getErrorText(e));
			}
		}

		return mediaId;
	}

	@Override
	public CorpBaseResult remindBulkTask(String msgid, String corpId) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/remind_groupmsg_send?access_token=" + getAccessToken(corpId);
		log.info("提醒客服群发消息msgId:{}", msgid);
		JSONObject json = new JSONObject();
		json.putOpt("msgid", msgid);
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(json.toString(), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("提醒客服群发消息错误: " + res.body());
		}
		log.info("提醒客服群发消息 result:{}", res.body());
		return Jsons.parseObject(res.body(), CorpBaseResult.class);
	}

	public String access_token(String corpId) throws Exception {
		WxCorpApp wxCorpApp = WeChatCorpFactory.getCorpConfigStrategy(corpId);
		if (corpId == null) {
			throw BusinessRuntimeException.getInstance("{0}企业微信不存在", corpId);
		}
		String url = String.format("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s", wxCorpApp.getCorpId(), wxCorpApp.getCorpSecret());
		HttpResponse<byte[]> res =
				J11HttpC.custom()
						.ofGet()
						.url(url)
						.cacheURI()
						.send(HttpResponse.BodyHandlers.ofByteArray());

		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("获取access_token失败: " + IoKit.toString(res.body()));
		}

		WechatAccessToken token = Jsons.parseObject(res.body(), WechatAccessToken.class);
		if (null == token) {
			throw BusinessRuntimeException.getInstance("获取企业微信access_token失败: " + IoKit.toString(res.body()));
		}

		final Integer errcode = token.getErrcode();
		if (null != errcode && 0 != errcode) {
			throw BusinessRuntimeException.getInstance("获取企业微信access_token失败: " + token.getErrmsg() + "[" + errcode + "]");
		}
		//存入redis
		String accessToken = token.getAccessToken();
		// 设置过期时间 比微信官方给的时间提前300秒过期
		redisService.set(RedisKey.CORP_ACCESS_TOKEN_KEY + corpId, accessToken, token.getExpiresIn() - 300L);
		return accessToken;
	}
	public String js_ticket(String corpId) throws Exception {
		String url = String.format("https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=%s", this.getAccessToken(corpId));
		HttpResponse<byte[]> res =
				J11HttpC.custom()
						.ofGet()
						.url(url)
						.cacheURI()
						.send(HttpResponse.BodyHandlers.ofByteArray());

		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("获取js_ticket失败: " + IoKit.toString(res.body()));
		}

		OffiaccountJsTicket ticket = Jsons.parseObject(res.body(), OffiaccountJsTicket.class);
		if (null == ticket) {
			throw BusinessRuntimeException.getInstance("获取js_ticket失败: " + IoKit.toString(res.body()));
		}

		final Integer errcode = ticket.getErrcode();
		if (null != errcode && 0 != errcode) {
			throw BusinessRuntimeException.getInstance("获取js_ticket失败: " + ticket.getErrmsg() + "[" + errcode + "]");
		}
		String jsTicket = ticket.getTicket();
		redisService.set(RedisKey.CORP_JS_TICKET_KEY + corpId, jsTicket, ticket.getExpiresIn() - 300L);
		return jsTicket;
	}

	private String agentJsTicket(String corpId) throws Exception {
		String url = String.format("https://qyapi.weixin.qq.com/cgi-bin/ticket/get?access_token=%s&type=agent_config", this.getAccessToken(corpId));
		HttpResponse<byte[]> res =
				J11HttpC.custom()
						.ofGet()
						.url(url)
						.cacheURI()
						.send(HttpResponse.BodyHandlers.ofByteArray());

		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("获取agent_js_ticket失败: " + IoKit.toString(res.body()));
		}

		OffiaccountJsTicket ticket = Jsons.parseObject(res.body(), OffiaccountJsTicket.class);
		if (null == ticket) {
			throw BusinessRuntimeException.getInstance("获取agent_js_ticket失败: " + IoKit.toString(res.body()));
		}
		final Integer errcode = ticket.getErrcode();
		if (null != errcode && 0 != errcode) {
			throw BusinessRuntimeException.getInstance("获取agent_js_ticket失败: " + ticket.getErrmsg() + "[" + errcode + "]");
		}
		String jsTicket = ticket.getTicket();
		redisService.set(RedisKey.CORP_AGENT_JS_TICKET_KEY + corpId, jsTicket, ticket.getExpiresIn() - 300L);
		return jsTicket;
	}


	@Override
	public WxCpLinkAddResp customerAcquisitionCreateLink(String corpId, WxCpLinkAdd wxCpLinkAdd) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/customer_acquisition/create_link?access_token=" + getAccessToken(corpId);

		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(wxCpLinkAdd), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("创建获客助手链接: " + res.body());
		}

		WxCpLinkAddResp resp = Jsons.parseObject(res.body(), WxCpLinkAddResp.class);
		if (resp.getErrcode() != 0) {
			throw BusinessRuntimeException.getInstance("创建获客助手链接: " + resp.getErrmsg());
		}
		return resp;
	}


	@Override
	public WxCpLinkAddResp customerAcquisitionUpdateLink(String corpId, WxCpLinkUpd wxCpLinkUpd) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/customer_acquisition/update_link?access_token=" + getAccessToken(corpId);

		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(wxCpLinkUpd), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("编辑获客助手链接: " + res.body());
		}

		WxCpLinkAddResp resp = Jsons.parseObject(res.body(), WxCpLinkAddResp.class);
		if (resp.getErrcode() != 0) {
			throw BusinessRuntimeException.getInstance("编辑获客助手链接: " + resp.getErrmsg());
		}
		return resp;
	}


	@Override
	public WxCpLinkAddResp customerAcquisitionDeleteLink(String corpId, WxCpLinkUpd wxCpLinkUpd) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/customer_acquisition/delete_link?access_token=" + getAccessToken(corpId);

		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(wxCpLinkUpd), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("删除获客助手链接: " + res.body());
		}

		WxCpLinkAddResp resp = Jsons.parseObject(res.body(), WxCpLinkAddResp.class);
		if (resp.getErrcode() != 0) {
			throw BusinessRuntimeException.getInstance("删除获客助手链接: " + resp.getErrmsg());
		}
		return resp;
	}

	@Override
	public List<String> getPermitUserList(String corpId, String type) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/msgaudit/get_permit_user_list?access_token=" + getMsgAccessToken(corpId);

		JSONObject params = new JSONObject();
		if (StringUtils.isNotBlank(type)) {
			params.putOpt("type",type);
		}
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(params), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("获取会话内容存档开启成员列表: " + res.body());
		}

		CorpBaseResult resp = Jsons.parseObject(res.body(), CorpBaseResult.class);
		if (resp.getErrcode() != 0) {
			throw BusinessRuntimeException.getInstance("获取会话内容存档开启成员列表: " + resp.getErrmsg());
		}
		return JSONUtil.parseObj(res.body()).getJSONArray("ids").toList(String.class);
	}

	public String getKfAccessToken(String corpId) throws Exception {
		WxCorpApp wxCorpApp = WeChatCorpFactory.getCorpConfigStrategy(corpId);
		if (StringUtils.isBlank(wxCorpApp.getMsgSecret())) {
			throw BusinessRuntimeException.getInstance("该企业会话存档权限尚未开通.请联系管理员配置api secret");
		}
		String accessToken = redisService.getStr(RedisKey.CORP_MSG_ACCESS_TOKEN_KEY + corpId);
		if(StringUtils.isBlank(accessToken)){
			String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken" +
					"?corpid=" + corpId +"&corpsecret=" + wxCorpApp.getMsgSecret();

			HttpResponse<String> res =
					J11HttpC.custom()
							.ofGet()
							.url(url)
							.send(HttpResponse.BodyHandlers.ofString());

			if (200 != res.statusCode()) {
				throw BusinessRuntimeException.getInstance("获取企业微信access_token失败: " + res.body());
			}
			JSONObject result = new JSONObject(res.body());

			final Integer errcode = result.getInt("errcode");
			if (null != errcode && 0 != errcode) {
				throw BusinessRuntimeException.getInstance("获取企业微信access_token失败: " + result.getStr("errmsg") + "[" + errcode + "]");
			}
			accessToken = result.getStr("access_token");
			redisService.set(RedisKey.CORP_MSG_ACCESS_TOKEN_KEY + corpId, accessToken, result.getInt("expires_in") - 300L);
			return accessToken;
		}
		return accessToken;
	}

	@Override
	public WxCpKfAccountAddResp addAccount(String corpId, WxCpKfAccountAdd add) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/kf/account/add?access_token=" + getAccessToken(corpId);
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(add), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("添加客服帐号: " + res.body());
		}

		WxCpKfAccountAddResp resp = Jsons.parseObject(res.body(), WxCpKfAccountAddResp.class);
		if (resp.getErrcode() != 0) {
			throw BusinessRuntimeException.getInstance("添加客服帐号: " + resp.getErrmsg());
		}
		return resp;
	}

	@Override
	public void delAccount(String corpId, WxCpKfAccountDel del) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/kf/account/del?access_token=" + getAccessToken(corpId);
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(del), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("删除客服帐号: " + res.body());
		}

		JSONObject jsonObject = new JSONObject(res.body());
		if (jsonObject.getInt("errcode") != 0) {
			throw BusinessRuntimeException.getInstance("删除客服账号出错: " + jsonObject.getStr("errmsg"));
		}
	}

	@Override
	public void updAccount(String corpId, WxCpKfAccountUpd upd) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/kf/account/update?access_token=" + getAccessToken(corpId);
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(upd), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("修改客服帐号: " + res.body());
		}

		JSONObject jsonObject = new JSONObject(res.body());
		if (jsonObject.getInt("errcode") != 0) {
			throw BusinessRuntimeException.getInstance("修改客服账号出错: " + jsonObject.getStr("errmsg"));
		}
	}

	@Override
	public WxCpKfMsgListResp syncMsg(String corpId, String cursor, String token, Integer limit, String openKfId) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/kf/sync_msg?access_token=" + getAccessToken(corpId);

		JSONObject json = new JSONObject();
		json.putOpt("open_kfid", openKfId);
		if (cursor != null) {
			json.putOpt("cursor", cursor);
		}
		if (token != null) {
			json.putOpt("token", token);
		}
		if (limit != null) {
			json.putOpt("limit", limit);
		}
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(json), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("读取消息: " + res.body());
		}

		WxCpKfMsgListResp resp = Jsons.parseObject(res.body(), WxCpKfMsgListResp.class);
		if (resp.getErrcode() != 0) {
			throw BusinessRuntimeException.getInstance("读取消息错误: " + resp.getErrmsg());
		}
		return resp;
	}

	@Override
	public WxCpKfMsgSendResp sendMsgOnEvent(String corpId, WxCorpKfMsgSendRequest request) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/kf/send_msg_on_event?access_token=" + getAccessToken(corpId);

		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(request), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("发送客服事件响应消息: " + res.body());
		}

		WxCpKfMsgSendResp resp = Jsons.parseObject(res.body(), WxCpKfMsgSendResp.class);
		if (resp.getErrcode() != 0) {
			throw BusinessRuntimeException.getInstance("发送欢迎语等事件响应消息错误: " + resp.getErrmsg());
		}
		return resp;
	}

	@Override
	public CorpKfServicerResp getKfServicerList(String corpId,String openKfId) throws Exception {
		String url = String.format("https://qyapi.weixin.qq.com/cgi-bin/kf/servicer/list?access_token=%s&open_kfid=%s", getAccessToken(corpId), openKfId);
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("获取微信客服接待人员响应消息: " + res.body());
		}

		CorpKfServicerResp resp = Jsons.parseObject(res.body(), CorpKfServicerResp.class);
		if (resp.getErrcode() != 0) {
			throw BusinessRuntimeException.getInstance("获取微信客服接待人员响应消息错误: " + resp.getErrmsg());
		}
		return resp;
	}

	@Override
	public void moveWaitingQueue(String corpId, String openKfId, String externalUserId) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/kf/service_state/trans?access_token=" + getAccessToken(corpId);
		JSONObject request = new JSONObject();
		request.putOpt("open_kfid", openKfId);
		request.putOpt("external_userid", externalUserId);
		//待接入池排队中
		request.putOpt("service_state", 2);
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(request), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("移入等待池响应消息: " + res.body());
		}

		CorpKfSessionResp resp = Jsons.parseObject(res.body(), CorpKfSessionResp.class);
		if (resp.getErrcode() != 0) {
			throw BusinessRuntimeException.getInstance("移入等待池响应消息错误: " + resp.getErrmsg());
		}
	}

	/**
	 *
	 * @param corpId
	 * @param openKfId
	 * @param userid
	 * @param externalUserId
	 * @param serviceState 0未处理 1由智能助手接待 2待接入池排队中 3由人工接待 4已结束/未开始
	 * @throws Exception
	 */
	@Override
	public void transCorpKfSessionToServicer(String corpId, String openKfId, String userid, String externalUserId, Integer serviceState) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/kf/service_state/trans?access_token=" + getAccessToken(corpId);
		JSONObject request = new JSONObject();
		request.putOpt("open_kfid", openKfId);
		request.putOpt("external_userid", externalUserId);
		//由人工接待
		request.putOpt("service_state", serviceState);
		request.putOpt("servicer_userid", userid);
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(request), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("客服接入会话响应消息: " + res.body());
		}

		CorpKfSessionResp resp = Jsons.parseObject(res.body(), CorpKfSessionResp.class);
		if (resp.getErrcode() != 0) {
			throw BusinessRuntimeException.getInstance("客服接入会话响应消息错误: " + resp.getErrmsg());
		}
	}

	@Override
	public void sendSatisfactionMsg(String msgid, String openKfId, String externalUserId, String msgCode) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/kf/send_msg_on_event?access_token=" + getAccessToken(yhlXWxCorpConfig.getCorpId());
		CorpKfSatisfactionMsg corpKfSatisfactionMsg = Jsons.parseObject(String.format(SATISFACTION, msgid, msgid, msgid), CorpKfSatisfactionMsg.class);
		corpKfSatisfactionMsg.setMsgid(RandomUtil.randomString(16));
		corpKfSatisfactionMsg.setTouser(externalUserId);
		corpKfSatisfactionMsg.setOpen_kfid(openKfId);
		corpKfSatisfactionMsg.setCode(msgCode);
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(corpKfSatisfactionMsg), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("满意度调查内容: " + res.body());
		}

		CorpKfSessionResp resp = Jsons.parseObject(res.body(), CorpKfSessionResp.class);
		if (resp.getErrcode() != 0) {
			throw BusinessRuntimeException.getInstance("满意度调查内容错误: " + resp.getErrmsg());
		}
		log.info("发送满意度调查返回:{}", resp.getErrmsg());
	}

	@Override
	public CorpKfUserInfo getCorpKfUserInfo(String externalUserid) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/kf/customer/batchget?access_token=" + getAccessToken(yhlXWxCorpConfig.getCorpId());
		JSONObject params = new JSONObject();
		params.putOpt("external_userid_list", List.of(externalUserid));
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(params), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("客户基础信息: " + res.body());
		}
		JSONObject re = Jsons.parseObject(res.body(), JSONObject.class);
		if (re.getInt("errcode") != 0) {
			throw BusinessRuntimeException.getInstance("客户基础信息错误: " + re.getStr("errmsg"));
		}
		List<CorpKfUserInfo> customerList = re.getJSONArray("customer_list").toList(CorpKfUserInfo.class);
		return customerList.isEmpty() ? null : customerList.get(0);
	}

	@Override
	public String getCorpKfUrl(String openKfId, String scene) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/kf/add_contact_way?access_token=" + getAccessToken(yhlXWxCorpConfig.getCorpId());
		JSONObject params = new JSONObject();
		params.putOpt("open_kfid", openKfId);
		if (StrUtil.isNotEmpty(scene)) {
			params.putOpt("scene", scene);
		}
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(params), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("微信客服账号链接信息: " + res.body());
		}
		JSONObject re = Jsons.parseObject(res.body(), JSONObject.class);
		if (re.getInt("errcode") != 0) {
			throw BusinessRuntimeException.getInstance("微信客服账号链接信息错误: " + re.getStr("errmsg"));
		}
		return re.getStr("url");
	}

	@Override
	public void delCorpKfServicer(String openKfId, List<String> delDutyFollowId) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/kf/servicer/del?access_token=" + getAccessToken(yhlXWxCorpConfig.getCorpId());
		JSONObject params = new JSONObject();
		params.putOpt("open_kfid", openKfId);
		params.putOpt("userid_list", delDutyFollowId);
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(params), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("删除接待人员信息: " + res.body());
		}
		JSONObject re = Jsons.parseObject(res.body(), JSONObject.class);
		if (re.getInt("errcode") != 0) {
			throw BusinessRuntimeException.getInstance("删除接待人员信息错误: " + re.getStr("errmsg"));
		}
	}

	@Override
	public void addCorpKfServicer(String openKfId, Set<String> dutyFollowIds) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/kf/servicer/add?access_token=" + getAccessToken(yhlXWxCorpConfig.getCorpId());
		JSONObject params = new JSONObject();
		params.putOpt("open_kfid", openKfId);
		params.putOpt("userid_list", dutyFollowIds);
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(params), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("添加接待人员信息: " + res.body());
		}
		JSONObject re = Jsons.parseObject(res.body(), JSONObject.class);
		if (re.getInt("errcode") != 0) {
			throw BusinessRuntimeException.getInstance("添加接待人员信息错误: " + re.getStr("errmsg"));
		}
	}

	@Override
	public void sendTextMsg(String corpId, WxCorpKfMsgSendRequest request) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/kf/send_msg?access_token=" + getAccessToken(corpId);

		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(request), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("发送文本事件响应消息: " + res.body());
		}

		WxCpKfMsgSendResp resp = Jsons.parseObject(res.body(), WxCpKfMsgSendResp.class);
		if (resp.getErrcode() != 0) {
			throw BusinessRuntimeException.getInstance("发送文本事件响应消息错误: " + resp.getErrmsg());
		}
	}


	@Override
	public ServiceStateResponse getCorpKfSessionStatus(String corpId, String openKfId, String externalUserid) throws Exception {
		String url = "https://qyapi.weixin.qq.com/cgi-bin/kf/service_state/get?access_token=" + getAccessToken(corpId);
		JSONObject jsonObject = new JSONObject();
		jsonObject.putOpt("open_kfid", openKfId);
		jsonObject.putOpt("external_userid", externalUserid);
		HttpResponse<String> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.headers(J11HttpC.ReqType.raw_json)
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(jsonObject), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofString());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("用户会话状态消息: " + res.body());
		}
		ServiceStateResponse serviceStateResponse = Jsons.parseObject(res.body(), ServiceStateResponse.class);
		if (serviceStateResponse.getErrcode() != 0) {
			throw BusinessRuntimeException.getInstance("添加接待人员信息错误: " + serviceStateResponse.getErrmsg());
		}
		return serviceStateResponse;
	}

	@Override
	public String getMedia(String corpId, String mediaId) throws Exception {
		if (StringUtils.isBlank(mediaId)){
			return null;
		}
		String url = "https://qyapi.weixin.qq.com/cgi-bin/media/get?access_token=" + getAccessToken(corpId) + "&media_id=" + mediaId;
		HttpResponse<byte []> res =
				J11HttpC.custom()
						.ofGet()
						.url(url)
						.send(HttpResponse.BodyHandlers.ofByteArray());
		if (200 != res.statusCode()) {
			throw BusinessRuntimeException.getInstance("企业微信获取素材错误:" + res.body());
		}
		// 提取文件名
		String contentDisposition = res.headers().firstValue("Content-Disposition").orElse("");
		String fileName = null;
		if (contentDisposition.contains("filename=")) {
			fileName = contentDisposition.substring(contentDisposition.indexOf("filename=") + 9).replace("\"", "");
		}
		byte[] body = res.body();
		Path tempFile = Files.createTempFile("media" + System.currentTimeMillis(), StrUtil.subAfter(fileName, StrUtil.DOT, true));
		try (FileImageOutputStream imageOutput = new FileImageOutputStream(tempFile.toFile())) {
			imageOutput.write(body, 0, body.length);
		}
		Map<String, Object> paramMap = new HashMap<>();
		paramMap.put("file", tempFile.toFile());
		JSONObject result = JSONUtil.parseObj(HttpUtil.post("https://files.liuliangbang.vip/pic/ups", paramMap));
		FileUtil.del(tempFile);
		return result.getJSONObject("value").getJSONArray("saved").getJSONObject(0).getJSONObject("info").getStr("cdnUrl");
	}

	@Override
	public String generateTempCorpFollowCode(CorpFollowContact corpFollowContact) throws Exception {
		//配置
		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/add_contact_way?access_token=" + getAccessToken(null);
		HttpResponse<byte[]> res =
				J11HttpC.custom()
						.ofPost()
						.url(url)
						.cacheURI()
						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(corpFollowContact), IoKit.Charsets.UTF_8.getCharset()))
						.send(HttpResponse.BodyHandlers.ofByteArray());
		String body = IoKit.toString(res.body());
		JSONObject jsonObject = Jsons.parseObject(body, JSONObject.class);
		if (jsonObject.getInt("errcode") != 0) {
			throw BusinessRuntimeException.getInstance("获取客服二维码错误，请联系客服");
		}
		return body;
	}
}
