package com.cyksj.web.controller.manage.account;

import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cyksj.common.annotation.Log;
import com.cyksj.common.annotation.NoSubmit;
import com.cyksj.common.constant.Constant;
import com.cyksj.common.exception.BusinessRuntimeException;
import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
import com.cyksj.common.util.J11HttpC;
import com.cyksj.common.util.Jsons;
import com.cyksj.dto.RedisKey;
import com.cyksj.dto.Result;
import com.cyksj.enums.BusinessType;
import com.cyksj.enums.GatewayResponse;
import com.cyksj.mapper.*;
import com.cyksj.mapper.manage.cms.CmsUserMapper;
import com.cyksj.mapper.sys.SysEmailMapper;
import com.cyksj.model.entity.*;
import com.cyksj.model.excel.*;
import com.cyksj.model.manage.request.ReqAccountIncrExpiryTime;
import com.cyksj.model.manage.views.AccountView;
import com.cyksj.model.manage.views.GroupsRelationView;
import com.cyksj.model.request.DelAccountTripsReq;
import com.cyksj.model.response.ExpiredAccountRep;
import com.cyksj.model.response.OrderDonAccountRep;
import com.cyksj.model.views.*;
import com.cyksj.redis.RedisService;
import com.cyksj.service.chatgpt.ChatGptAccountService;
import com.cyksj.service.mange.AccountCommonService;
import com.cyksj.service.mange.CmsAccountService;
import com.cyksj.service.mange.CmsGroupService;
import com.cyksj.service.mange.cms.CmsUserManager;
import com.cyksj.service.template.TemplateCommonService;
import com.cyksj.web.util.EasyExcelUtils;
import com.ejlchina.searcher.BeanSearcher;
import com.ejlchina.searcher.SearchResult;
import com.ejlchina.searcher.param.Operator;
import com.ejlchina.searcher.util.MapBuilder;
import com.ejlchina.searcher.util.MapUtils;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.http.HttpResponse;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author chan
 * @date 2022/9/27 6:15 PM
 */
@Slf4j
@RequiredArgsConstructor
@RequestMapping("/manage/account")
@RestController
public class CmsAccountController {

    private final BeanSearcher beanSearcher;

    private final HttpServletRequest request;

    private final CmsAccountService accountService;

    private final HomeGroupMapper homeGroupMapper;

    private final GroupsMapper groupsMapper;

	private final TemplateCommonService templateCommonService;

	private final CmsGroupService cmsGroupService;

	private final UserTicketClearedRecordMapper userTicketClearedRecordMapper;

	private final UserMapper userMapper;

	private final CmsUserMapper cmsUserMapper;

	private final CmsUserManager cmsUserManager;

	private final GroupsRelationMapper groupsRelationMapper;

	private final AccountDoubleVerifyRecordMapper doubleVerifyRecordMapper;

	private final AccountUpdatePasswordRecordMapper accountUpdatePasswordRecordMapper;

	private final AccountCommonService accountCommonService;

	private final RedisService redisService;

	private final GoodsDonSkuMapper goodsDonSkuMapper;

	private final SysEmailMapper sysEmailMapper;

	private final AccountBlockedReplaceRecordMapper accountBlockedReplaceRecordMapper;

	private final ChatGptAccountService chatGptAccountService;

	private final MidjourneyAccountMapper midjourneyAccountMapper;

	private final GoodsDonMapper goodsDonMapper;

	private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();

	private final OrderDonMapper orderDonMapper;

	@GetMapping("/get")
	@SaCheckPermission("account:view")
	public Result<SearchResult<AccountView>> get(String title, Long userId, String showId, String nickName, String submitAccount, String expiryStartTime, String expiryEndTime, Boolean expiredAll, Boolean existsExpired, Boolean existsExpiredToday, Integer noChangeMonths, Boolean isMjMirror, String orderNo, Boolean isEmpty) {
		MapBuilder mapBuilder = MapUtils.flatBuilder(request.getParameterMap());
		if (StrUtil.isNotEmpty(expiryStartTime) || StrUtil.isNotEmpty(expiryEndTime)) {
			mapBuilder.field(AccountView::getExpiryTime, expiryStartTime, expiryEndTime)
					.op(Operator.Between);
		}
		if (StrUtil.isNotBlank(title)) {
			GoodsDon goodsDon = goodsDonMapper.selectOne(Wrappers.lambdaQuery(GoodsDon.class).eq(GoodsDon::getTitle, title)
					.last("limit 1"));
			if (goodsDon != null) {
				mapBuilder.field(AccountView::getGoodsId, goodsDon.getId());
				title = null;
			}
		}
		if (StrUtil.isNotEmpty(showId)) {
			userId = userMapper.getUserIdByShowId(showId);
		}
		if (isEmpty != null && isEmpty) {
			mapBuilder.field(AccountView::getNum, AccountView::getAvailableNum).sql("$1 = $2");
		}
		//用户订单号查找对应账号
		if (StrUtil.isNotBlank(orderNo)) {
			Long groupId = 0l;
			OrderDon orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
					.eq(OrderDon::getOrderNo, orderNo)
					.last("limit 1"));
			if (orderDon != null) {
				GroupsRelation relation = groupsRelationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class).eq(GroupsRelation::getId, orderDon.getRelationId()).eq(GroupsRelation::getUserId, orderDon.getUserId())
						.in(GroupsRelation::getStatus, List.of(GroupsRelation.Status.validity, GroupsRelation.Status.outside))
						.last("limit 1"));
				if (relation != null) {
					groupId = relation.getGroupsId();
				}
			}
			mapBuilder.field(AccountView::getGroupsId, groupId);
		}

		if (userId != null || StringUtils.isNotBlank(nickName) || StringUtils.isNotBlank(submitAccount)) {
			List<Long> accountIds = homeGroupMapper.getAccountIdsByUserIdOrNickNameOrSubmitAccount(userId, nickName, submitAccount);
			if (accountIds.isEmpty()) {
				return GatewayResponse.SUCCESS.newBuilder().toResult(new SearchResult<>());
			}

			mapBuilder.field(AccountView::getId, accountIds).op(Operator.InList);
		}
		String extra_sql = StrUtil.EMPTY;
		if (expiredAll != null && expiredAll) {
			extra_sql += "(if((select count(ur.id) from user_ticket_cleared_record ur where ur.account_id = a.id and deleted is true and source = 'self')>=3,true,false)) = 1";
		}

		if (noChangeMonths != null) {
			DateTime pastMonth = DateUtil.offsetMonth(DateTime.now(), -noChangeMonths);
			List<Long> accountIds = accountUpdatePasswordRecordMapper.selectPasswordNotChangedThePastMonthAccountId(pastMonth);
			mapBuilder.field(AccountView::getId, accountIds).op(Operator.InList);
		}

		if (existsExpired != null && existsExpired) {
			if (StrUtil.isNotEmpty(extra_sql)) {
				extra_sql += " and";
			}
			extra_sql += "(select count(ur.id) from user_ticket_cleared_record ur where ur.account_id = a.id and deleted is true and source = 'self') > 0";
		}
		if (existsExpiredToday != null) {
			DateTime endOfDay = DateUtil.endOfDay(DateTime.now());
			if (StrUtil.isNotEmpty(extra_sql)) {
				extra_sql += " and";
			}
			if (existsExpiredToday) {
				extra_sql += String.format(" (select min(expiry_time) from (select gt.id from groups_trips where account_id = a.id) gt inner join groups_relation gr on gr.groups_id = gt.id) <= '%s'", endOfDay);
			} else {
				extra_sql += String.format(" (select min(expiry_time) from (select gt.id from groups_trips where account_id = a.id) gt inner join groups_relation gr on gr.groups_id = gt.id) > '%s'", endOfDay);
			}
		}
		if (isMjMirror != null) {
			if (StrUtil.isNotEmpty(extra_sql)) {
				extra_sql += " and";
			}
			extra_sql += String.format(" %s (select 1 from midjourney_account ma where ma.account_id = a.id limit 1)", isMjMirror ? "EXISTS" : "NOT EXISTS");
		}
		if (StrUtil.isNotEmpty(extra_sql)) {
			mapBuilder.put("extra_sql", extra_sql);
		}

		SearchResult<AccountView> search = beanSearcher.search(AccountView.class, mapBuilder.build());
		search.getDataList().forEach((accountView) -> {
			Integer invited = homeGroupMapper.selectCount(Wrappers.lambdaQuery(HomeGroup.class).eq(HomeGroup::getAccountId, accountView.getId()).eq(HomeGroup::getStatus, "invited"));
			GroupsTrips groupsTrips = groupsMapper.selectOne(Wrappers.lambdaQuery(GroupsTrips.class).eq(GroupsTrips::getAccountId, accountView.getId()).last(" limit 1"));
			int total = groupsTrips == null ? 0 : groupsTrips.getNum() - groupsTrips.getAvailableNum();
			Integer unInvited = Math.max(total - invited, 0);
			accountView.setUnInvited(unInvited);
			accountView.setInvited(invited);
			accountView.setTotal(total);
			List<AccountUpdatePasswordRecord> recordList = beanSearcher.searchList(AccountUpdatePasswordRecord.class,
					MapUtils.builder().field(AccountUpdatePasswordRecord::getAccountId, accountView.getId())
							.onlySelect(AccountUpdatePasswordRecord::getCreatedTime)
							.orderBy(AccountUpdatePasswordRecord::getId)
							.build());
			accountView.setUpdatePwdNum(recordList.size());
			if (accountView.getCustomerServiceId() != 0) {
				CmsUser cmsUser = cmsUserMapper.selectById(accountView.getCustomerServiceId());
				Optional.ofNullable(cmsUser).ifPresent(customer -> accountView.setCustomerServiceName(cmsUser.getNickname()));
			}
			if (recordList.size() > 0) {
				accountView.setLastUpdatePwdDate(recordList.get(0).getCreatedTime());
			}
			Integer expiryCount = groupsMapper.countExpiredNum(accountView.getId());
			accountView.setExpiryCount(expiryCount);

			if (StrUtil.isEmpty(accountView.getEmailPassword())) {
				Optional.ofNullable(sysEmailMapper.selectOne(Wrappers.lambdaQuery(SysEmail.class)
								.eq(SysEmail::getEmail, accountView.getAccount()).select(SysEmail::getPassword)
								.last("limit 1")))
						.ifPresent(email -> accountView.setEmailPassword(email.getPassword()));
			}
			if (accountView.getGoodsId() == 26) {
				Integer count = midjourneyAccountMapper.selectCount(Wrappers.lambdaQuery(MidjourneyAccount.class)
						.eq(MidjourneyAccount::getAccountId, accountView.getId()));
				accountView.setIsMjMirror(count > 0 ? true : false);
			}
		});
		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
	}

    /**
     * 导出账号
     */
    @GetMapping("/export/account")
    @SaCheckPermission("account:export")
    @Log(module = "平台管理/导出账号", businessType = BusinessType.EXPORT, isSaveRequestData = true)
    public void exportAccount(String title, Long userId, String showId, String nickName, String submitAccount, String expiryStartTime, String expiryEndTime, Boolean expiredAll, Boolean existsExpired, Boolean existsExpiredToday, Integer noChangeMonths, Boolean isEmpty, HttpServletResponse response) {
        MapBuilder mapBuilder = MapUtils.flatBuilder(request.getParameterMap());
        if (StrUtil.isNotEmpty(expiryStartTime) || StrUtil.isNotEmpty(expiryEndTime)) {
            mapBuilder.field(ExcelManageAccountData::getExpiryTime, expiryStartTime, expiryEndTime)
                    .op(Operator.Between);
        }
	    if (StrUtil.isNotEmpty(showId)) {
		    userId = userMapper.getUserIdByShowId(showId);
	    }

		if (StrUtil.isNotBlank(title)) {
			GoodsDon goodsDon = goodsDonMapper.selectOne(Wrappers.lambdaQuery(GoodsDon.class).eq(GoodsDon::getTitle, title)
					.last("limit 1"));
			if (goodsDon != null) {
				mapBuilder.field(ExcelManageAccountData::getGoodsId, goodsDon.getId());
				title = null;
			}
		}

	    if (isEmpty != null && isEmpty) {
		    mapBuilder.field(ExcelManageAccountData::getNum, ExcelManageAccountData::getAvailableNum).sql("$1 = $2");
	    }

        if (userId != null || StringUtils.isNotBlank(nickName) || StringUtils.isNotBlank(submitAccount)) {
            List<Long> accountIds = homeGroupMapper.getAccountIdsByUserIdOrNickNameOrSubmitAccount(userId, nickName, submitAccount);
            if (accountIds.isEmpty()) {
                throw BusinessRuntimeException.getInstance("暂无数据！");
            }

            mapBuilder.field(ExcelManageAccountData::getId, accountIds).op(Operator.InList);
        }
	    String extra_sql = StrUtil.EMPTY;
	    if (expiredAll != null && expiredAll) {
		    extra_sql += "(if((select count(ur.id) from user_ticket_cleared_record ur where ur.account_id = a.id and deleted is true and source = 'self')>=3,true,false)) = 1";
	    }

	    if (noChangeMonths != null) {
		    DateTime pastMonth = DateUtil.offsetMonth(DateTime.now(), -noChangeMonths);
		    List<Long> accountIds = accountUpdatePasswordRecordMapper.selectPasswordNotChangedThePastMonthAccountId(pastMonth);
		    mapBuilder.field(AccountView::getId, accountIds).op(Operator.InList);
	    }

	    if (existsExpired != null && existsExpired) {
		    if (StrUtil.isNotEmpty(extra_sql)) {
			    extra_sql += " and";
		    }
		    extra_sql += "(select count(ur.id) from user_ticket_cleared_record ur where ur.account_id = a.id and deleted is true and source = 'self') > 0";
	    }
	    if (existsExpiredToday != null) {
		    DateTime endOfDay = DateUtil.endOfDay(DateTime.now());
		    if (StrUtil.isNotEmpty(extra_sql)) {
			    extra_sql += " and";
		    }
		    if (existsExpiredToday) {
			    extra_sql += String.format(" (select min(expiry_time) from (select gt.id from groups_trips where account_id = a.id) gt inner join groups_relation gr on gr.groups_id = gt.id) <= '%s'", endOfDay);
		    } else {
			    extra_sql += String.format(" (select min(expiry_time) from (select gt.id from groups_trips where account_id = a.id) gt inner join groups_relation gr on gr.groups_id = gt.id) > '%s'", endOfDay);
		    }
	    }
	    if (StrUtil.isNotEmpty(extra_sql)) {
		    mapBuilder.put("extra_sql", extra_sql);
	    }
	    List<ExcelManageAccountData> list = beanSearcher.searchAll(ExcelManageAccountData.class, mapBuilder.build());

	    EasyExcelUtils.createExcelStreamMutilByEasyExcel(response, ExcelManageAccountData.class, list, "账号列表", "账号列表", ExcelTypeEnum.XLSX, null);
    }

    /**
     * 下载批量修改密码模板
     */
    @GetMapping("/get/account/template")
    public void getAccountTemplate(HttpServletResponse response) {
        EasyExcelUtils.createTemplateExcel(response, ExcelImportAccountData.class, "账号批量修改密码模板", "账号批量修改密码模板", ExcelTypeEnum.XLSX, null);
    }

	/**
	 * 导入文件,修改密码
	 */
	@PostMapping("/import/account")
	public Result<String> importAccount(Long goodsId, MultipartFile file) throws Exception {
		long userId = StpUtil.getLoginIdAsLong();
		EasyExcel.read(file.getInputStream(), ExcelImportAccountData.class, new AnalysisEventListener<ExcelImportAccountData>() {
			@SneakyThrows
			@Override
			public void invoke(ExcelImportAccountData data, AnalysisContext analysisContext) {
				if (StrUtil.isNotBlank(data.getAccount())) {
					Account account = accountService.getOne(Wrappers.lambdaQuery(Account.class)
							.eq(Account::getGoodsId, goodsId)
							.eq(Account::getAccount, data.getAccount().trim()));
					Boolean flag = false;
					Boolean isUpdatePwd = false;
					if (account != null) {
						if (StrUtil.isNotEmpty(data.getRemark())) {
							account.setRemark(data.getRemark());
							flag = true;
						}
						if (StrUtil.isNotEmpty(data.getVerifyLink())) {
							account.setVerifyLink(data.getVerifyLink());
							flag = true;
						}
						if (StrUtil.isNotEmpty(data.getSecondEmail())) {
							account.setEmail(data.getSecondEmail());
							flag = true;
						}
						if (StrUtil.isNotEmpty(data.getRelateEmail())) {
							account.setRelateEmail(data.getRelateEmail());
							flag = true;
						}
						if (StrUtil.isNotBlank(data.getBankCard())) {
							account.setBankCard(data.getBankCard());
							flag = true;
						}
						String oldPwd = account.getPassword();
						if (StrUtil.isNotBlank(data.getExpiryTime())) {
							account.setExpiryTime(DateUtil.parse(data.getExpiryTime()));
							flag = true;
						}
						if (StrUtil.isNotBlank(data.getGptEmailPwd())) {
							account.setGptEmailPwd(data.getGptEmailPwd());
							flag = true;
						}
						String new_pass = data.getPassword();
						if (StrUtil.isNotBlank(new_pass) && !new_pass.trim().equals(account.getPassword())) {
							account.setPassword(new_pass.trim());
							account.setCustomerServiceId(0l);
							flag = true;
							isUpdatePwd = true;
							//发放模板消息
							templateCommonService.sendAccountTemplateMsg(account);

							if (StrUtil.isNotBlank(account.getGptRefreshToken())) {
								account.setGptRefreshToken(StrUtil.EMPTY);
								//清除gpt token
								redisService.del(RedisKey.CHATGPT_ACCESS_TOKEN + account.getAccount());
								redisService.del(RedisService.key.CHAT_GPT_TOKEN_EXCEPTION_KEY.getName() + account.getAccount());
							}
						}
						if (flag) {
							account.setStartTime(null);
							accountService.updateById(account);
							if (isUpdatePwd) {
								//清空过期账号 逻辑删除
								userTicketClearedRecordMapper.update(null, Wrappers.lambdaUpdate(UserTicketClearedRecord.class)
										.set(UserTicketClearedRecord::getDeleted, false)
										.eq(UserTicketClearedRecord::getAccountId, account.getId()));
								accountService.saveRecord(userId, account.getId(), oldPwd, account.getPassword(), "上传文件修改密码");
							}
						}
					}
				}
			}

			@Override
			public void doAfterAllAnalysed(AnalysisContext analysisContext) {

			}
		}).sheet().doRead();
		return GatewayResponse.SUCCESS.newBuilder().toResult("导入成功");
	}

	/**
	 * 解析chatGpt独立账号excel
	 */
	@PostMapping("/analysis/chatGPT/account")
	public Result<String> analysisChatGPTAccount(@RequestParam(required = false, defaultValue = "30") Long goodsId, @RequestParam(required = false, defaultValue = "198") Long skuId, MultipartFile file) throws IOException {
		List<Object> accountList = EasyExcel.read(file.getInputStream()).sheet().headRowNumber(0).doReadSync();
		accountService.analysisChatGPTAccount(goodsId, skuId, accountList);
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}

	/**
	 * 批量导入特殊格式excel平台账号
	 */
	@PostMapping("/import/special/goods/account")
	public Result<String> analysisChatGptAccount(@RequestParam(required = true) Long goodsId, @RequestParam(required = true) Long skuId, MultipartFile file) throws IOException {
		if (!file.getOriginalFilename().contains(".xls")) {
			throw BusinessRuntimeException.getInstance("请导入excel文件");
		}
		List<Object> accountList = EasyExcel.read(file.getInputStream()).sheet().headRowNumber(0).doReadSync();
		accountService.analysisChatGPTAccount(goodsId, skuId, accountList);
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}

	@GetMapping("/test")
	public Result<String> stest(MultipartFile file) throws IOException {
		List<Object> objects = EasyExcel.read(file.getInputStream()).sheet().headRowNumber(0).doReadSync();

		String url = "https://plus1.caifree.com/api?email=%s";
		objects.forEach(e->{
			Map<String, Object> map = Jsons.toMap(e);
			String account = String.valueOf(map.get("0"));
			if (StrUtil.isNotEmpty(account)) {
				String formatUrl = String.format(url, account);
				try {
					HttpResponse<String> send = J11HttpC.custom()
							.ofGet()
							.url(formatUrl)
							.send(HttpResponse.BodyHandlers.ofString());
					System.out.println(send.body());
				} catch (Exception exception) {

				}
			}

		});
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}

	public static void main(String[] args) {
		String url = "https://plus1.caifree.com/api?email=sentzb99@gmail.com";
		try {
			HttpResponse<String> send = J11HttpC.custom()
					.ofGet()
					.url(url)
					.send(HttpResponse.BodyHandlers.ofString());
			System.out.println(send.body());
		} catch (Exception exception) {

		}
	}

    @PostMapping("/post")
    @NoSubmit
    @Transactional(rollbackFor = Throwable.class)
    @SaCheckPermission("account:add")
    @Log(module = "平台管理/新增账号", businessType = BusinessType.POST, isSaveRequestData = true)
    public Result<String> add(@RequestBody Account account){
	    if (StrUtil.isNotBlank(account.getAccount())) {
		    account.setAccount(account.getAccount().trim());
		    account.setPassword(account.getPassword().trim());
		    //账号是否已经添加过
		    if (!account.getAccount().contains("客服")) {
			    if (accountCommonService.checkIsDupAccount(account.getGoodsId(), account.getAccount())) {
				    throw BusinessRuntimeException.getInstance("该平台下已生成该账号");
			    }
		    }
	    }
        accountService.save(account);
	    if (account.getSkuId() != null) {
		    //自动配置车队
		    GroupsTrips groupsTrips = new GroupsTrips();

		    //限制每天新车队前3天 停车数量
		    Integer availableParkingNum = 0;
		    if (account.getSkuId() == 161l) {
			    availableParkingNum = 20;
		    }
		    if (account.getSkuId() == 178l) {
			    availableParkingNum = 6;
		    }
			groupsTrips.setAvailableParkingNum(availableParkingNum);

		    groupsTrips.setSkuId(account.getSkuId());
		    groupsTrips.setAccountId(account.getId());
		    cmsGroupService.issuance(groupsTrips, account.getVerifyCodes(), false);
	    }

        return GatewayResponse.SUCCESS.newBuilder().toResult();
    }

	/**
	 * 获取导入账号模板
	 */
	@GetMapping("/get/account/import/template")
	public void getImportAccountTemplate(HttpServletResponse response) {
		EasyExcelUtils.createTemplateExcel(response, ExcelImportAccountData.class, "导入账号模板", "导入账号模板", ExcelTypeEnum.XLSX, null);
	}

	/**
	 * 导入账号
	 */
	@PostMapping("/import/account/issuance")
	public Result<String> importAccountAndIssuance(Long goodsId, Long skuId, MultipartFile file, Boolean isRecover) throws Exception {
		GoodsDonSkuView goodsDonSkuView = beanSearcher.searchFirst(GoodsDonSkuView.class, MapUtils.builder().field(GoodsDonSkuView::getGoodsId, goodsId).field(GoodsDonSkuView::getSkuId, skuId).build());
		if (goodsDonSkuView == null) {
			throw BusinessRuntimeException.getInstance("所选平台与规格不匹配");
		}
		EasyExcel.read(file.getInputStream(), ExcelImportAccountData.class, new AnalysisEventListener<ExcelImportAccountData>() {
			@SneakyThrows
			@Override
			public void invoke(ExcelImportAccountData data, AnalysisContext analysisContext) {
				String account = data.getAccount(), password = data.getPassword(), startTime = data.getStartTime(), expiryTime = data.getExpiryTime(), bankCard = data.getBankCard();
				Account at = new Account();
				if (StrUtil.isEmpty(data.getAccount())) {
					return;
				}
				if (StrUtil.isNotBlank(data.getAccount())) {
					//账号是否已经添加过
					if (!account.contains("客服")) {
						if (accountCommonService.checkIsDupAccount(goodsId, account)) {
							log.info("该平台下:{}已生成该账号:{}", goodsId, account);
							return;
						}
					}
					at.setAccount(account.trim());
					at.setPassword(password.trim());
				}
				at.setGoodsId(goodsId);
				at.setSkuId(skuId);
				at.setBankCard(bankCard);
				//激活码
				at.setActCode(data.getActCode());
				String isMonth = data.getIsMonth();
				if (StrUtil.isNotBlank(isMonth)) {
					at.setIsMonth(true);
				}
				String outsidePurchase = data.getOutsidePurchase();
				if (StrUtil.isNotEmpty(outsidePurchase)) {
					if (outsidePurchase.equals("是") || outsidePurchase.equals("1")) {
						at.setOutsidePurchase(true);
					}
				}
				at.setVerifyLink(data.getVerifyLink());
				at.setRelateEmail(data.getRelateEmail());
				at.setEmail(data.getSecondEmail());
				at.setGptEmailPwd(data.getGptEmailPwd());
				if (StrUtil.isNotBlank(startTime)) {
					at.setStartTime(DateUtil.parse(startTime));
				} else {
					at.setStartTime(DateTime.now());
				}
				if (StrUtil.isNotBlank(expiryTime)) {
					at.setExpiryTime(DateUtil.parse(expiryTime));
				} else {
					at.setExpiryTime(DateUtil.offsetMonth(at.getStartTime(), 1));
				}
				at.setRemark(data.getRemark());
				accountService.save(at);
				if (at.getSkuId() != null) {
					//自动配置车队
					GroupsTrips groupsTrips = new GroupsTrips();
					//限制每天新车队前3天 停车数量
					Integer availableParkingNum = 0;
					if (at.getSkuId() == 161l) {
						availableParkingNum = 20;
					}
					if (at.getSkuId() == 178l) {
						availableParkingNum = 6;
					}
					groupsTrips.setAvailableParkingNum(availableParkingNum);
					groupsTrips.setSkuId(at.getSkuId());
					groupsTrips.setAccountId(at.getId());
					cmsGroupService.issuance(groupsTrips, null, isRecover);
				}
			}

			@Override
			public void doAfterAllAnalysed(AnalysisContext analysisContext) {

			}
		}).sheet().doRead();
		return GatewayResponse.SUCCESS.newBuilder().toResult("导入账号成功");
	}

    @PutMapping("/update")
    @SaCheckPermission("account:update")
    @Log(module = "平台管理/编辑账号", businessType = BusinessType.PUT, isSaveRequestData = true)
    public Result<String> update(@RequestBody Account account) throws Exception {
	    Account db = accountService.getById(account.getId());
	    if (db == null) {
		    throw BusinessRuntimeException.getInstance("该账号不存在");
	    }
	    Boolean isClear = false;
	    String updateAccount = account.getAccount();
	    String dbAccount = db.getAccount();
		//排除不需要填写账号的平台
	    if (StrUtil.isNotBlank(updateAccount)){
			long userId = StpUtil.getLoginIdAsLong();
			if (!db.getPassword().equals(account.getPassword())) {
				account.setCustomerServiceId(0l);
				accountService.saveRecord(userId, account.getId(), db.getPassword(), account.getPassword(), "操作编辑");

				//清空过期账号 逻辑删除
				userTicketClearedRecordMapper.update(null, Wrappers.lambdaUpdate(UserTicketClearedRecord.class)
						.set(UserTicketClearedRecord::getDeleted, false)
						.eq(UserTicketClearedRecord::getAccountId, account.getId()));
				isClear = true;
			}
			if (!db.getAccount().equals(account.getAccount())){
				account.setAccount(account.getAccount().trim());
				//账号是否已经添加过
				if (!account.getAccount().contains("客服")) {
					if (accountCommonService.checkIsDupAccount(account.getGoodsId(), account.getAccount())) {
						throw BusinessRuntimeException.getInstance("该平台下已生成该账号");
					}
				}
				if (!dbAccount.equals(account.getAccount())) {
					isClear = true;
				}
			}
		}
	    if (StrUtil.isNotBlank(account.getPassword())){
		    account.setPassword(account.getPassword().trim());
	    }
	    String verifyCodes = account.getVerifyCodes();
	    if (StrUtil.isNotBlank(verifyCodes) && !StrUtil.equals(verifyCodes, db.getVerifyCodes())) {
		    verifyCodes = verifyCodes.replaceAll("\n", "");
		    String[] verifyCodesArray = verifyCodes.split("\\*");
		    List<String> verifyCodeList = Arrays.stream(verifyCodesArray).filter(str -> StrUtil.isNotBlank(str)).map(str -> str.trim()).collect(Collectors.toList());
		    List<GroupsRelationView> relationViews = beanSearcher.searchAll(GroupsRelationView.class, MapUtils.builder()
				    .field(GroupsRelationView::getAccountId, account.getId())
				    .onlySelect(GroupsRelationView::getId)
				    .build());
		    if (CollUtil.isNotEmpty(relationViews)) {
			    for (int i = 0; i < verifyCodeList.size(); i++) {
				    if (i < relationViews.size()) {
					    GroupsRelationView groupsRelationView = relationViews.get(i);
					    groupsRelationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class)
							    .set(GroupsRelation::getVerifyCode, verifyCodeList.get(i))
							    .eq(GroupsRelation::getId, groupsRelationView.getId()));
				    }
			    }
		    }
	    }
	    String apiSecret = account.getApiSecret();
	    if (StrUtil.isNotBlank(apiSecret)) {
		    doubleVerifyRecordMapper.update(null, Wrappers.lambdaUpdate(AccountDoubleVerifyRecord.class)
				    .set(AccountDoubleVerifyRecord::getDeleted, false)
				    .set(AccountDoubleVerifyRecord::getApiSecret, apiSecret)
				    .eq(AccountDoubleVerifyRecord::getAccountId, account.getId()));
	    }
	    account.setStartTime(null);
	    if (isClear){
		    account.setGptRefreshToken(StrUtil.EMPTY);
		    //清除gpt token
		    redisService.del(RedisKey.CHATGPT_ACCESS_TOKEN + dbAccount);
		    redisService.del(RedisService.key.CHAT_GPT_TOKEN_EXCEPTION_KEY.getName() + dbAccount);
		    templateCommonService.sendAccountTemplateMsg(account);
	    }
	    //时间有效
	    if (account.getExpiryTime() != null && account.getExpiryTime().after(DateTime.now())) {
		    groupsMapper.update(null, Wrappers.lambdaUpdate(GroupsTrips.class)
				    .set(GroupsTrips::getStatus, GroupsTrips.Status.validity)
				    .eq(GroupsTrips::getAccountId, account.getId())
				    .eq(GroupsTrips::getStatus, GroupsTrips.Status.down));
	    }
	    accountService.updateById(account);

        return GatewayResponse.SUCCESS.newBuilder().toResult();
    }

	/**
	 * 修改密码并发送模板消息
	 */
	@PutMapping("/update/pwd")
	@Log(module = "平台管理/批量修改密码", businessType = BusinessType.PUT, isSaveRequestData = true)
	public Result<String> updatePwdAndSendMsg(@RequestBody List<Account> accounts) throws Exception {
		long userId = StpUtil.getLoginIdAsLong();
		accounts.forEach(account -> {
			Account db = accountService.getById(account.getId());
			if (db != null) {
				String oldPwd = db.getPassword();
				String updatePWd = account.getPassword().trim();
				if (!oldPwd.equals(updatePWd)) {
					db.setPassword(updatePWd);
					db.setCustomerServiceId(0l);

					if (StrUtil.isNotBlank(db.getGptRefreshToken())) {
						db.setGptRefreshToken(StrUtil.EMPTY);
						//清除gpt token
						redisService.del(RedisKey.CHATGPT_ACCESS_TOKEN + db.getAccount());
						redisService.del(RedisService.key.CHAT_GPT_TOKEN_EXCEPTION_KEY.getName() + db.getAccount());
					}
					accountService.updateById(db);
					//记录
					accountService.saveRecord(userId, account.getId(), oldPwd, updatePWd, "多选修改密码");
					//清空过期账号 逻辑删除
					userTicketClearedRecordMapper.update(null, Wrappers.lambdaUpdate(UserTicketClearedRecord.class)
							.set(UserTicketClearedRecord::getDeleted, false)
							.eq(UserTicketClearedRecord::getAccountId, account.getId()));
					//发放模板消息
					try {
						account.setAccount(db.getAccount());
						templateCommonService.sendAccountTemplateMsg(account);
					} catch (Exception e) {
						log.error("发送账号{}修改密码消息模板错误", account.getId());
					}
				}
			}
		});
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}

	/**
	 * 账号已过期车票列表
	 */
	@GetMapping("/get/expired/ticket")
	public Result<SearchResult<UserTicketClearedRecordView>> getUserHadExpiredTicket() {
		SearchResult<UserTicketClearedRecordView> search = beanSearcher.search(UserTicketClearedRecordView.class, MapUtils.flatBuilder(request.getParameterMap())
				.field(UserTicketClearedRecordView::getSource, UserTicketClearedRecord.Source.self.name())
				.build());
		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
	}

	/**
	 * 移除账号过期用户车票记录
	 */
	@DeleteMapping("/clear/expired/ticket/{recordId}")
	public Result<String> clearExpiredTicket(@PathVariable Long recordId) {
		userTicketClearedRecordMapper.update(null, Wrappers.lambdaUpdate(UserTicketClearedRecord.class)
				.set(UserTicketClearedRecord::getDeleted, false)
				.eq(UserTicketClearedRecord::getId, recordId));
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}


	/**
	 * 账号未过期车票列表
	 */
	@GetMapping("/get/valid/ticket")
	public Result<SearchResult<GroupsRelationView>> getValidTicket() {
		SearchResult<GroupsRelationView> search = beanSearcher.search(GroupsRelationView.class, MapUtils.flatBuilder(request.getParameterMap())
				.field(GroupsRelationView::getStatus, GroupsRelation.Status.validity.name()).build());
		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
	}


	@DeleteMapping("/delete/{id}")
	@SaCheckPermission("account:delete")
	@Log(module = "平台管理/删除账号", businessType = BusinessType.DELETE, isSaveRequestData = true)
	public Result<String> update(@PathVariable Long id){
	    //是否关联了车队
	    Integer count = groupsMapper.selectCount(Wrappers.lambdaQuery(GroupsTrips.class).eq(GroupsTrips::getAccountId, id));
	    if (count > 0) {
		    throw BusinessRuntimeException.getInstance("已经发布账号,必须先删除车队才可删除账号");
	    }
        accountService.removeById(id);
        return GatewayResponse.SUCCESS.newBuilder().toResult();
    }

	/**
	 * 批量删除 车队以及账号
	 */
	@DeleteMapping("/bathc/delete/accountTrips")
	public Result<String> batchDeleteAccountTrips(@RequestBody DelAccountTripsReq delAccountTripsReq) throws Exception {
		long adminId = StpUtil.getLoginIdAsLong();
		accountService.batchDeleteAccountTrips(delAccountTripsReq, adminId);
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}

    @PutMapping("/update/expiryTime")
    @SaCheckPermission("account:addExpiryTime")
    public Result<String> updateExpiryTime(@RequestBody ReqAccountIncrExpiryTime incrExpiryTime){
        incrExpiryTime.getAccounts().forEach((accountId)->{
            Account account = accountService.getById(accountId);
            DateTime time = DateUtil.offsetDay(account.getExpiryTime(), incrExpiryTime.getDays());
            accountService.update(Wrappers.lambdaUpdate(Account.class).eq(Account::getId,accountId).set(Account::getExpiryTime,time));
        });
        return GatewayResponse.SUCCESS.newBuilder().toResult();
    }

	/**
	 * 需要修改主账号密码的平台
	 */
	@GetMapping("/get/expired/platform")
	public Result<String> getExpiredPlatform() {
		List<AccountView> expiredAllAccount = accountService.getExpiredAllAccount();
		Set<String> platFormSet = expiredAllAccount.stream().map(AccountView::getTitle).collect(Collectors.toSet());
		String platForms = platFormSet.stream().collect(Collectors.joining("、"));
		return GatewayResponse.SUCCESS.newBuilder().toResult(platForms);
	}

	/**
	 * 账号修改密码记录详情列表
	 */
	@GetMapping("/get/updatePwd/record")
	public Result<SearchResult<AccountUpdatePasswordRecord>> getUpdatePwdRecord() {
		SearchResult<AccountUpdatePasswordRecord> search = beanSearcher.search(AccountUpdatePasswordRecord.class, MapUtils.flatBuilder(request.getParameterMap()).build());
		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
	}

	/**
	 * 拉取最近购买订单的列表
	 */
	@GetMapping("/get/latest/orders")
	public Result<List<OrderDonAccountRep>> getLatestOrders() {
		//最近五分钟
		DateTime offFive = DateUtil.offsetMinute(DateTime.now(), -5);
		List<OrderDonAccountRep> list = beanSearcher.searchAll(OrderDonAccountRep.class, MapUtils.builder()
				.put("time", String.format("od.created_time >= '%s'", offFive))
				.field(OrderDonAccountRep::getGoodsId, 1)
				.orderBy(ExpiredAccountRep::getAccountId)
				.orderBy(ExpiredAccountRep::getNum)
				.build());
		list.forEach(data->{
			User user = userMapper.selectById(data.getUserId());
			data.setNickname(user != null ? user.getNickname() : "");
		});
		return GatewayResponse.SUCCESS.newBuilder().toResult(list);
	}

	/**
	 * 15天要到期的账号列表
	 */
	@GetMapping("/get/expired/accounts")
	public Result<List<ExpiredAccountRep>> getExpiredAccountsList() {
		//十五天后
		DateTime afterFif = DateUtil.offsetDay(DateTime.now(), 15);
		List<ExpiredAccountRep> list = beanSearcher.searchAll(ExpiredAccountRep.class, MapUtils.builder()
				.field(ExpiredAccountRep::getGoodsId, 1)
				.field(ExpiredAccountRep::getExpiredTime, afterFif).op(Operator.LessEqual)
				.orderBy(ExpiredAccountRep::getAccountId)
				.orderBy(ExpiredAccountRep::getNum)
				.build());
		return GatewayResponse.SUCCESS.newBuilder().toResult(list);
	}

	/**
	 * 修改账号密码 并发放模板消息
	 */
	@PutMapping("/put")
	public Result<String> updateAccountPwd(@RequestBody Account accountReq) throws Exception {
		if (accountReq.getId() == null || StrUtil.isEmpty(accountReq.getPassword())) {
			return GatewayResponse.FAIL.newBuilder().toResult("缺少必填入参");
		}
		Account account = accountService.getById(accountReq.getId());
		if (account == null) {
			return GatewayResponse.FAIL.newBuilder().toResult("账号不存在");
		}
		if (account.getGoodsId() != 1l) {
			return GatewayResponse.SUCCESS.newBuilder().toResult();
		}
		Boolean flag = false;
		String oldPwd = account.getPassword();
		if (account != null) {
			if (accountReq.getExpiryTime() != null) {
				account.setExpiryTime(accountReq.getExpiryTime());
				flag = true;
			}
			String new_pass = accountReq.getPassword();
			if (StrUtil.isNotBlank(new_pass) && !new_pass.trim().equals(account.getPassword())) {
				account.setPassword(new_pass.trim());
				account.setCustomerServiceId(0l);
				flag = true;
				//发放模板消息
				templateCommonService.sendAccountTemplateMsg(account);
				//记录操作日志
				accountService.recordAutoUpdatePwd(account.getId(), account.getAccount(), oldPwd, new_pass, ServletUtil.getClientIP(request));
				if (StrUtil.isNotBlank(account.getGptRefreshToken())) {
					account.setGptRefreshToken(StrUtil.EMPTY);
					//清除gpt token
					redisService.del(RedisKey.CHATGPT_ACCESS_TOKEN + account.getAccount());
					redisService.del(RedisService.key.CHAT_GPT_TOKEN_EXCEPTION_KEY.getName() + account.getAccount());
				}
				//记录
				accountService.saveRecord(0l, account.getId(), oldPwd, new_pass, "自动修改密码");
			}
			if (flag) {
				accountService.updateById(account);
			}
		}
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}

	/**
	 * 客服需要修改密码详情
	 */
	@GetMapping("/get/updatePwd/num")
	public Result<CustomerServiceUpdatePwdDetail> updatePwdDetails() {
		long userId = StpUtil.getLoginIdAsLong();
		CmsUser user = cmsUserManager.getUser(userId);
		MapBuilder builder = MapUtils.builder();
		if (user != null && user.getRoleNames() != null && user.getRoleNames().contains("客服")) {
			builder.field(AccountView::getCustomerServiceId, userId);
		}
		//需要修改密码
		String extra_sql = "(if((select count(ur.id) from user_ticket_cleared_record ur where ur.account_id = a.id and deleted is true and source = 'self')>=3,true,false)) = 1";
		builder.put("extra_sql", extra_sql);
		Number expireAll = beanSearcher.searchCount(AccountView.class, builder.build());
		//存在过期
		extra_sql = "(select count(ur.id) from user_ticket_cleared_record ur where ur.account_id = a.id and deleted is true and source = 'self') > 0";
		builder.put("extra_sql", extra_sql);
		Number existsExpired = beanSearcher.searchCount(AccountView.class, builder.build());

		CustomerServiceUpdatePwdDetail updatePwdDetail = new CustomerServiceUpdatePwdDetail();
		updatePwdDetail.setNumOfExpiredAllAccount(expireAll.intValue());
		updatePwdDetail.setNumOfExpiredAccount(existsExpired.intValue());

		return GatewayResponse.SUCCESS.newBuilder().toResult(updatePwdDetail);
	}

	/**
	 * 批量导入需要美区appid并发车
	 */
	@PostMapping("/import/america/appid")
	public Result<String> importAmericaAppid(@RequestParam(required = false, defaultValue = "31") Long goodsId, @RequestParam(required = false, defaultValue = "214") Long skuId, MultipartFile file) throws IOException {
		List<Object> accountList = EasyExcel.read(file.getInputStream()).sheet().headRowNumber(0).doReadSync();
		accountService.analysisAmericaAppidAccount(goodsId, skuId, accountList);
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}

	/**
	 * 等待双重认证gpt账号列表
	 */
	@GetMapping("/get/doubleVerifyAccount")
	public Result<SearchResult<DoubleVerifyAccountView>> getDoubleVerifyAccount() {
		SearchResult<DoubleVerifyAccountView> search = beanSearcher.search(DoubleVerifyAccountView.class, MapUtils.flatBuilder(request.getParameterMap()).build());
		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
	}

	/**
	 * 编辑双重认证账号
	 */
	@PutMapping("/put/doubleVerifyAccount")
	public Result<String> updateDoubleVerifyAccount(@RequestBody Account account) throws Exception {
		Long id = account.getId();
		Account db = accountService.getById(id);
		String dnPwd = db.getPassword();
		String newPwd = account.getPassword();

		String apiSecret = account.getApiSecret();
		boolean isFlag = false;
		if (StrUtil.isNotBlank(apiSecret)) {
			db.setApiSecret(apiSecret);
			isFlag = true;
			doubleVerifyRecordMapper.update(null, Wrappers.lambdaUpdate(AccountDoubleVerifyRecord.class)
					.set(AccountDoubleVerifyRecord::getDeleted, false)
					.set(AccountDoubleVerifyRecord::getApiSecret, apiSecret)
					.eq(AccountDoubleVerifyRecord::getAccountId, id));
		}
		if (StrUtil.isNotBlank(newPwd)) {
			newPwd = newPwd.trim();
			db.setPassword(newPwd);
			isFlag = true;
		}
		if (isFlag) {
			boolean b = accountService.updateById(db);
			if (StrUtil.isNotBlank(newPwd) && !dnPwd.equals(newPwd) && b) {
				account.setAccount(db.getAccount());
				templateCommonService.sendAccountTemplateMsg(account);
			}
		}
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}

	/**
	 * 导出双重验证账号列表
	 */
	@GetMapping("/doubleVerify/export")
	public void exportDoubleVerifyAccount(HttpServletResponse response) {
		List<ExcelDoubleVerifyAccountData> dataList = beanSearcher.searchAll(ExcelDoubleVerifyAccountData.class, MapUtils.flatBuilder(request.getParameterMap()).build());
		EasyExcelUtils.createExcelStreamMutilByEasyExcel(response, ExcelDoubleVerifyAccountData.class, dataList, "账号待开启双重验证列表", "账号待开启双重验证列表", ExcelTypeEnum.XLS, null);
	}


	/**
	 * 批量导入账号双重账号验证码
	 */
	@PostMapping("/doubleVerify/import")
	public Result<String> importDoubleVerifyCode(MultipartFile file) throws Exception {
		EasyExcel.read(file.getInputStream(), ExcelDoubleVerifyAccountData.class, new AnalysisEventListener<ExcelDoubleVerifyAccountData>() {
			@Override
			public void invoke(ExcelDoubleVerifyAccountData data, AnalysisContext analysisContext) {
				String account = data.getAccount();
				String apiSecret = data.getApiSecret();
				if (StrUtil.isBlank(account) || StrUtil.isBlank(apiSecret)) {
					return;
				}
				Account ac = accountService.getOne(Wrappers.lambdaQuery(Account.class)
						.in(Account::getGoodsId, List.of(18, 26))
						.eq(Account::getAccount, account.trim())
						.eq(Account::getApiSecret, "")
						.last("limit 1"));
				if (ac == null) return;
				ac.setApiSecret(apiSecret.trim());
				accountService.updateById(ac);

				doubleVerifyRecordMapper.update(null, Wrappers.lambdaUpdate(AccountDoubleVerifyRecord.class)
						.set(AccountDoubleVerifyRecord::getDeleted, false)
						.set(AccountDoubleVerifyRecord::getApiSecret, apiSecret.trim())
						.eq(AccountDoubleVerifyRecord::getAccountId, ac.getId())
						.eq(AccountDoubleVerifyRecord::getDeleted, true));
			}

			@Override
			public void doAfterAllAnalysed(AnalysisContext analysisContext) {

			}
		}).sheet().doRead();
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}

	/**
	 * 双重认证导入模板
	 */
	@GetMapping("/export/doubleVerify/template")
	public void exportDoubleVerifyAccountTemplate(HttpServletResponse response) {
		EasyExcelUtils.createTemplateExcel(response, ExcelImportDoubleVerifyAccountData.class, "账号双重认证码导入模板", "账号双重认证码导入模板", ExcelTypeEnum.XLS, null);
	}

	/**
	 * 查看账号双重认证验证码列表记录
	 */
	@GetMapping("/get/doubleVerify/viewList")
	public Result<SearchResult<AccountDoubleVerifyUserListView>> getAccountDoubleVerifyUserListView() {
		SearchResult<AccountDoubleVerifyUserListView> search = beanSearcher.search(AccountDoubleVerifyUserListView.class, MapUtils.flatBuilder(request.getParameterMap()).build());
		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
	}

	/**
	 * 查看账号双重认证验证码列表刷新记录
	 */
	@GetMapping("/get/doubleVerify/clickRefresh/list")
	public Result<List<DoubleVerifyClickRefreshRecord>> getDoubleVerifyClickRefreshRecord() {
		List<DoubleVerifyClickRefreshRecord> search = beanSearcher.searchAll(DoubleVerifyClickRefreshRecord.class, MapUtils.flatBuilder(request.getParameterMap()).build());
		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
	}

	/**
	 * 用户车看车票账号密码 记录列表
	 */
	@GetMapping("/get/ticket/pwd/view")
	public Result<SearchResult<TicketPwdViewRecordView>> getTicketPwdView() {
		SearchResult<TicketPwdViewRecordView> search = beanSearcher.search(TicketPwdViewRecordView.class, MapUtils.flatBuilder(request.getParameterMap())
				.orderBy(TicketPwdViewRecordView::getId).desc()
				.build());
		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
	}

	/**
	 * MidJourney、ChatGPT年付 ChatGpt独立月付未续费列表
	 */
	@GetMapping("/get/specific/account")
	public Result<SearchResult<AccountSpecificView>> getSpecificAccount() {
		//获取MidJourney、ChatGPT年付 ChatGpt独立月付 规格id
		Long chatGptPlusGid = Constant.AI_goodsIds.get(0);
		//年付
		List<Long> yearSkuIds = goodsDonSkuMapper.getSpecificSkuIdsByGoodsIds(Constant.AI_goodsIds, 12);
		//ChatGpt Plus 独立月付
		List<Long> monthSkuIds = goodsDonSkuMapper.getSpecificSkuIdsByGoodsId(chatGptPlusGid, 1, 1);
		Collection<Long> skuIds = CollUtil.addAll(yearSkuIds, monthSkuIds);
		if (skuIds.isEmpty()) {
			return GatewayResponse.SUCCESS.newBuilder().toResult();
		}
		SearchResult<AccountSpecificView> search = beanSearcher.search(AccountSpecificView.class, MapUtils.flatBuilder(request.getParameterMap())
				.put("condition", String.format("and (select count(*) from groups_relation gr where gr.groups_id = gt.id and gr.user_id != 0 and gr.status in ('validity','outside')) > 0"))
				.field(AccountSpecificView::getExpiryTime, DateTime.now()).op(Operator.LessThan)
				.field(AccountSpecificView::getSkuId, skuIds).op(Operator.InList).build());
		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
	}

	/**
	 * 编辑 未续费的账号
	 */
	@PutMapping("/update/renew/account")
	public Result<String> updateRenewAccount(@RequestBody Account account) {
		Long id = account.getId();
		Date startTime = account.getStartTime();
		Date expiryTime = account.getExpiryTime();
		String remark = account.getRemark();
		Account byId = accountService.getById(id);
		Assert.notNull(byId, "账号不存在");
		byId.setStartTime(startTime);
		byId.setExpiryTime(expiryTime);
		byId.setRemark(remark);
		accountService.updateById(byId);
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}

	/**
	 * 下载 替换错误账号模板
	 */
	@GetMapping("/get/replace/banned/template")
	public void getReplaceBannedTemplate(HttpServletResponse response) {
		EasyExcelUtils.createTemplateExcel(response, ExcelBannedAccountData.class, "封禁账号替换模板", "封禁账号替换模板", ExcelTypeEnum.XLSX, null);
	}

	/**
	 * 替换封禁账号
	 */
	@PostMapping("/replace/banned")
	public Result<List<String>> replaceBannedAccount(Long goodsId, MultipartFile file) throws IOException {
		List<String> repeat = new ArrayList<>();
		List<Long> sendMsgList = new ArrayList<>();
		EasyExcel.read(file.getInputStream(), ExcelBannedAccountData.class, new AnalysisEventListener<ExcelBannedAccountData>() {
			@Override
			public void invoke(ExcelBannedAccountData data, AnalysisContext analysisContext) {
				String oldAccount = data.getOldAccount();
				String account = data.getAccount();
				String password = data.getPassword();
				if (StrUtil.isEmpty(oldAccount) || StrUtil.isEmpty(account) || StrUtil.isEmpty(password)) {
					return;
				}
				Account dbAccount = accountService.getOne(Wrappers.lambdaQuery(Account.class)
						.eq(Account::getGoodsId, goodsId)
						.eq(Account::getAccount, oldAccount.trim())
						.last("limit 1"));
				if (dbAccount == null) {
					return;
				}
				account = account.trim();
				password = password.trim();
				if (accountCommonService.checkIsDupAccount(goodsId, account)) {
					repeat.add(oldAccount);
					return;
				}
				dbAccount.setAccount(account);
				dbAccount.setPassword(password);
				dbAccount.setStartTime(DateTime.now());
				dbAccount.setExpiryTime(DateUtil.offsetMonth(DateTime.now(), 1));
				if (StrUtil.isNotEmpty(data.getEmail())) {
					dbAccount.setEmail(data.getEmail());
				}
				if (StrUtil.isNotEmpty(data.getRelateEmail())) {
					dbAccount.setRelateEmail(data.getRelateEmail());
				}
				if (data.getExpiryTime() != null) {
					dbAccount.setExpiryTime(DateUtil.parse(data.getExpiryTime()));
				}
				if (StrUtil.isNotBlank(data.getRemark())) {
					dbAccount.setRemark(data.getRemark());
				}
				if (StrUtil.isNotEmpty(data.getBankCard())) {
					dbAccount.setBankCard(data.getBankCard());
				}
				dbAccount.setGptEmailPwd(data.getGptEmailPwd());
				accountService.updateById(dbAccount);

				GroupsTrips groupsTrips = groupsMapper.selectOne(Wrappers.lambdaQuery(GroupsTrips.class)
						.eq(GroupsTrips::getAccountId, dbAccount.getId())
						.eq(GroupsTrips::getStatus, GroupsTrips.Status.down)
						.last("limit 1"));
				if (groupsTrips != null) {
					groupsTrips.setStatus(GroupsTrips.Status.validity);
					groupsMapper.updateById(groupsTrips);
				}
				sendMsgList.add(dbAccount.getId());
			}

			@Override
			public void doAfterAllAnalysed(AnalysisContext analysisContext) {

			}
		}).sheet().doRead();

		if (CollUtil.isNotEmpty(sendMsgList)) {
			TASK_EXECUTOR.execute(() -> {
				sendMsgList.forEach(accountId -> {
					GroupsTrips groupsTrips = groupsMapper.selectOne(Wrappers.lambdaQuery(GroupsTrips.class)
							.eq(GroupsTrips::getAccountId, accountId).last("limit 1"));
					if (groupsTrips != null) {
						Account byId = accountService.getById(accountId);
						cmsGroupService.sendChangeAccountMsg(groupsTrips.getId(), goodsId, byId.getAccount());
					}
				});
			});
		}
		return GatewayResponse.SUCCESS.newBuilder().toResult(repeat);
	}

	/**
	 * 导入备用账号模板
	 */
	@GetMapping("/export/prepare/template")
	public void getPrepareTemplate(HttpServletResponse response) {
		EasyExcelUtils.createTemplateExcel(response, ExcelPrepareAccountData.class, "导入备用账号模板", "导入备用账号模板", ExcelTypeEnum.XLSX, null);
	}

	/**
	 * 批量导入备用账号
	 */
	@PostMapping("/batch/import/prepare")
	public Result<String> batchImportPrepare(Long goodsId, String preSkuIds, MultipartFile file) throws IOException {
		if (goodsId == null) {
			throw BusinessRuntimeException.getInstance("请选择对应平台");
		}
		if (goodsId == 26) {
			if (StrUtil.isEmpty(preSkuIds)) {
				throw BusinessRuntimeException.getInstance("请选择对应规格id");
			}
			String[] split = preSkuIds.split(",");
			preSkuIds = Arrays.stream(split).distinct().collect(Collectors.joining(","));
		}
		final String f_skuIds = preSkuIds;
		EasyExcel.read(file.getInputStream(), ExcelPrepareAccountData.class, new AnalysisEventListener<ExcelPrepareAccountData>() {
			@Override
			public void invoke(ExcelPrepareAccountData data, AnalysisContext analysisContext) {
				String account = data.getAccount();
				String password = data.getPassword();
				if (StrUtil.isEmpty(account) || StrUtil.isEmpty(password)) {
					return;
				}
				Account insert = new Account();
				insert.setAccount(account.trim());
				insert.setPassword(password.trim());
				insert.setGoodsId(goodsId);
				insert.setPreSkuIds(f_skuIds);
				insert.setType(2);
				insert.setStartTime(DateTime.now());
				if (data.getExpiryTime() != null) {
					insert.setExpiryTime(DateUtil.parse(data.getExpiryTime()));
				} else {
					insert.setExpiryTime(DateUtil.offsetMonth(DateTime.now(), 1));
				}
				//邮箱密码
				insert.setGptEmailPwd(data.getGptEmailPwd());
				insert.setBankCard(data.getBankCard());
				//辅助邮箱
				insert.setEmail(data.getSecondEmail());
				//关联邮箱
				insert.setRelateEmail(data.getRelateEmail());
				//验证链接
				insert.setVerifyLink(data.getVerifyLink());
				insert.setRemark(data.getRemark());
				if (StrUtil.isNotEmpty(data.getOutsidePurchaseStr())) {
					if (data.getOutsidePurchaseStr().equals("是") || data.getOutsidePurchaseStr().equals("1")) {
						insert.setOutsidePurchase(true);
					}
				}
				//账号是否已经添加过
				if (!insert.getAccount().contains("客服")) {
					if (accountCommonService.checkIsDupAccount(insert.getGoodsId(), insert.getAccount())) {
						return;
					}
				}
				accountService.save(insert);
			}

			@Override
			public void doAfterAllAnalysed(AnalysisContext analysisContext) {

			}
		}).sheet().doRead();
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}

	/**
	 * 新增预备账号
	 */
	@PostMapping("/post/prepare")
	public Result<String> postPrepare(@RequestBody Account account) {
		account.setAccount(account.getAccount().trim());
		account.setPassword(account.getPassword().trim());
		//账号是否已经添加过
		if (!account.getAccount().contains("客服")) {
			if (accountCommonService.checkIsDupAccount(account.getGoodsId(), account.getAccount())) {
				throw BusinessRuntimeException.getInstance("该平台下已生成该账号");
			}
		}
		account.setType(2);
		if (account.getGoodsId() == 26) {
			String preSkuIds = account.getPreSkuIds();
			if (StrUtil.isEmpty(preSkuIds)) {
				throw BusinessRuntimeException.getInstance("请选择对应规格id");
			}
			String[] split = preSkuIds.split(",");
			String skuSetStr = Arrays.stream(split).distinct().collect(Collectors.joining(","));
			account.setPreSkuIds(skuSetStr);
		}
		accountService.save(account);
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}

	/**
	 * 批量导入系统邮箱
	 */
	@PostMapping("/batch/import/email")
	public Result<String> batchImportEmail(MultipartFile file) throws Exception {
		accountService.readFileAndImportEmail(file);
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}

	/**
	 * 系统邮箱列表
	 */
	@GetMapping("/get/email")
	public Result<SearchResult<SysEmail>> getEmail() {
		SearchResult<SysEmail> search = beanSearcher.search(SysEmail.class, MapUtils.flatBuilder(request.getParameterMap())
				.field(SysEmail::getDeleted, 1)
				.build());
		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
	}

	/**
	 * 新增邮箱
	 */
	@PostMapping("/post/email")
	public Result postEmail(@RequestBody @Validated SysEmail sysEmail) {
		try {
			sysEmailMapper.insert(sysEmail);
		} catch (DuplicateKeyException e) {
		}
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}

	/**
	 * 修改系统邮箱
	 */
	@PutMapping("/update/email")
	public Result updateEmail(@RequestBody SysEmail sysEmail) {
		sysEmailMapper.updateById(sysEmail);
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}

	/**
	 * 删除系统邮箱
	 */
	@DeleteMapping("/delete/email/{id}")
	public Result deleteEmail(@PathVariable Long id) {
		sysEmailMapper.deleteById(id);
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}

	/**
	 * 批量导入被封禁平台账号
	 */
	@PostMapping("/batch/import/blocked")
	public Result<String> batchImportBlockedAccount(MultipartFile file, Long goodsId) throws IOException {
		Assert.notNull(goodsId, "请选择对应平台");
		List<Object> accountBlockedList = EasyExcel.read(file.getInputStream()).sheet().headRowNumber(0).doReadSync();
		accountService.batchImportBlockedAccount(goodsId, accountBlockedList);
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}

	/**
	 * 删除封禁平台账号
	 */
	@DeleteMapping("/delete/blocked/{id}")
	public Result<String> deleteBlockedAccount(@PathVariable Long id) throws IOException {
		accountBlockedReplaceRecordMapper.deleteById(id);
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}

	/**
	 * 被封禁账号列表
	 */
	@GetMapping("/get/blocked")
	public Result<SearchResult<AccountBlockedReplaceRecord>> getBlockedAccount() {
		SearchResult<AccountBlockedReplaceRecord> search = beanSearcher.search(AccountBlockedReplaceRecord.class, MapUtils.flatBuilder(request.getParameterMap())
				.orderBy(AccountBlockedReplaceRecord::getId).desc()
				.build());
		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
	}

	/**
	 * 禁用账号
	 */
	@PutMapping("/update/disable/{accountId}")
	@Log(module = "平台管理/禁用账号", businessType = BusinessType.PUT, isSaveRequestData = true)
	public Result<String> updateDisableAccount(@PathVariable Long accountId) {
		Optional.ofNullable(accountService.getById(accountId))
				.ifPresent(e -> {
					e.setIsDisable(!e.getIsDisable());
					accountService.updateById(e);
				});
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}

	/**
	 * 账号禁用列表
	 */
	@GetMapping("/get/disable/acount")
	public Result<SearchResult<AccountDisableView>> getDisableAccount() {
		SearchResult<AccountDisableView> search = beanSearcher.search(AccountDisableView.class, MapUtils.flatBuilder(request.getParameterMap())
				.field(AccountDisableView::getIsDisable, 1).build());
		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
	}


	/**
	 * 奈飞同步用户更新装置
	 */
	@PostMapping("/nf/upgrade/device/{accountId}")
	public Result<String> nfUpgradeDevice(@PathVariable Long accountId) throws Exception {
		String url = accountService.nfUpgradeDevice(accountId);
		return GatewayResponse.SUCCESS.newBuilder().toResult(url);
	}

	/**
	 * 查看验证码
	 */
	@GetMapping("/get/account/email/code")
	public Result<String> getAccountEmailCode(Long accountId) throws Exception {
		String code = accountService.getAccountEmailCode(accountId);
		return GatewayResponse.SUCCESS.newBuilder().toResult(code);
	}

	/**
	 * 下载导入账号 辅助邮箱、关联邮箱模板
	 */
	@GetMapping("/get/export/email/template")
	public void getAccountEmailTemplate(HttpServletResponse response) {
		EasyExcelUtils.createTemplateExcel(response, ExcelAccountEmailBindData.class, "导入账号邮箱模板", "导入账号邮箱模板", ExcelTypeEnum.XLSX, null);
	}

	/**
	 * 导入账号 辅助邮箱、关联邮箱
	 */
	@PostMapping("/export/email")
	public Result<String> exportEmail(MultipartFile file) throws IOException {
		EasyExcel.read(file.getInputStream(), ExcelAccountEmailBindData.class, new AnalysisEventListener<ExcelAccountEmailBindData>() {
			@Override
			public void invoke(ExcelAccountEmailBindData data, AnalysisContext analysisContext) {
				String account = data.getAccount();
				String email = data.getEmail();
				String relateEmail = data.getRelateEmail();
				if (StrUtil.isAllEmpty(email, relateEmail)) {
					return;
				}
				List<Account> list = accountService.list(Wrappers.lambdaQuery(Account.class)
						.eq(Account::getAccount, account.trim()));
				if (list.isEmpty()) return;

				list.forEach(dbAccount->{
					dbAccount.setEmail(email);
					dbAccount.setRelateEmail(relateEmail);
					accountService.updateById(dbAccount);
				});
			}

			@Override
			public void doAfterAllAnalysed(AnalysisContext analysisContext) {

			}
		}).sheet().doRead();
		return GatewayResponse.SUCCESS.newBuilder().toResult();
	}
}
