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

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 com.alibaba.excel.support.ExcelTypeEnum;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cyksj.common.annotation.Log;
import com.cyksj.common.constant.Constant;
import com.cyksj.common.exception.BusinessRuntimeException;
import com.cyksj.dto.Result;
import com.cyksj.enums.BusinessType;
import com.cyksj.enums.GatewayResponse;
import com.cyksj.mapper.*;
import com.cyksj.mapper.corp.CorpUserAuthMapper;
import com.cyksj.mapper.manage.CustomerServiceEveryMonthDetailMapper;
import com.cyksj.mapper.manage.RealGoodsOrderHandleMapper;
import com.cyksj.mapper.manage.cms.CmsUserMapper;
import com.cyksj.mapper.manage.statistics.IncrExpiryTimeRecordMapper;
import com.cyksj.mapper.manage.statistics.StatisticsUserDataMapper;
import com.cyksj.mapper.stock.GoodsDonStockRecordMapper;
import com.cyksj.model.entity.*;
import com.cyksj.model.excel.ExcelAccountData;
import com.cyksj.model.excel.ExcelServiceMonthStatisticsData;
import com.cyksj.model.excel.ExcelSheetAndData;
import com.cyksj.model.request.BatchServiceReq;
import com.cyksj.model.request.RedBookReq;
import com.cyksj.model.request.RenewTimeUpReq;
import com.cyksj.model.views.*;
import com.cyksj.service.mange.statistics.StatisticsDataService;
import com.cyksj.service.relation.GroupRelationClearService;
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 org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/*
 *项目名: netflix
 *文件名: StatisticsDataController
 *创建者: JavaZou
 *创建时间:2022/10/17 13:34
 * 数据统计控制器
 */
@RestController
@RequiredArgsConstructor
@RequestMapping("/manager/statistics")
public class StatisticsDataController {

	private final StatisticsDataService statisticsDataService;

	private final GroupsRelationMapper groupsRelationMapper;

	private final IncrExpiryTimeRecordMapper incrExpiryTimeRecordMapper;

	private final BeanSearcher beanSearcher;

	private final UserMapper userMapper;

	private final StatisticsUserDataMapper statisticsUserDataMapper;

	private final CustomerServiceEveryMonthDetailMapper detailMapper;

	private final RealGoodsOrderHandleMapper realGoodsOrderHandleMapper;

	private final GoodsDonStockRecordMapper stockRecordMapper;

	private final CustomerServiceRenewOrderRecordMapper customerServiceRenewOrderRecordMapper;

	private final CorpUserAuthMapper corpUserAuthMapper;

	private final OrderDonMapper orderDonMapper;

	private final MidjourneyUserMapper midjourneyUserMapper;

	private final ChatgptUserMapper chatgptUserMapper;

	private final CmsUserMapper cmsUserMapper;

	private final GroupRelationClearService clearService;

	private final HttpServletRequest request;

	/**
	 * 查询头部统计数据
	 */
	@GetMapping("/get/header")
	public Result<BackgroundDataView> getStatisticsHeader(Long startTime, Long endTime) throws InterruptedException {
		return statisticsDataService.getStatisticsHeader(startTime, endTime);
	}

	/**
	 * 查询平台统计数据
	 * type 1、流媒体，2、实物，3、潮玩，4、注册账号
	 */
	@GetMapping("/get/goods")
	@SaCheckPermission("statics:view")
	public Result<BackgroundDataView> getStatisticsByCondition(@RequestParam(defaultValue = "0") Integer type, Long startTime, Long endTime) throws InterruptedException {
		return statisticsDataService.getStatisticsByCondition(type, startTime, endTime);
	}

	/**
	 * 导出订单统计数据
	 */
	@GetMapping("/export")
	@SaCheckPermission("statics:export")
	public void exportStatisticsData(Integer type, Long startTime, Long endTime, HttpServletResponse response) {
		if (startTime == null || endTime == null) {
			throw BusinessRuntimeException.getInstance("请选择导出的时间");
		}
		List<ExcelSheetAndData> excelSheetDataList = statisticsDataService.exportStatisticsDataByType(type, startTime, endTime);
		excelSheetDataList = excelSheetDataList.stream().sorted(Comparator.comparing(ExcelSheetAndData::getSort)).collect(Collectors.toList());
		EasyExcelUtils.createExcelStreamMutilByEasyExcel(response, excelSheetDataList, "订单统计数据", ExcelTypeEnum.XLSX, null);
	}

	/**
	 * 每月业务数据导出
	 */
	@GetMapping("/export/monthly/data")
	public void exportMonthlyData(Long startTime, Long endTime, HttpServletResponse response) {
		if (startTime == null || endTime == null) {
			throw BusinessRuntimeException.getInstance("请选择导出的时间");
		}
		List<ExcelSheetAndData> excelSheetDataList = statisticsDataService.exportMonthlyData(startTime, endTime);
		excelSheetDataList = excelSheetDataList.stream().sorted(Comparator.comparing(ExcelSheetAndData::getSort)).collect(Collectors.toList());
		EasyExcelUtils.createExcelStreamMutilByEasyExcel(response, excelSheetDataList, String.format("%s月业务数据", DateUtil.month(DateTime.of(startTime)) + 1), ExcelTypeEnum.XLSX, null);
	}

	/**
	 * 综合数据统计
	 * type 1销售数据 2自然渠道分类数据 3 渠道商+个人 分类数据 4 续费/非续费数据
	 */
	@GetMapping("/get/groupsData")
	public Result<GroupsDataStatisticsView> getGroupsData(Long startTime, Long endTime, @RequestParam(defaultValue = "0") Integer type, Boolean isGeneral) throws Exception {
		GroupsDataStatisticsView groupsDataStatisticsView = statisticsDataService.getGroupsData(startTime, endTime, type, isGeneral);
		return GatewayResponse.SUCCESS.newBuilder().toResult(groupsDataStatisticsView);
	}

	/**
	 * 有效子账号数列表
	 */
	@GetMapping("/expired/account")
	@SaCheckPermission("validAccount:view")
	public Result<IPage<ExpiredAccountDataView>> getExpiredAccountView(Boolean isCorpWx, String customerService, Long goodsId, Long skuId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, Long startTime, Long endTime, @RequestParam(required = false, defaultValue = "1") Long start, @RequestParam(required = false, defaultValue = "10") Long limit) {
		return GatewayResponse.SUCCESS.newBuilder().toResult(statisticsDataService.getExpiredAccountView(isCorpWx, customerService, goodsId, skuId, account, renewStatus, handleStatus, cnAccount, usAccount, userId, startTime, endTime, start, limit));
	}

	/**
	 * 导出有效子账号数
	 */
	@GetMapping("/export/account")
	public void exportAccount(Boolean isCorpWx, String customerService, Long goodsId, Long skuId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, Long startTime, Long endTime, HttpServletResponse response) {
		List<ExcelAccountData> expiredAccountView = statisticsDataService.getExpiredAccountViewNoPage(isCorpWx, customerService, goodsId, skuId, account, renewStatus, handleStatus, cnAccount, usAccount, userId, startTime, endTime);
		EasyExcelUtils.createExcelStreamMutilByEasyExcel(response, ExcelAccountData.class, expiredAccountView, "有效子账号", "有效子账号", ExcelTypeEnum.XLSX, null);
	}

	/**
	 * 到期状态续费意愿修改
	 */
	@PutMapping("/put/renewStatus/{relationId}")
	public Result<String> updateRenewStatus(@PathVariable Long relationId) {
		if (relationId == null || relationId < 1) {
			throw BusinessRuntimeException.getInstance("用户关联组id不存在");
		}
		return statisticsDataService.updateRenewStatus(relationId);
	}

	/**
	 * 处理状态修改
	 */
	@PutMapping("/put/handleStatus/{relationId}")
	public Result<String> updateHandleStatus(@PathVariable Long relationId) {
		if (relationId == null || relationId < 1) {
			throw BusinessRuntimeException.getInstance("用户关联组id不存在");
		}
		return statisticsDataService.updateHandleStatus(relationId);
	}


	/**
	 * 增加续费时长
	 */
	@PutMapping("/renewTime/up")
	@Transactional(rollbackFor = Throwable.class)
	@SaCheckPermission("validAccount:addTime")
	@Log(module = "有效子账号管理/增加时长", businessType = BusinessType.PUT, isSaveRequestData = true)
	public Result<String> addRenewTime(@RequestBody @Validated RenewTimeUpReq renewTimeUpReq) {
		long adminId = StpUtil.getLoginIdAsLong();
		GroupsRelation relation = groupsRelationMapper.selectById(renewTimeUpReq.getRelationId());
		if (relation == null) {
			throw BusinessRuntimeException.getInstance("该车票已不存在");
		}
		if (relation.getStatus() != GroupsRelation.Status.validity && relation.getStatus() != GroupsRelation.Status.outside) {
			throw BusinessRuntimeException.getInstance("该车票无效");
		}
		if (relation.getExpiryTime() == null) {
			throw BusinessRuntimeException.getInstance("车票已过期");
		}
		IncrExpiryTimeRecord incrExpiryTimeRecord = new IncrExpiryTimeRecord();
		incrExpiryTimeRecord.setExpiryTimeInitial(relation.getExpiryTime());
		incrExpiryTimeRecord.setIncrExpiryDays(renewTimeUpReq.getDays());
		incrExpiryTimeRecord.setRemark(renewTimeUpReq.getRemark());
		relation.setExpiryTime(DateUtil.offsetDay(relation.getExpiryTime(), renewTimeUpReq.getDays()));
		if (relation.getReservedDays() != null && relation.getReservedDays() > 0) {
			relation.setReservedDays(relation.getReservedDays() + incrExpiryTimeRecord.getIncrExpiryDays());
		}
		groupsRelationMapper.updateById(relation);

		//扣除镜像过期时间
		ChatgptUser chatgptUser = chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class)
				.eq(ChatgptUser::getRelationId, relation.getId())
				.last("limit 1"));
		if (chatgptUser != null) {
			chatgptUser.setExpireTime(DateUtil.offsetDay(chatgptUser.getExpireTime(), renewTimeUpReq.getDays()));
			chatgptUserMapper.updateById(chatgptUser);
		} else {
			MidjourneyUser midjourneyUser = midjourneyUserMapper.selectOne(Wrappers.lambdaQuery(MidjourneyUser.class)
					.eq(MidjourneyUser::getRelationId, relation.getId())
					.last("limit 1"));
			if (midjourneyUser != null) {
				midjourneyUser.setExpireTime(DateUtil.offsetDay(midjourneyUser.getExpireTime(), renewTimeUpReq.getDays()));
				midjourneyUserMapper.updateById(midjourneyUser);
			}
		}

		//记录操作日志
		incrExpiryTimeRecord.setUserId(relation.getUserId());
		incrExpiryTimeRecord.setRelationId(relation.getId());
		Optional.ofNullable(cmsUserMapper.selectById(adminId)).ifPresent(admin -> incrExpiryTimeRecord.setOperator(admin.getNickname()));
		incrExpiryTimeRecordMapper.insert(incrExpiryTimeRecord);

		//清除车票
		if (relation.getExpiryTime().before(DateTime.now())) {
			clearService.clearTicket(relation, UserTicketClearedRecord.Source.manual);
		}
		return GatewayResponse.SUCCESS.newBuilder().toResult("增加车票有效期成功");
	}

	/**
	 * 增加时长操作记录
	 */
	@GetMapping("/get/realtion/time/record")
	public Result<SearchResult<IncrExpiryTimeRecord>> getRelationTimeRecord() {
		SearchResult<IncrExpiryTimeRecord> search = beanSearcher.search(IncrExpiryTimeRecord.class, MapUtils.flatBuilder(request.getParameterMap()).build());
		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
	}

	/**
	 * 增加续费操作记录
	 */
	@GetMapping("/get/operation/record")
	public Result<SearchResult<IncrExpiryTimeRecord>> getOpRecord(HttpServletRequest request) {
		return GatewayResponse.SUCCESS.newBuilder().toResult(beanSearcher.search(IncrExpiryTimeRecord.class, MapUtils.flatBuilder(request.getParameterMap()).build()));
	}

	/**
	 * 总用户数列表
	 */
	@GetMapping("/get/user/data")
	public Result<StatisticsUserDataView> getUserData(HttpServletRequest request) {
		StatisticsUserDataView statisticsUserDataView = new StatisticsUserDataView();
		DateTime now = DateTime.now();
		DateTime thisZero = DateUtil.beginOfDay(now);
		DateTime thisEnd = DateUtil.endOfDay(now);
		Integer total = userMapper.selectCount(null);
		Integer pcUserNum = statisticsUserDataMapper.getPcTotalOfUser();
		statisticsUserDataView.setNumOfUser(total - pcUserNum);
		statisticsUserDataView.setPcNumOfUser(pcUserNum);
		statisticsUserDataView.setNumOfNewUser(statisticsUserDataMapper.getNumOfNewUser(thisZero, thisEnd));
		statisticsUserDataView.setPcNumOfNewUser(statisticsUserDataMapper.getPcNumOfNewUser(thisZero, thisEnd));
		statisticsUserDataView.setNumOfActiveUser(statisticsUserDataMapper.getNumOfActiveUser(thisZero, thisEnd));
		statisticsUserDataView.setPcNumOfActiveUser(statisticsUserDataMapper.getPcNumOfActiveUser(thisZero, thisEnd));

		statisticsUserDataView.setNumOfDistributeUser(statisticsUserDataMapper.getNumOfDistributeUserToday(thisZero, thisEnd));

		List<CorpUserAuth> corpUserAuths = corpUserAuthMapper.selectList(Wrappers.lambdaQuery(CorpUserAuth.class)
				.between(CorpUserAuth::getCreatedTime, thisZero, thisEnd).select(CorpUserAuth::getExternalUserid).groupBy(CorpUserAuth::getExternalUserid));
		Integer numOfCorpUser = corpUserAuths.isEmpty() ? 0 : corpUserAuths.size();
		statisticsUserDataView.setNumOfCorpUser(numOfCorpUser);
		MapBuilder builder = MapUtils.builder()
				.put("time", String.format("cfr.created_time between '%s' and '%s'", thisZero, thisEnd))
				.group("cfr.corp_user_id");
		Number equipments = beanSearcher.searchCount(CorpUserTagView.class, builder.put("tagName", "ct.tag_name = '设备'").build());
		statisticsUserDataView.setNumOfCorpEquipmentUser(equipments.intValue());
		Number accounts = beanSearcher.searchCount(CorpUserTagView.class, builder.put("tagName", "ct.tag_name = '账号'").build());
		statisticsUserDataView.setNumOfCorpAccountUser(accounts.intValue());

		statisticsUserDataView.setUserData(beanSearcher.search(StatisticsUserData.class, MapUtils.flatBuilder(request.getParameterMap()).build()));
		Number redBookUsers = beanSearcher.searchSum(StatisticsUserData.class, MapUtils.flatBuilder(request.getParameterMap()).build(), "numOfRedBookUser");
		Integer numOfRedBookUser = redBookUsers.intValue();
		statisticsUserDataView.setNumOfRedBookUser(numOfRedBookUser);
		return GatewayResponse.SUCCESS.newBuilder().toResult(statisticsUserDataView);
	}

	/**
	 * 客服分销数据统计
	 */
	@GetMapping("/get/customer/service/data")
	public Result<StatisticsCustomerDataView> getCustomerData(Integer type, HttpServletRequest request) throws Exception {
		DateTime now = DateTime.now();
		DateTime begin = DateUtil.beginOfMonth(now);
		String statisticsDate = DateUtil.format(now, "yyyy-MM");
		StatisticsCustomerDataView customerDataView = new StatisticsCustomerDataView();
		StatisticsCustomerServiceEveryMonthView toMonth = new StatisticsCustomerServiceEveryMonthView();
		toMonth.setStatisticsData(statisticsDate);
		List<CustomerServiceHandleOrderToMonthView> toMonthDetail;
		if (type == 3) {
			toMonthDetail = customerServiceRenewOrderRecordMapper.selectCustomerToMonthData(now, begin, type);
		} else {
			toMonthDetail = realGoodsOrderHandleMapper.selectCustomerToMonthData(now, begin, Constant.noOrderAllStatus, type);
		}
		if (!toMonthDetail.isEmpty()) {
			toMonth.setDetails(toMonthDetail);
			customerDataView.setToMonth(toMonth);
		}
		SearchResult<StatisticsCustomerServiceEveryMonth> search = beanSearcher.search(StatisticsCustomerServiceEveryMonth.class, MapUtils.flatBuilder(request.getParameterMap()).build());
		search.getDataList().forEach(data -> {
			data.setDetails(detailMapper.getDetailData(type, data.getId()));
		});
		customerDataView.setMonthData(search);
		return GatewayResponse.SUCCESS.newBuilder().toResult(customerDataView);
	}

	/**
	 * 客服业绩订单明细
	 */
	@GetMapping("/get/service/orders/detail")
	public Result<CustomerServiceOrdersDetailView> getCustomerData(Integer type, String monthDate, String customerService, HttpServletRequest request) {
		DateTime month = null;
		if (StrUtil.isEmpty(monthDate)) {
			month = DateTime.now();
		} else {
			month = DateUtil.parse(monthDate, "yyyy-MM-dd");
		}
		DateTime beginOfMonth = DateUtil.beginOfMonth(month);
		DateTime endOfMonth = DateUtil.endOfMonth(month);
		CustomerServiceOrdersDetailView view = new CustomerServiceOrdersDetailView();
		if (type != 3) {
			MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap())
					.field(ServiceOrdersDetailView::getRecordCreatedTime, beginOfMonth, endOfMonth).op(Operator.Between);
			if (type == 1) {
				builder.field(ServiceOrdersDetailView::getBeforeServiceName, customerService).op(Operator.Equal);
			} else if (type == 2) {
				builder.field(ServiceOrdersDetailView::getAfterServiceName, customerService).op(Operator.Equal);
			}
			builder.field(ServiceOrdersDetailView::getStatus, Constant.noOrderAllStatus).op(Operator.NotIn);
			SearchResult<ServiceOrdersDetailView> search = beanSearcher.search(ServiceOrdersDetailView.class, builder.build());
			Number AppleTV = beanSearcher.searchCount(ServiceOrdersDetailView.class, builder.field(ServiceOrdersDetailView::getSpecVal, "Apple").op(Operator.Contain).build());
			Number Chromecast = beanSearcher.searchCount(ServiceOrdersDetailView.class, builder.field(ServiceOrdersDetailView::getSpecVal, "Chromecast").op(Operator.Contain).build());
			view.setAppleTV(AppleTV.intValue());
			view.setChromecast(Chromecast.intValue());
			view.setOrders(search.getTotalCount().intValue());
			view.setResult(search);
		} else {
			SearchResult<ServiceRenewOrdersDetailView> search = beanSearcher.search(ServiceRenewOrdersDetailView.class, MapUtils.flatBuilder(request.getParameterMap())
					.field(ServiceRenewOrdersDetailView::getCustomerService, customerService).op(Operator.Equal)
					.field(ServiceRenewOrdersDetailView::getRecordCreatedTime, beginOfMonth, endOfMonth).op(Operator.Between).build());
			view.setResult(search);
		}
		return GatewayResponse.SUCCESS.newBuilder().toResult(view);
	}

	/**
	 * 导出客服每月订单数据统计
	 */
	@GetMapping("/export/service/orders")
	public void exportServiceMonthStatisticsData(Integer type, String monthDate, String customerService, HttpServletRequest request, HttpServletResponse response) {
		DateTime month = null;
		if (StrUtil.isEmpty(monthDate)) {
			month = DateTime.now();
		} else {
			month = DateUtil.parse(monthDate, "yyyy-MM-dd");
		}
		DateTime beginOfMonth = DateUtil.beginOfMonth(month);
		DateTime endOfMonth = DateUtil.endOfMonth(month);
		MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap())
				.field(ExcelServiceMonthStatisticsData::getRecordCreatedTime, beginOfMonth, endOfMonth).op(Operator.Between);
		if (type == 1) {
			builder.field(ExcelServiceMonthStatisticsData::getBeforeServiceName, customerService).op(Operator.Equal);
		} else if (type == 2) {
			builder.field(ExcelServiceMonthStatisticsData::getAfterServiceName, customerService).op(Operator.Equal);
		}
		builder.field(ExcelServiceMonthStatisticsData::getStatus, Constant.noOrderAllStatus).op(Operator.NotIn);
		List<ExcelServiceMonthStatisticsData> excelServiceMonthStatisticsData = beanSearcher.searchAll(ExcelServiceMonthStatisticsData.class, builder.build());
		EasyExcelUtils.createExcelStreamMutilByEasyExcel(response, ExcelServiceMonthStatisticsData.class, excelServiceMonthStatisticsData, "客服每月订单数据统计", "客服每月订单数据统计", ExcelTypeEnum.XLSX, null);
	}

	@GetMapping("/get/stock")
	public Result<List<StatisticsStockView>> getStock(HttpServletRequest request, @RequestParam(defaultValue = "0") Long startTime, @RequestParam(defaultValue = "0") Long endTime) {
		List<GoodsDonStock> goodsDonStocks = beanSearcher.searchAll(GoodsDonStock.class, MapUtils.flatBuilder(request.getParameterMap()).build());
		List<StatisticsStockView> res = new ArrayList<>(goodsDonStocks.size() + 1);

		goodsDonStocks.forEach((stock) -> {
			List<GoodsDonStockRecord> goodsDonStockRecords = stockRecordMapper.selectList(Wrappers.lambdaQuery(GoodsDonStockRecord.class)
					.eq(GoodsDonStockRecord::getStockId, stock.getId())
					.eq(GoodsDonStockRecord::getIsUndo, false)
					.ge(startTime != 0, GoodsDonStockRecord::getCreatedTime, DateTime.of(startTime).toString())
					.le(endTime != 0, GoodsDonStockRecord::getCreatedTime, DateTime.of(endTime).toString())
			);
			Map<String, List<GoodsDonStockRecord>> typeRecordsMap = goodsDonStockRecords.stream().collect(Collectors.groupingBy(GoodsDonStockRecord::getType));
			StatisticsStockView statisticsStockView = new StatisticsStockView();
			List<GoodsDonStockRecord> put = typeRecordsMap.get("put");
			if (put != null) {
				int sum = put.stream().mapToInt(GoodsDonStockRecord::getAvailable).sum();
				BigDecimal price = put.stream().map((record) -> record.getPrice().multiply(BigDecimal.valueOf(record.getAvailable())))
						.reduce(BigDecimal.ZERO, BigDecimal::add);
				statisticsStockView.setThenTotal(sum);
				statisticsStockView.setThenPrice(price);
			}
//			statisticsStockView.setUnavailable(stock.getUnavailable());
			statisticsStockView.setTitle(stock.getTitle());
			typeRecordsMap.forEach((type, records) -> {
				int sum = records.stream().mapToInt(GoodsDonStockRecord::getNum).sum();
				BigDecimal price = records.stream().map((record) -> record.getPrice().multiply(BigDecimal.valueOf(record.getNum())))
						.reduce(BigDecimal.ZERO, BigDecimal::add);
				statisticsStockView.setTotalWithPrice(type, sum, price);
			});
			res.add(statisticsStockView);
		});

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

	@GetMapping("/export/stock")
	public void exportStock(HttpServletRequest request, HttpServletResponse response, @RequestParam(defaultValue = "0") Long startTime, @RequestParam(defaultValue = "0") Long endTime) {
		List<GoodsDonStock> goodsDonStocks = beanSearcher.searchAll(GoodsDonStock.class, MapUtils.flatBuilder(request.getParameterMap()).build());
		List<StatisticsStockView> res = new ArrayList<>(goodsDonStocks.size() + 1);

		goodsDonStocks.forEach((stock) -> {
			List<GoodsDonStockRecord> goodsDonStockRecords = stockRecordMapper.selectList(Wrappers.lambdaQuery(GoodsDonStockRecord.class)
					.eq(GoodsDonStockRecord::getStockId, stock.getId())
					.eq(GoodsDonStockRecord::getIsUndo, false)
					.ge(startTime != 0, GoodsDonStockRecord::getCreatedTime, DateTime.of(startTime).toString())
					.le(endTime != 0, GoodsDonStockRecord::getCreatedTime, DateTime.of(endTime).toString())
			);
			Map<String, List<GoodsDonStockRecord>> typeRecordsMap = goodsDonStockRecords.stream().collect(Collectors.groupingBy(GoodsDonStockRecord::getType));
			StatisticsStockView statisticsStockView = new StatisticsStockView();
			List<GoodsDonStockRecord> put = typeRecordsMap.get("put");
			if (put != null) {
				int sum = put.stream().mapToInt(GoodsDonStockRecord::getAvailable).sum();
				BigDecimal price = put.stream().map((record) -> record.getPrice().multiply(BigDecimal.valueOf(record.getAvailable())))
						.reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_EVEN);
				statisticsStockView.setThenTotal(sum);
				statisticsStockView.setThenPrice(price);
			}
//			statisticsStockView.setUnavailable(stock.getUnavailable());
			statisticsStockView.setTitle(stock.getTitle());
			typeRecordsMap.forEach((type, records) -> {
				int sum = records.stream().mapToInt(GoodsDonStockRecord::getNum).sum();
				BigDecimal price = records.stream().map((record) -> record.getPrice().multiply(BigDecimal.valueOf(record.getNum())))
						.reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_EVEN);
				statisticsStockView.setTotalWithPrice(type, sum, price);
			});
			res.add(statisticsStockView);
		});
		EasyExcelUtils.createExcelStreamMutilByEasyExcel(response, StatisticsStockView.class, res, "库存统计", "库存统计", ExcelTypeEnum.XLSX, null);

	}

	/**
	 * 批量分配客服
	 */
	@PostMapping("/batch/service")
	@SaCheckPermission("validAccount:distributeServices")
	public Result<String> batchService(@RequestBody List<BatchServiceReq> batchServices) {
		batchServices.forEach(service -> {
			if (StrUtil.isEmpty(service.getCustomerService())) {
				throw BusinessRuntimeException.getInstance("所分配客服不能为空");
			}
			GroupsRelation relation = groupsRelationMapper.selectById(service.getRelationId());
			if (relation == null) {
				return;
			}
			if (StrUtil.isEmpty(relation.getCustomerService()) && relation.getUserId() != 0
					&& GroupsRelation.Status.validity == relation.getStatus()) {
				relation.setCustomerService(service.getCustomerService());
				groupsRelationMapper.updateById(relation);
			}
		});
		return GatewayResponse.SUCCESS.newBuilder().toResult("分配客服成功");
	}

	/**
	 * 签到以及积分数据统计
	 */
	@GetMapping("/get/sign")
	public Result<SearchResult<SignStatisticsData>> getSign(HttpServletRequest request) {
		SearchResult<SignStatisticsData> search = beanSearcher.search(SignStatisticsData.class, MapUtils.flatBuilder(request.getParameterMap()).build());
		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
	}

	/**
	 * 注册账号、网飞空车位数据
	 */
	@GetMapping("/get/title/data")
	public Result<BackgroundDataView> getTitleData() {
		BackgroundDataView views = statisticsDataService.getTitleData();
		return GatewayResponse.SUCCESS.newBuilder().toResult(views);
	}

	/**
	 * 获取每日平台订单折线数据
	 * @param chain 默认1日 2周 3月
	 */
	@GetMapping("/get/plat/brokenLine")
	public Result<BrokenLineObjView> getPlatBrokenLine(Long firstDate, Long lastDate, @RequestParam(defaultValue = "1") Integer chain) {
		String first = null;
		String end = null;
		if (firstDate != null) {
			first = DateUtil.format(DateTime.of(firstDate), "yyyy-MM-dd");
		}
		if (lastDate != null) {
			end = DateUtil.format(DateTime.of(lastDate), "yyyy-MM-dd");
		}
		String dateSqL = "date_format(od.created_time,'%Y-%m-%d')";
		if (chain == 2) {
			dateSqL = "DATE_FORMAT(created_time - INTERVAL (DAYOFWEEK(created_time) - 2) DAY, '%Y-%m-%d')";
		} else if (chain == 3) {
			dateSqL = "DATE_FORMAT(od.created_time, '%Y-%m')";
		}
		BrokenLineObjView brokenLineObjView = Optional.ofNullable(orderDonMapper.selectPlatBrokenLineMoney(first, end))
				.orElse(new BrokenLineObjView());

		if (brokenLineObjView != null) {
			List<PlatOrderDonBrokenLineView> views = beanSearcher.searchAll(PlatOrderDonBrokenLineView.class, MapUtils.builder()
					.put("date", dateSqL)
					.field(PlatOrderDonBrokenLineView::getDate, first, end).op(Operator.Between)
					.orderBy(PlatOrderDonBrokenLineView::getDate).asc()
					.build());
			Map<Long, List<PlatOrderDonBrokenLineView>> goodsMap = views.stream().collect(Collectors.groupingBy(PlatOrderDonBrokenLineView::getGoodsId));
			brokenLineObjView.setGoodsData(goodsMap);
		}
		return GatewayResponse.SUCCESS.newBuilder().toResult(brokenLineObjView);
	}

	/**
	 * 渠道后台数据统计折线图
	 *
	 * @return
	 */
	@GetMapping("/get/channelStatistics/brokenLine")
	public Result<ChannelStatisticsView> getChannelStatisticsView(Long firstDate, Long lastDate) {
		String first = null;
		String end = null;
		if (firstDate != null) {
			first = DateUtil.format(DateTime.of(firstDate), "yyyy-MM-dd");
		}
		if (lastDate != null) {
			end = DateUtil.format(DateTime.of(lastDate), "yyyy-MM-dd");
		}
		ChannelStatisticsView channelStatisticsView = Optional.ofNullable(orderDonMapper.selectChannelStatistics(first, end))
				.orElse(new ChannelStatisticsView());

		List<ChannelCommonView> natureChannelData = beanSearcher.searchAll(ChannelCommonView.class, MapUtils.builder()
				.field(ChannelCommonView::getDate, first, end).op(Operator.Between)
				.put("type", "od.is_distribute is false")
				.orderBy(PlatOrderDonBrokenLineView::getDate).asc()
				.build());
		channelStatisticsView.setNatureChannelData(natureChannelData);

		List<ChannelCommonView> generalPopularizeChannelData = beanSearcher.searchAll(ChannelCommonView.class, MapUtils.builder()
				.field(ChannelCommonView::getDate, first, end).op(Operator.Between)
				.put("type", "od.is_distribute is true and od.is_fixed_distribute is false")
				.orderBy(PlatOrderDonBrokenLineView::getDate).asc()
				.build());
		channelStatisticsView.setGeneralPopularizeChannelData(generalPopularizeChannelData);

		List<ChannelCommonView> distributeChannelData = beanSearcher.searchAll(ChannelCommonView.class, MapUtils.builder()
				.field(ChannelCommonView::getDate, first, end).op(Operator.Between)
				.put("type", "od.is_fixed_distribute is true")
				.orderBy(PlatOrderDonBrokenLineView::getDate).asc()
				.build());
		channelStatisticsView.setDistributeChannelData(distributeChannelData);
		return GatewayResponse.SUCCESS.newBuilder().toResult(channelStatisticsView);
	}

	/**
	 * 用户新增数据统计折线图
	 */
	@GetMapping("/get/newUserStatistics/brokenLine")
	public Result<NewUserStatisticsView> getNewUserStatisticsView(Long firstDate, Long lastDate) {
		String first = null;
		String end = null;
		if (firstDate != null) {
			first = DateUtil.format(DateTime.of(firstDate), "yyyy-MM-dd");
		}
		if (lastDate != null) {
			end = DateUtil.format(DateTime.of(lastDate), "yyyy-MM-dd");
		}
		NewUserStatisticsView newUserStatisticsView = new NewUserStatisticsView();

		List<NewAllUserView> newAllUserViews = beanSearcher.searchAll(NewAllUserView.class, MapUtils.builder().field(NewAllUserView::getDate, first, end).build());

		if (CollUtil.isNotEmpty(newAllUserViews)) {
			newUserStatisticsView.setFirstDate(newAllUserViews.get(0).getDate());
			newUserStatisticsView.setLastDate(newAllUserViews.get(newAllUserViews.size() - 1).getDate());
		}

		List<NewUserView> newUserViews = beanSearcher.searchAll(NewUserView.class, MapUtils.builder().field(NewUserView::getDate, first, end).op(Operator.Between).build());
		newUserStatisticsView.setNewUserData(newUserViews);

		List<NewPcUserView> newPcUserViews = beanSearcher.searchAll(NewPcUserView.class, MapUtils.builder().field(NewPcUserView::getDate, first, end).op(Operator.Between).build());
		newUserStatisticsView.setNewPcUserData(newPcUserViews);

		List<NewDistributeUserView> newDistributeUserViews = beanSearcher.searchAll(NewDistributeUserView.class, MapUtils.builder().field(NewDistributeUserView::getDate, first, end).op(Operator.Between).build());
		newUserStatisticsView.setNewDistributeUserData(newDistributeUserViews);

		List<NewCorpUserView> newCorpUserViews = beanSearcher.searchAll(NewCorpUserView.class, MapUtils.builder().field(NewCorpUserView::getDate, first, end).op(Operator.Between).build());
		newUserStatisticsView.setNewCorpUserData(newCorpUserViews);

		List<NewRedBookUsersView> newRedBookUserViews = beanSearcher.searchAll(NewRedBookUsersView.class, MapUtils.builder().field(NewRedBookUsersView::getDate, first, end).op(Operator.Between).build());
		newUserStatisticsView.setNewRedBookUserData(newRedBookUserViews);
		return GatewayResponse.SUCCESS.newBuilder().toResult(newUserStatisticsView);
	}

	/**
	 * 更新小红书人数
	 */
	@PutMapping("/update/redBookUsers")
	public Result<String> updateRedBookUsers(@RequestBody List<RedBookReq> redBooks) {
		DateTime endOfDay = DateUtil.beginOfDay(DateTime.now());
		redBooks.forEach(redBook -> {
			try {
				DateTime dateTime = DateUtil.parseDate(redBook.getDate());
				if (dateTime.isAfter(endOfDay)) {
					return;
				}
				Optional.ofNullable(statisticsUserDataMapper.selectOne(Wrappers.lambdaQuery(StatisticsUserData.class).eq(StatisticsUserData::getStatisticsDate, dateTime).last("limit 1")))
						.ifPresentOrElse(statistics ->
										statisticsUserDataMapper.update(null, Wrappers.lambdaUpdate(StatisticsUserData.class)
												.set(StatisticsUserData::getNumOfRedBookUser, redBook.getNum())
												.eq(StatisticsUserData::getId, statistics.getId())),
								() -> {
									StatisticsUserData statisticsUserData = new StatisticsUserData();
									statisticsUserData.setNumOfRedBookUser(redBook.getNum());
									statisticsUserData.setStatisticsDate(dateTime);
									statisticsUserDataMapper.insert(statisticsUserData);
								});
			} catch (Exception e) {

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

	/**
	 * 虚拟商品订单客服数据统计
	 */
	@GetMapping("/get/order/service/data")
	public Result<SearchResult<OrderCustomerServiceDataView>> getOrderServiceData() {
		SearchResult<OrderCustomerServiceDataView> search = beanSearcher.search(OrderCustomerServiceDataView.class, MapUtils.builder().build());
		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
	}
}
