package com.tzy.controller.appuser;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.ImmutableMap;
import com.tzy.annotation.ApiLimitRule;
import com.tzy.annotation.ApiLog;
import com.tzy.annotation.RequireRoles;
import com.tzy.app.domain.AppUserDetailInfo;
import com.tzy.app.domain.AppUserPoint;
import com.tzy.app.dto.CardCountDTO;
import com.tzy.app.dto.SimpleMerchantInfo;
import com.tzy.app.dto.SimpleUserMerchant;
import com.tzy.app.dto.UserGroupOrderDTO;
import com.tzy.app.dto.group.GiftCardManage;
import com.tzy.app.dto.group.UserGoodsDTO;
import com.tzy.app.dto.user.AppUserParam;
import com.tzy.app.dto.user.UserConfigDTO;
import com.tzy.app.service.AppUserDetailInfoService;
import com.tzy.app.service.IAppAccountService;
import com.tzy.app.service.IAppBaseUserService;
import com.tzy.common.config.QuerySql;
import com.tzy.common.config.handle.ApiVersion;
import com.tzy.common.constant.Constants;
import com.tzy.common.constant.MqConstans;
import com.tzy.common.constant.MsgConstants;
import com.tzy.common.core.domain.AjaxResult;
import com.tzy.common.core.domain.entity.SysDictData;
import com.tzy.common.dto.UserInfo;
import com.tzy.common.dto.*;
import com.tzy.common.dto.group.OrderQuery;
import com.tzy.common.dto.group.RefundReqDTO;
import com.tzy.common.dto.invoice.InvoiceMerchantDTO;
import com.tzy.common.dto.invoice.InvoiceMerchantQuery;
import com.tzy.common.dto.invoice.InvoiceOrderQuery;
import com.tzy.common.dto.invoice.SimpleOrderDTO;
import com.tzy.common.dto.order.CostOrderDTO;
import com.tzy.common.dto.order.MerchantCostConfig;
import com.tzy.common.dto.order.ShippingPayParam;
import com.tzy.common.dto.order.ShippingPayResult;
import com.tzy.common.enums.BusinessType;
import com.tzy.common.exception.AppAssert;
import com.tzy.common.exception.ServiceException;
import com.tzy.common.utils.*;
import com.tzy.common.utils.bean.BeanUtils;
import com.tzy.common.utils.bean.JSONTools;
import com.tzy.common.utils.poi.ExcelUtil;
import com.tzy.common.utils.qiniu.QiniuUtil;
import com.tzy.coupon.card.domain.TzyCardBaseInfo;
import com.tzy.coupon.card.service.ITzyCardBaseInfoService;
import com.tzy.framework.util.RedisTools;
import com.tzy.framework.util.RedisUtils;
import com.tzy.framework.web.domain.LimitRule;
import com.tzy.member.benefits.dto.SimpleBenefitsDTO;
import com.tzy.member.benefits.service.IMemberBenefitsService;
import com.tzy.pojo.app.*;
import com.tzy.pojo.card.CardGroupInfo;
import com.tzy.pojo.card.CardGroupOrderInfo;
import com.tzy.pojo.item.EvaluationRecord;
import com.tzy.sportcard.api.bean.invoice.AppUserInvoice;
import com.tzy.sportcard.api.bean.invoice.AppUserInvoiceRecord;
import com.tzy.sportcard.api.bean.param.ShippingQuery;
import com.tzy.sportcard.api.domain.*;
import com.tzy.sportcard.api.dto.Carmichae;
import com.tzy.sportcard.api.dto.ReceiveGoodsDTO;
import com.tzy.sportcard.api.service.*;
import com.tzy.sportcard.base.service.CommonCacheService;
import com.tzy.sportcard.dto.MineCardDTO;
import com.tzy.sportcard.dto.RefundCheckDTO;
import com.tzy.sportcard.group.domain.*;
import com.tzy.sportcard.group.service.*;
import com.tzy.sportcard.group.service.impl.GroupActService;
import com.tzy.sportcard.point.service.IAppUserPointRecordService;
import com.tzy.system.domain.TzyShippingAddress;
import com.tzy.system.domain.TzySysNoticeRecord;
import com.tzy.system.service.ISysDictDataService;
import com.tzy.system.service.ITzyShippingAddressService;
import com.tzy.system.service.ITzySysNoticeRecordService;
import com.tzy.util.RandomUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
import org.teasoft.bee.osql.*;
import org.teasoft.honey.osql.core.ConditionImpl;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.text.ParseException;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

/**
 * @author by po'yi
 * @Classname AppUserControllerNew
 * @Description
 * @Date 2022/3/3 13:24
 */
@Api(value = "app用户相关-新")
@RestController
@RequestMapping("/api/{version}/mine")
@Slf4j
public class AppUserControllerNew {
    @Autowired
    private MineApiService mineApiService;
    @Autowired
    private SuidRich suidRich;
    @Autowired
    private PreparedSql preparedSql;
    @Autowired
    private GroupApiService groupApiService;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private ISysDictDataService dictDataService;
    @Autowired
    private ICardGroupOrderInfoService orderInfoService;
    @Autowired
    private ITzyCardBaseInfoService cardBaseInfoService;
    @Value("${tzy.app-version:dev}")
    private String appVersion;
    @Autowired
    private IAppAccountService appAccountService;
    @Autowired
    private MerchantApiService merchantApiService;
    @Autowired
    @Qualifier("threadPoolTaskExecutor")
    private ThreadPoolTaskExecutor pools;
    @Resource
    private AsyncAppService asyncAppService;
    @Resource
    private ChecklistCalendarService checklistCalendarService;
    @Resource
    private IAppUserWinCardService appUserWinCardService;
    @Resource
    private ICardGroupOrderInfoService cardGroupOrderInfoService;
    @Resource
    private ITzySysNoticeRecordService noticeRecordService;
    @Resource
    private CommonCacheService commonCacheService;
    @Resource
    private AppUserInvoiceRecordService appUserInvoiceRecordService;
    @Resource
    private IMemberBenefitsService memberBenefitsService;
    @Resource
    private IAppBaseUserService appBaseUserService;
    @Resource
    private RabbitTemplate rabbitTemplate;
    @Resource
    private IAppUserPointRecordService appUserPointRecordService;
    @Resource
    private ICardGroupOrderReviewService groupReviewService;
    @Resource
    private GroupActService groupActService;
    @Resource
    private ITzyShippingAddressService addressService;
    @Resource
    private AppUserDetailInfoService appUserDetailInfoService;
    @Value("${tzy.idCardMaxNum:2}")
    private Integer idCardMaxNum;
    @Resource
    private ITzyMerchantInfoService tzyMerchantInfoService;
    @Autowired
    private NoticeService noticeService;
    @Autowired
    private IOrderChangeRecordService orderChangeRecordService;

    @ApiLog(title = "获取用戶累积发货卡片数量", businessType = BusinessType.SEARCH)
    @ResponseBody
    @RequestMapping(value = "/card/waitShippingCount", method = RequestMethod.POST)
    @ApiVersion(1.0)
    @ApiOperation("累积待处理数量")
    public OutDTO waitShippingCount(@RequestBody InDto inDto) {
        AppUserInfoDto user = mineApiService.checkUserNew(inDto);
        String sql = QuerySql.getQuerySql("com.tzy.app.dto.CardGoodsDTO.getWaitShippingMerchants");
        List<MerchantInfo> merchantInfos = preparedSql.selectSomeField(sql, new MerchantInfo(),new Object[]{user.getId()});
        return OutDTO.ok().put("merchantInfos", merchantInfos.stream().map(m -> m.getName()).collect(Collectors.toList()));
    }

    @ApiLog(title = "用戶拒绝Or同意商家名下所有订单累积发货", businessType = BusinessType.SEARCH)
    @ResponseBody
    @RequestMapping(value = "/order/refuseByMerchant", method = RequestMethod.POST)
    @ApiVersion(1.0)
    @ApiOperation("用戶拒绝Or同意商家名下所有订单累积发货,传参{id,refuseStatus}")
    public OutDTO refuseByMerchant(@RequestBody InDto inDto) {
        AppUserInfoDto user = mineApiService.checkUserNew(inDto);
        CardGroupOrderInfo condition = new CardGroupOrderInfo();
        if (inDto.get("refuseStatus") == null || inDto.get("id") == null) {
            return OutDTO.error(500, "参数为空");
        }
        condition.setMerchantId(Long.valueOf((inDto.get("id").toString())));
        condition.setUserId(user.getId().longValue());
        condition.setPickUpType("3");
        //condition.setRefuseStatus(0);

        CardGroupOrderInfo updateInfo = new CardGroupOrderInfo();
        int refuseStatus = (Integer) inDto.get("refuseStatus");
        if(2 == refuseStatus) {
            condition.setRefuseStatus(0); // 更新累计状态为0的时间
            updateInfo.setRefuseTime(DateUtils.getNowDate()); // 用户确认累计时间
        }
        updateInfo.setRefuseStatus(refuseStatus);
        updateInfo.setUpdateTime(DateUtils.getTimestampNow());
        suidRich.update(condition, updateInfo);
        return OutDTO.ok();
    }

    @ApiLog(title = "用户同意累积发货", businessType = BusinessType.SEARCH)
    @ResponseBody
    //@RequestMapping(value = "/order/allowPickUp", method = RequestMethod.POST)
    @ApiVersion(1.0)
    @ApiOperation("用户同意累积发货,传参{orderId}")
    public OutDTO allowPickUp(@RequestBody InDto inDto) {
        mineApiService.checkUserNew(inDto);
        CardGroupOrderInfo updateInfo = new CardGroupOrderInfo();
        updateInfo.setId(Long.valueOf((inDto.get("orderId").toString())));
        updateInfo.setRefuseStatus(2);
        updateInfo.setUpdateTime(DateUtils.getTimestampNow());
        suidRich.update(updateInfo);
        return OutDTO.ok();
    }

    @ApiLog(title = "用戶自提", businessType = BusinessType.SEARCH)
    @ResponseBody
    //@RequestMapping(value = "/order/pickUp", method = RequestMethod.POST)
    @ApiVersion(1.0)
    @ApiOperation("用戶自提,传参:{orderId}")
    public OutDTO pickUpOrder(@RequestBody InDto inDto) {
        AppUserInfoDto userInfo = mineApiService.checkUserNew(inDto);
        CardGroupOrderInfo condition = new CardGroupOrderInfo();
        condition.setUserId(userInfo.getId().longValue());
        condition.setId(Long.valueOf((inDto.get("orderId").toString())));
        condition.setStatus(103L);
        //condition.setRefuseStatus(0);
        CardGroupOrderInfo orderInfo = suidRich.selectOne(condition);
        if (orderInfo == null) {
            return OutDTO.error(10010, "订单不符合自提要求!");
        }
        CardGroupOrderInfo updateInfo = new CardGroupOrderInfo();
        updateInfo.setStatus(301L);
        updateInfo.setPickUpType("1");
        updateInfo.setShipTime(DateUtils.getTimestampNow());
        updateInfo.setFinishedTime(DateUtils.getTimestampNow());
        if(Constants.ORDER_TYPE_GROUP.equals(orderInfo.getOrderType())){
            //组团下所有中卡订单全部改为自提
            //自提确认收货 即代表订单完成
            Condition beeCondition = new ConditionImpl();
            beeCondition.op("status", Op.eq, 103)
                    .op("groupInfoId", Op.eq, orderInfo.getGroupInfoId())
                    .op("refuseStatus", Op.lt, 3)
                    .op("userId", Op.eq, userInfo.getId());
            suidRich.update(updateInfo, "status,pickUpType,shipTime,finishedTime",beeCondition);
        }else if(Constants.ORDER_TYPE_SHOP.equals(orderInfo.getOrderType())){
            //商城订单
            updateInfo.setId(orderInfo.getId());
            if(!Constants.payment_type.contains(orderInfo.getPaymentType()) ){
                updateInfo.setClosePaymentStatus(100);//商城订单的打款状态: 0=未结束，100=可申请，200=申请中，300=已打款
                try {
                    Date afterNumWorkDay = DateUtils.getNumWorkDayByNow(new Date(), 5);
                    updateInfo.setClosePaymentTime(afterNumWorkDay);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }
            suidRich.update(updateInfo);
            asyncAppService.addPointByMallOrder(orderInfoService.selectCardGroupOrderInfoById(orderInfo.getId()));
        }
        return OutDTO.ok();
    }

    @ApiLog(title = "限额提醒", businessType = BusinessType.SEARCH)
    @ResponseBody
    //@RequestMapping(value = "/payMsgFlag", method = RequestMethod.POST)
    @ApiVersion(1.0)
    @ApiOperation("限额提醒")
    public OutDTO payMsgFlag(@RequestBody InDto inDto) {
        AppUserInfoDto appUserInfoDto = mineApiService.checkUserNew(inDto);
        String key = LocalDate.now() + "_" + inDto.getUserId();
        boolean msgOfDay = RedisTools.getRedisUtils().hHasKey(Constants.USER_PAY_REMIND, key);
        if (msgOfDay) {
            return OutDTO.ok().put("payMsgFlag", false);
        }
        String sql = QuerySql.getQuerySql("com.tzy.sportcard.api.domain.AppUserInfoDto.getUserOrderAmountOfDay");
        String s = preparedSql.selectFun(sql, new Object[]{inDto.getUserId()});
        boolean flag = false;
        if (Constants.ONE.equals(appUserInfoDto.getOpenPayAmount())
                && new BigDecimal(appUserInfoDto.getPayAmountOfDay()).compareTo(new BigDecimal(s)) == -1) {
            flag = true;
            RedisTools.getRedisUtils().hset(Constants.USER_PAY_REMIND, key, 1, 24 * 60 * 60);
        }
        return OutDTO.ok().put("payMsgFlag", flag);
    }

    @ApiLog(title = "限额提醒V2", businessType = BusinessType.SEARCH)
    @ResponseBody
    @RequestMapping(value = "/payMsgFlag", method = RequestMethod.POST)
    @ApiVersion(2.2)
    @ApiOperation("限额提醒2")
    public OutDTO payMsgFlagV2(@RequestBody InDto inDto) {
        AppUserInfoDto appUserInfoDto = mineApiService.checkUserNew(inDto);
        String key = Constants.USER_PAY_REMIND + LocalDate.now() + "_" + appUserInfoDto.getId();
        String sql = QuerySql.getQuerySql("com.tzy.sportcard.api.domain.AppUserInfoDto.getUserOrderAmountOfDay");
        String s = preparedSql.selectFun(sql, new Object[]{inDto.getUserId()});
        BigDecimal userDayAmount = new BigDecimal(s);
        if (!redisUtils.hasKey(key) && Constants.ONE.equals(appUserInfoDto.getOpenPayAmount()) && new BigDecimal(appUserInfoDto.getPayAmountOfDay()).compareTo(userDayAmount) == -1) {
            redisUtils.set(key, 1, 12 * 60 * 60);
            return OutDTO.ok().put("payMsgFlag", true).put("type", "user").put("amount", appUserInfoDto.getPayAmountOfDay());
        }

        if (!redisUtils.hasKey(key + "_day")) {
            List<SysDictData> remindDay = commonCacheService.getCommonDictData(Constants.USER_PAY_REMIND_DAY, null, null, Constants.USER_PAY_REMIND_DAY, -1);
            int remindDayAmount = CollectionUtils.isEmpty(remindDay) ? 5000 : Integer.parseInt(remindDay.get(0).getDictValue());
            if (userDayAmount.doubleValue() > remindDayAmount) {
                redisUtils.set(key + "_day", 1, 12 * 60 * 60);
                return OutDTO.ok().put("payMsgFlag", true).put("type", "day").put("amount", remindDayAmount);
            }
        }
        if (!redisUtils.hasKey(key + "_month")) {
            String sql2 = QuerySql.getQuerySql("com.tzy.sportcard.api.domain.AppUserInfoDto.getUserOrderAmountOfMonth");
            String monthAmount = preparedSql.selectFun(sql2, new Object[]{inDto.getUserId()});
            List<SysDictData> remindMonth = commonCacheService.getCommonDictData(Constants.USER_PAY_REMIND_MONTH, null, null, Constants.USER_PAY_REMIND_MONTH, -1);
            int remindMonthAmount = CollectionUtils.isEmpty(remindMonth) ? 30000 : Integer.parseInt(remindMonth.get(0).getDictValue());
            if (new BigDecimal(monthAmount).doubleValue() > remindMonthAmount) {
                redisUtils.set(key + "_month", 1, 12 * 60 * 60);
                return OutDTO.ok().put("payMsgFlag", true).put("type", "month").put("amount", remindMonthAmount);
            }
        }
        return OutDTO.ok().put("payMsgFlag", false);
    }

    private List<UserGroupOrderDTO> buildOrderGroupMerchant(Map<String, List<GroupUserOrderDto>> orders) {
        List<UserGroupOrderDTO> data = new ArrayList<>();
        Set<Map.Entry<String, List<GroupUserOrderDto>>> entries = orders.entrySet();
        for (Map.Entry<String, List<GroupUserOrderDto>> entry : entries) {
            List<GroupUserOrderDto> value = entry.getValue();
            List<GroupUserOrderDto> infoOrder = value.stream().filter(o -> o.getId() != null).collect(Collectors.toList());
            List<GroupUserOrderDto> mallOrder = value.stream().filter(o -> o.getId() == null).collect(Collectors.toList());
            String[] merArray = entry.getKey().split("#");
            data.add(new UserGroupOrderDTO(new SimpleMerchantInfo(Integer.valueOf(merArray[0]), merArray[1], merArray[2]), infoOrder,mallOrder));
        }
        return data;
    }

    @ApiLog(title = "获取用户名下累积发货订单(包含商城累计)", businessType = BusinessType.SEARCH)
    @ResponseBody
    @RequestMapping(value = "/getWaitShippingOrder", method = RequestMethod.POST)
    @ApiVersion(2.6)
    @ApiOperation("获取用户名下累积发货订单(包含商城累计)")
    public OutDTO getWaitShippingOrderV2(@RequestBody InDto inDto) {
        UserInfo userInfo = UserUtils.getSimpleUserInfo();
        if(userInfo==null){
            return OutDTO.error500("请先登陆！");
        }
        Integer refuseStatus = inDto.getIntegerParam("refuseStatus");
        refuseStatus=refuseStatus==null?0:refuseStatus;
        int size = inDto.getPageSize();
        int offSet = (inDto.getPageNo() - 1) * size;

        OutDTO result = OutDTO.ok();
        ShippingQuery query = new ShippingQuery().setUserId(userInfo.getId()).setRefuseStatus(refuseStatus)
                .setPageSize(size).setOffset(offSet).setKeyword(inDto.getString("keyword"));
        List<GroupUserOrderDto> userOrders =orderInfoService.getUserWaitShippingOrder(query);
        if (userOrders.isEmpty()) {
            return result;
        }
        Map<String, List<GroupUserOrderDto>> data = new HashMap<>();
        Map<String, String> address = new HashMap<>();
        GroupUserOrderDto firstOrder = userOrders.get(0);
        address.put("shippingAddressId", firstOrder.getShippingAddressId().toString());
        address.put("shippingAddress", firstOrder.getShippingAddress());
        address.put("shippingAddressLinkname", firstOrder.getShippingAddressLinkname());
        address.put("shippingAddressPhone", firstOrder.getShippingAddressPhone());
        List<Integer> userOrderIds = userOrders.stream().filter(o -> o.getId()!=null).map(GroupUserOrderDto::getUserOrderId).collect(Collectors.toList());
        List<Carmichae> allCardGoods=new ArrayList<>();
        if(!CollectionUtils.isEmpty(userOrderIds)){
            allCardGoods = groupApiService.selectGoodsByOrderIds(userOrderIds, 1);
        }
        for (GroupUserOrderDto order : userOrders) {
            if (order.getId()!=null) {
                List<Carmichae> carmichael = allCardGoods.stream().filter(c -> c.getUserOrderId() == order.getUserOrderId().intValue()).collect(Collectors.toList());
                order.setCarmichaels(carmichael);
                if (!CollectionUtils.isEmpty(carmichael)) {
                    List<String> collect = carmichael.stream().map(Carmichae::getCarmichael).filter(StringUtils::isNotEmpty).collect(Collectors.toList());
                    order.setCarmichael(collect);
                    List<String> collect2 = carmichael.stream().map(Carmichae::getPicture).filter(StringUtils::isNotEmpty).collect(Collectors.toList());
                    order.setPictureList(collect2);
                }
            }
            String key = order.getMerchantId() + "#" + order.getMerName() + "#" + order.getMerAvatar();
            if (data.containsKey(key)) {
                List<GroupUserOrderDto> value = data.get(key);
                value.add(order);
            } else {
                List<GroupUserOrderDto> value = new ArrayList<>();
                value.add(order);
                data.put(key, value);
            }
        }
        return result.put("groupOrders", buildOrderGroupMerchant(data))
                .put("address", address);
    }


    @ApiLog(title = "用户消费总金额和支付限制", businessType = BusinessType.SEARCH)
    @ResponseBody
    @RequestMapping(value = "/totalAmount", method = RequestMethod.POST)
    @ApiVersion(1.0)
    @ApiOperation("用户消费总金额和支付限制")
    public OutDTO userTotalAmount(@RequestBody InDto inDto) {
        if(inDto.getUserId()==null){
            return OutDTO.ok();
        }
        AppUserInfoDto userInfo = mineApiService.checkUserNew(inDto);
        Set<String> whitelist = getMarkUserIds();
        if (!whitelist.isEmpty() && whitelist.contains(inDto.getUserId().toString())) {
            return OutDTO.ok().put("totalAmount", BigDecimal.ZERO);
        }
        String sql = QuerySql.getQuerySql("com.tzy.sportcard.api.domain.AppUserInfoDto.getUserTotalAmount");
        String s = preparedSql.selectFun(sql, new Object[]{userInfo.getId()});
        List<SysDictData> payLimit = dictDataService.getByTypeAndStatus("user_pay_limit", Constants.STATUS_OK);
        long count=3;
        int time=1;
        if(!payLimit.isEmpty()){
            count=payLimit.get(0).getDictSort();
            time=Integer.valueOf(payLimit.get(0).getDictValue());
        }
        String countSql = QuerySql.getQuerySql("com.tzy.sportcard.api.domain.AppUserInfoDto.getUserOrderCountByTime");
        countSql +=" and (now() - create_time::timestamp < interval '"+time+" minute')";
        String orderCount = preparedSql.selectFun(countSql, new Object[]{userInfo.getId()});
        return OutDTO.ok().put("totalAmount", new BigDecimal(s)).put("payLimit",Integer.valueOf(orderCount)>=count);
    }

    private Set<String> getMarkUserIds() {
        Object o = redisUtils.get(Constants.WHITELIST_FAVE_VERIFY_USER);
        if (o == null) {
            SysDictData dictData = new SysDictData();
            dictData.setDictType(Constants.WHITELIST_FAVE_VERIFY_USER);
            dictData.setStatus(Constants.STATUS_OK);
            List<SysDictData> list = dictDataService.selectDictDataList(dictData);
            Set<String> collect = new HashSet<>();
            if (!list.isEmpty()) {
                collect = list.stream().map(d -> d.getDictValue()).collect(Collectors.toSet());
                redisUtils.set(Constants.WHITELIST_FAVE_VERIFY_USER, collect, 60);
            }
            return collect;
        }
        return (Set<String>) o;
    }

    /**
     * 查询我的关注，收藏信息
     *
     * @param inDto
     * @return
     */
    @ApiLog(title = "查询我的关注，收藏信息", businessType = BusinessType.UPDATE, indto = "{{inDto}}")
    @ResponseBody
    @ApiOperation("用户我的数量显示")
    @RequestMapping(value = "/getMinesCount", method = RequestMethod.POST)
    public OutDTO getMinesCount(@RequestBody InDto inDto) {
        return OutDTO.ok();
    }

    /**
     * 查询我的关注，收藏信息
     *
     * @param inDto
     * @return
     */
    @ApiLog(title = "查询我的关注，收藏信息", businessType = BusinessType.UPDATE, indto = "{{inDto}}")
    @ResponseBody
    @ApiOperation("查询我的所有数据")
    @RequestMapping(value = "/getMineTotalCount", method = RequestMethod.POST)
    public OutDTO getMineTotalCount(@RequestBody InDto inDto) throws Exception {
        return OutDTO.ok();
    }

    /**
     * 评论通用接口
     *
     * @param inDto
     * @return
     */
    @ApiLog(title = "评论通用接口", businessType = BusinessType.UPDATE, indto = "{{inDto}}")
    @ResponseBody
    @ApiOperation("评论通用接口")
    @RequestMapping(value = "/common/evaluation", method = RequestMethod.POST)
    public OutDTO evaluation(@RequestBody InDto inDto) {
        if(StringUtils.isEmpty(inDto.getString("type"))||StringUtils.isEmpty(inDto.getString("refId"))){
            return OutDTO.error500("参数为空!");
        }

        AppUserInfoDto appUserInfoDto = mineApiService.checkUserNew(inDto);
        EvaluationRecord evaluationRecord = new EvaluationRecord();
        evaluationRecord.setType(inDto.getString("type"));
        evaluationRecord.setRefId(Long.valueOf(inDto.getString("refId")));
        evaluationRecord.setCreateUserId(appUserInfoDto.getId());
        List<EvaluationRecord> list = suidRich.select(evaluationRecord);
        if (!list.isEmpty()) {
            return OutDTO.error(500, "重复!");
        }

        evaluationRecord.setAvatar(appUserInfoDto.getAvatar());
        evaluationRecord.setNickName(appUserInfoDto.getNickname());
        evaluationRecord.setAnonymous((Integer) inDto.get("anonymous"));
        evaluationRecord.setContent(inDto.getString("content"));
        evaluationRecord.setImgUrl(inDto.getString("imgUrl"));
        evaluationRecord.setValue((Integer) inDto.get("value"));

        evaluationRecord.setCreateTime(DateUtils.getTimestampNow());
        suidRich.insert(evaluationRecord);
        if (inDto.get("orderId") != null) {
            CardGroupOrderInfo update = new CardGroupOrderInfo();
            update.setId(Long.valueOf((inDto.getString("orderId"))));
            update.setEvaluation(JSONTools.obj2json(evaluationRecord));
            suidRich.update(update);
        }

        if(Constants.EVALUATION_TYPE_LIST_CALENDAR.equals(evaluationRecord.getType())){
            //日历点赞人数
            checklistCalendarService.updateCalendarLikeNums(evaluationRecord.getRefId());
        }
        return OutDTO.ok();
    }

    /**
     * 取消评论
     *
     * @param inDto
     * @return
     */
    @ApiLog(title = "取消评论", businessType = BusinessType.UPDATE, indto = "{{inDto}}")
    @ResponseBody
    @ApiOperation("取消评论")
    @RequestMapping(value = "/common/evaluation/cancel", method = RequestMethod.POST)
    public OutDTO delEvaluation(@RequestBody InDto inDto) {
        if(StringUtils.isEmpty(inDto.getString("type"))||StringUtils.isEmpty(inDto.getString("refId"))){
            return OutDTO.error500("参数为空!");
        }
        AppUserInfoDto user = mineApiService.checkUserNew(inDto);
        EvaluationRecord evaluationRecord = new EvaluationRecord();
        evaluationRecord.setType(inDto.getString("type"));
        evaluationRecord.setRefId(Long.valueOf(inDto.getString("refId")));
        evaluationRecord.setCreateUserId(user.getId());
        suidRich.delete(evaluationRecord);
        if(Constants.EVALUATION_TYPE_LIST_CALENDAR.equals(evaluationRecord.getType())){
            //日历点赞人数
            checklistCalendarService.updateCalendarLikeNums(evaluationRecord.getRefId());
        }
        return OutDTO.ok();

    }
    /**
     * 根据卡片数量计算物流费用
     *
     * @param inDto
     * @return
     */
    @ApiLog(title = "根据卡片数量计算物流费用", businessType = BusinessType.UPDATE, indto = "{{inDto}}")
    @ResponseBody
    @ApiOperation("根据卡片数量计算物流费用")
    @PostMapping(value = "/shippingCost")
    public OutDTO shippingCost(@RequestBody InDto inDto) {
        Integer totalCount = inDto.getIntegerParam("totalCount");
        Integer cardId = inDto.getIntegerParam("cardId");
        Integer merchantId = inDto.getIntegerParam("merchantId");
        BigDecimal cost;
        if (merchantId == null) {
            cost = getShippingCost(totalCount, cardId);
        } else {
            cost = getShippingCost(totalCount, cardId, merchantId);
        }
        return OutDTO.ok().put("cost", cost);
    }


    private BigDecimal getShippingCost(Integer totalCount, Integer cardId) {
        if (totalCount < 1) {
            throw new ServiceException(500, "订单数量异常");
        }
        String type = "shipping_cost";
        String cost = "99";
        String sql = QuerySql.getQuerySql("com.tzy.sportcard.api.domain.GroupUserOrderDto.getShippingCost");
        try {
            String cardType = cardId != null ? type + cardId : type;
            cost = preparedSql.selectFun(sql, new Object[]{cardType, totalCount, totalCount});
        } catch (Exception e) {
            log.error("运费查询异常", e);
        }
        if (StringUtils.isEmpty(cost) || "-1".equals(cost)) {
            cost = preparedSql.selectFun(sql, new Object[]{type, totalCount, totalCount});
        }
        return new BigDecimal(cost);
    }

    private BigDecimal getShippingCost(Integer totalCount, Integer cardId, Integer merchantId) {
        if (totalCount < 1 || cardId == null) {
            throw new ServiceException(500, "订单数量异常");
        }
        MerchantInfo merchant = merchantApiService.getMerchantInfo(merchantId);
        if (StringUtils.isNotEmpty(merchant.getShippingCostConfig())) {
            MerchantCostConfig costConfig = JSONTools.jsonStr2obj(merchant.getShippingCostConfig(), MerchantCostConfig.class);
            return MerchantCostConfig.buildMerchantShippingCost(costConfig, totalCount);
        }
        List<SysDictData> merchantShippingCost = dictDataService.selectDictDataByCache("merchant_shipping_cost_config", merchantId.toString(), Constants.ONE, true);
        String type = "shipping_cost";
        String cardType = type + cardId;
        if (!CollectionUtils.isEmpty(merchantShippingCost) && StringUtils.isNotEmpty(merchantShippingCost.get(0).getDictValue())) {
            List<String> cardCost = Arrays.asList(merchantShippingCost.get(0).getDictValue().split(","));
            if (cardCost.contains(cardId.toString())) {
                cardType = "merchant_shipping_cost:" + merchantId;
            }
        }
        String sql = QuerySql.getQuerySql("com.tzy.sportcard.api.domain.GroupUserOrderDto.getShippingCost");
        String cost = "99";
        try {
            cost = preparedSql.selectFun(sql, new Object[]{cardType, totalCount, totalCount});
        } catch (Exception e) {
            log.error("运费查询异常", e);
        }
        if (StringUtils.isEmpty(cost) || "-1".equals(cost)) {
            cost = preparedSql.selectFun(sql, new Object[]{type, totalCount, totalCount});
        }
        BigDecimal bigDecimal = new BigDecimal(cost);
        if (bigDecimal.doubleValue() < 0) {
            throw new ServiceException(500, "运费设置异常");
        }
        return new BigDecimal(cost);
    }

    /**
     * 兑换商家积分
     * 2023-3-29 弃用
     *
     * @param inDto
     * @return
     */
    @ApiLog(title = "兑换商家积分", businessType = BusinessType.UPDATE, indto = "{{inDto}}")
    @ResponseBody
    @ApiOperation("兑换商家积分")
    //@RequestMapping(value = "/exchangePoint", method = RequestMethod.POST)
    public OutDTO exchangePoint(@RequestBody InDto inDto) {
        mineApiService.checkUserNew(inDto);
        //多个用逗号隔开
        String userOrderIds = inDto.getString("userOrderIds");
        if (StringUtils.isEmpty(userOrderIds)) {
            return OutDTO.error500("订单为空!");
        }
        String[] ids = userOrderIds.split(",");
        for(String id :ids){
            orderInfoService.exchangePoint(Long.valueOf(id));
        }
        return OutDTO.ok();
    }

    /**
     * 查询用户商家积分
     *
     * @param inDto
     * @return
     */
    @ApiLog(title = "查询用户商家积分", businessType = BusinessType.UPDATE, indto = "{{inDto}}")
    @ResponseBody
    @ApiOperation("查询用户商家积分")
    @RequestMapping(value = "/getMerchantPoint", method = RequestMethod.POST)
    public OutDTO getMerchantPoint(@RequestBody InDto inDto) {
        AppUserInfoDto userInfo = mineApiService.checkUserNew(inDto);
        String sql = QuerySql.getQuerySql("com.tzy.common.dto.MerchantPointDTO.getMerchantPoint");
        List<MerchantPointDTO> points = preparedSql.selectSomeField(sql, new MerchantPointDTO(), new Object[]{userInfo.getId()});
        //points.forEach(r -> r.setPointTypeMsg(dictDataService.getMerchantPointMsg(r.getType())));
        return OutDTO.ok().put("points", points);
    }


    /**
     * 查询用户商家积分
     *
     * @param inDto
     * @return
     */
    @ApiLog(title = "兑换优惠劵", businessType = BusinessType.UPDATE, indto = "{{inDto}}")
    @ResponseBody
    @ApiOperation("兑换优惠劵")
    @RequestMapping(value = "/exchangeCoupon", method = RequestMethod.POST)
    public OutDTO exchangeCoupon(@RequestBody InDto inDto) {
//        AppUserInfoDto appUserInfoDto = mineApiService.checkUserNew(inDto);
//        String cdkey = inDto.getString("cdkey");
//        if(StringUtils.isEmpty(cdkey)){
//            return OutDTO.error500("兑换码为空!");
//        }
//        return orderInfoService.exchangeCoupon(cdkey.trim(),appUserInfoDto.getId());
        return OutDTO.error500("请下载最新版本app!");
    }

    /**
     * 查询用户商家积分
     *
     * @param inDto
     * @return
     */
    @ApiLog(title = "兑换优惠劵", businessType = BusinessType.UPDATE, indto = "{{inDto}}")
    @ResponseBody
    @ApiOperation("兑换优惠劵")
    @ApiLimitRule(seconds = 2,limitCount=10,type= LimitRule.ACT_TOKEN,msg="抱歉,抢购人数太多,请稍后再试!")
    //@RequestMapping(value = "/exchange/coupon/id", method = RequestMethod.POST)
    public OutDTO exchangeCouponById(@RequestBody InDto inDto) {
        AppUserInfoDto appUserInfoDto = mineApiService.checkUserNew(inDto);
        Object couponId = inDto.get("id");
        if(couponId==null){
            return OutDTO.error500("兑换参数为空!");
        }
        return orderInfoService.exchangeCouponById(Long.valueOf(couponId.toString()),appUserInfoDto.getId());
    }


    /**
     * 查询用户商家积分
     *
     * @param inDto
     * @return
     */
    @ApiLog(title = "根据key查询优惠劵详情", businessType = BusinessType.UPDATE, indto = "{{inDto}}")
    @ResponseBody
    @ApiOperation("根据key查询优惠劵详情")
    @RequestMapping(value = "/getCouponByKey", method = RequestMethod.POST)
    public OutDTO getCouponByKey(@RequestBody InDto inDto) {
        String cdkey = inDto.getString("cdkey");
        if(StringUtils.isEmpty(cdkey)){
            return OutDTO.error500("key为空!");
        }
        TzyCardBaseInfo condition = new TzyCardBaseInfo();
        condition.setCdkey(cdkey.trim());
        List<TzyCardBaseInfo> tzyCardBaseInfos = cardBaseInfoService.selectTzyCardBaseInfoList(condition);
        if (tzyCardBaseInfos.isEmpty()) {
            return OutDTO.error500("优惠劵不存在!");
        }
        TzyCardBaseInfo cardBaseInfo = tzyCardBaseInfos.get(0);
        if(StringUtils.isNotEmpty(cardBaseInfo.getPointType())){
            cardBaseInfo.setPointTypeMsg(dictDataService.getMerchantPointMsg(cardBaseInfo.getPointType()));
        }
        return OutDTO.ok().put("coupon",cardBaseInfo);
    }

    /**
     * 查询用户商家积分
     *
     * @param inDto
     * @return
     */
    @ApiLog(title = "积分明细", businessType = BusinessType.UPDATE, indto = "{{inDto}}")
    @ResponseBody
    @ApiVersion(1.0)
    @ApiOperation("积分明细")
    @RequestMapping(value = "/pointRecord", method = RequestMethod.POST)
    public OutDTO getPointRecordByType(@RequestBody InDto inDto) {
        AppUserInfoDto appUserInfoDto = mineApiService.checkUserNew(inDto);
        AppUserPointRecord recordCondition = new AppUserPointRecord();
        Long userId = appUserInfoDto.getId().longValue();
        recordCondition.setUserId(userId);
        Condition condition = new ConditionImpl();
        BigDecimal sumGrowth = new BigDecimal(0);
        //类型,merchant
        String type = inDto.getString("type");
        if (Constants.POINT_TYPE_MERCHANT.equals(type)) {
            condition.op("type", Op.eq, Constants.POINT_TYPE_MERCHANT);
            condition.op("merchantId", Op.eq, Long.valueOf(inDto.getString("merchantId")));
        } else if (Constants.POINT_TYPE_GOODS.equals(type)) {
            condition.op("type", Op.eq, Constants.POINT_TYPE_GOODS);
        } else if (StringUtils.isNotEmpty(type) && type.startsWith(Constants.POINT_TYPE_MERCHANT_ACT_PRE)) {
            condition.op("type", Op.eq, type);
            condition.op("merchantId", Op.eq, Long.valueOf(inDto.getString("merchantId")));
        } else if (StringUtils.isNotEmpty(type) && type.startsWith(Constants.POINT_TYPE_HB_ACT_PRE)) {
            condition.op("type", Op.eq, type);
        } else if (Constants.POINT_TYPE_USER_GROWTH.equals(type)) {
            // 会员成长值
            condition.op("type", Op.eq, Constants.POINT_TYPE_USER_GROWTH);
            String currentMonth = inDto.getString("currentMonth"); // 是否当月 0否 1是
            String monthStr = DateUtils.dateTimeNow(DateUtils.YYYY_MM);
            if ("0".equals(currentMonth)) {
                monthStr = DateUtils.getLastMonthStr();
                condition.op("createTime", Op.greatEqual, DateUtils.getLastMonthDay(1))
                        .op("createTime", Op.lt, DateUtils.getFirstDayOfMonth());
            } else {
                condition.op("createTime", Op.greatEqual, DateUtils.getFirstDayOfMonth());
            }
            sumGrowth = appUserPointRecordService.sumGrowthByMonth(userId, monthStr);
        } else if (StringUtils.isNotEmpty(type) && type.startsWith(Constants.BRAND_POINT_TYPE_PRE)) {
            // 品牌会员积分
            condition.op("type", Op.eq, type)
                    .op("createTime", Op.greatEqual, DateUtils.getDateByDay(-90)); // 近90天
        } else if (StringUtils.isNotEmpty(type) && type.startsWith(Constants.BRAND_GROWTH_TYPE_PRE)) {
            // 品牌会员成长值
            condition.op("type", Op.eq, type);
            String currentMonth = inDto.getString("currentMonth"); // 是否当月 0否 1是
            if ("0".equals(currentMonth)) {
                condition.op("createTime", Op.greatEqual, DateUtils.getLastMonthDay(1))
                        .op("createTime", Op.lt, DateUtils.getFirstDayOfMonth());
            } else {
                condition.op("createTime", Op.greatEqual, DateUtils.getFirstDayOfMonth());
            }
        }else if (StringUtils.isNotEmpty(type) && type.startsWith(Constants.POINT_TYPE_ACT_LUCKY)) {
            condition.op("type", Op.eq, type);
        } else {
            condition.op("type", Op.likeRight, Constants.POINT_TYPE_COMMON)
                    .op("createTime", Op.greatEqual, DateUtils.getDateByDay(-90)); // 近90天
        }
        String subType = inDto.getString("subType");
        if(Constants.ADD.equals(subType)){
            condition.op("changePoint", Op.gt,0);
        }else if(Constants.SUB.equals(subType)){
            condition.op("changePoint", Op.lt,0);
        }
        condition.orderBy("id", OrderType.DESC);
        int page=inDto.getPageNo()!=null&&inDto.getPageNo()>1?inDto.getPageNo():1;
        int size=inDto.getPageSize()!=null&&inDto.getPageSize()>1?inDto.getPageSize():10;
        int offSet=(page-1)*size;
        condition.start(offSet).size(size);
        List<AppUserPointRecord> records = suidRich.select(recordCondition, condition);
        String rStr="refund_"+userId+"_";
        records.stream().filter(r -> r.getMerchantId() != null).forEach(r -> {
            r.setPointTypeMsg(dictDataService.getMerchantPointMsg(r.getType()));
            if (StringUtils.isNotEmpty(r.getRefId()) && r.getRefId().startsWith(rStr)) {
                Long refundId = Long.valueOf(r.getRefId().replace(rStr, StringUtils.EMPTY));
                PointRefundRecord refundRecord = mineApiService.getPointRecord(refundId);
                if (refundRecord != null) {
                    r.setRefundStatus(getPointRefundMsf(refundRecord.getStatus(),refundRecord.getMerchantRemark()));
                }
            }
//            MerchantInfo merchant = merchantApiService.getMerchantByCache(r.getMerchantId().intValue());
//            r.setRefundLimitDay(merchant.getRefundLimitDay());
        });
        return OutDTO.ok()
                .put("records",records)
                .put("sumGrowth", sumGrowth);
    }

    private String getPointRefundMsf(Integer status, String merchantRemark) {
        String msg;
        if (status == 6) {
            msg = "已退款";
        } else if (status == 2) {
            msg = "退款失败【" + merchantRemark + "】";
        } else if (status == 1) {
            msg = "待审核";
        }  else if (status == 5) {
            msg = "退款失败【" + merchantRemark + "】";
        } else {
            msg = "退款中";
        }
        return msg;
    }

    /**
     * 导出用户卡密
     *
     * @param
     * @return
     */
    @ResponseBody
    @ApiVersion(1.0)
    @ApiOperation("导出用户卡密")
    @GetMapping("/exportGoods")
    @CrossOrigin(allowCredentials = "true")
    public void exportGoods(Long groupInfoId, String code, String token, HttpServletResponse response) throws Exception {
        if(groupInfoId==null||StringUtils.isEmpty(code)||StringUtils.isEmpty(token)){
            throw new ServiceException(500,"参数为空!");
        }
        AppBaseUser user = new AppBaseUser();
        user.setCode(code);
        AppBaseUser existUser = suidRich.selectOne(user);
        if(existUser == null){
            throw new ServiceException(500,"用户不存在!");
        }

        if (Constants.APP_VERSION_PROD.equals(appVersion)) {
            Object o = redisUtils.get("export_token" + existUser.getId());
            if(Objects.isNull(o)){
                throw new ServiceException(500,"token失效!");
            }
            if(StringUtils.isEmpty(token)||!token.equals(o.toString())){
                throw new ServiceException(500,"token失效!");
            }
        }

        CardGroupInfo cardGroupInfo = new CardGroupInfo();
        cardGroupInfo.setId(groupInfoId);
        CardGroupInfo info = suidRich.selectOne(cardGroupInfo);
        if(info==null){
            throw new ServiceException(500,"组团不存在");
        }
        UserGoodsDTO con = new UserGoodsDTO();
        con.setGroupInfoId(info.getId());
        con.setUserId(existUser.getId());
        List<UserGoodsDTO> goods = suidRich.selectOrderBy(con, "cast(substring(no from '([0-9]+)$') as integer)");
        if (goods.isEmpty()) {
            throw new ServiceException(500, "卡密为空");
        }
        ExcelUtil<UserGoodsDTO> util = new ExcelUtil(UserGoodsDTO.class);
        String fileName=info.getCode();
        util.exportExcelHttp(goods, response,fileName);
    }

    /**
     * 获取下载token
     *
     * @param inDto
     * @return
     */
    @ApiLog(title = "获取下载token", businessType = BusinessType.SEARCH)
    @ResponseBody
    @ApiVersion(1.0)
    @ApiOperation("获取下载token")
    @PostMapping("/getExportToken")
    public OutDTO getExportToken(@RequestBody InDto inDto) {
        AppUserInfoDto user = mineApiService.checkUserNew(inDto);
        UUID uuid = UUID.randomUUID();
        redisUtils.set("export_token"+user.getId(), uuid,30);
        return OutDTO.ok().put("token",uuid);
    }

    /**
     * 查询用户,仅用于code
     *
     * @param inDto
     * @return
     */
    @ApiLog(title = "查询用户", businessType = BusinessType.SEARCH)
    @ResponseBody
    @ApiVersion(1.0)
    @ApiOperation("查询用户")
    @PostMapping("/getUser")
    public OutDTO getUser(@RequestBody InDto inDto) {
        if(StringUtils.isEmpty(inDto.getString("code"))){
            return OutDTO.error500("code为空!");
        }
        AppBaseUser user = new AppBaseUser();
        user.setCode(inDto.getString("code"));
        AppBaseUser existUser = suidRich.selectOne(user);
        return OutDTO.ok().put("user",existUser);
    }

    /**
     * 赠送优惠劵
     *
     * @param inDto
     * @return
     */
    @ApiLog(title = "赠送优惠劵", businessType = BusinessType.SEARCH)
    @ResponseBody
    @ApiVersion(1.0)
    @ApiOperation("赠送优惠劵")
    @PostMapping("/giveawayCoupon")
    public OutDTO giveawayCoupon(@RequestBody InDto inDto) {
        AppUserInfoDto appUserInfoDto = mineApiService.checkUserNew(inDto);
        Object couponId = inDto.get("couponId");
        Object userId = inDto.get("userId");
        if(couponId==null||userId==null){
            return OutDTO.error500("参数为空!");
        }

        com.tzy.coupon.card.domain.AppUserCardRecord appUserCardRecord = new com.tzy.coupon.card.domain.AppUserCardRecord();
        appUserCardRecord.setUserId(appUserInfoDto.getId().longValue());
        appUserCardRecord.setColumnsearch(" and cbi.give_status=1 and aucr.id = " + couponId);
        CouponInfo giveCoupon = mineApiService.getOneMineCard(appUserCardRecord);
        if(giveCoupon==null){
            return OutDTO.error500("优惠劵不存在!");
        }

        AppUserCardRecord coupon = new AppUserCardRecord();
        coupon.setId(Long.parseLong(couponId.toString()));
        coupon.setUserId(Long.valueOf(userId.toString()));
        suidRich.update(coupon);
        return OutDTO.ok();
    }

    /**
     * 获取用户名下所有积分
     *
     * @param inDto
     * @return
     */
    @ApiLog(title = "获取用户名下所有积分", businessType = BusinessType.SEARCH)
    @ResponseBody
    @ApiVersion(1.0)
    @ApiOperation("获取用户名下所有积分")
    @PostMapping("/point")
    public OutDTO getPoint(@RequestBody InDto inDto) {
        AppUserInfoDto appUserInfoDto = mineApiService.checkUserNew(inDto);
        AppUserPoint userPoint = new AppUserPoint();
        userPoint.setUserId(appUserInfoDto.getId());
        if(StringUtils.isNotEmpty(inDto.getString("type"))){
            userPoint.setType(inDto.getString("type"));
        }
        List<AppUserPoint> points = suidRich.select(userPoint);
        Map<String, List<AppUserPoint>> pointType=null;
        if(!points.isEmpty()){
            pointType = points.stream().collect(Collectors.groupingBy(AppUserPoint::getName));
        }
        return OutDTO.ok().put("userPoint",pointType);
    }

    /**
     * 拉黑商家
     *
     * @param inDto
     * @return
     */
    @ApiLog(title = "拉黑商家", businessType = BusinessType.SEARCH)
    @ResponseBody
    @ApiVersion(1.0)
    @ApiOperation("拉黑商家")
    @PostMapping("/pullBack")
    public OutDTO pullBack(@RequestBody InDto inDto) {
        if(inDto.get("merchantId")==null&&inDto.get("merchantAppUserId")==null){
            return OutDTO.error500("参数为空");
        }
        Long merchantId;
        if(inDto.get("merchantId")!=null){
            merchantId= Long.parseLong(inDto.getString("merchantId"));
        }else{
            MerchantInfo merchantInfo=merchantApiService.getMerchantByAppUserId((Integer)inDto.get("merchantAppUserId"));
            merchantId=merchantInfo.getId().longValue();
        }

        AppUserInfoDto appUserInfoDto = mineApiService.checkUserNew(inDto);
        AppMerchantFans appMerchantFans = new AppMerchantFans();
        appMerchantFans.setUserId(appUserInfoDto.getId().longValue());
        appMerchantFans.setMerchantId(merchantId);
        appMerchantFans.setType(Constants.FANS_TYPE_USER_DISLIKE);
        if(inDto.get("status")==null||Constants.STATUS_OK.equals(inDto.getString("status"))){
            List<AppMerchantFans> existFans = suidRich.select(appMerchantFans);
            if(existFans.isEmpty()){
                appMerchantFans.setCreateTime(new Date());
                suidRich.insert(appMerchantFans);
            }
        }else{
            suidRich.delete(appMerchantFans);
        }
        redisUtils.del("user_dislike_merchant:"+inDto.getUserId());
        return OutDTO.ok();
    }

    /**
     * 获取抢购token
     *
     * @param inDto
     * @return
     */
    @ApiLog(title = "获取抢购token", businessType = BusinessType.SEARCH)
    @ResponseBody
    @ApiVersion(1.0)
    @ApiOperation("获取抢购token")
    @ApiLimitRule(seconds = 1,limitCount=1,type= LimitRule.USER,msg="火爆抢购中,请稍后再试!")
    @PostMapping("/getActToken")
    public OutDTO getActToken(@RequestBody InDto inDto) throws Exception {
        mineApiService.checkUserNew(inDto);
        String key=Constants.AES_TOKEN_CACHE+"_EXPIRE_TIME";
        Object token = redisUtils.rightPop(Constants.AES_TOKEN_CACHE);
        if(token!=null){
            redisUtils.set(Constants.ACT_USER_TOKEN_CACHE+inDto.getUserId(),token.toString(),30);
            return OutDTO.ok().put("token",AESUtil.encryptData(token.toString(),Constants.AES_TOKEN_PASSWORD));
        }

        if(redisUtils.hasKey(key)){
            return OutDTO.error500("火爆抢购中,请稍后再试!");
        }else{
            redisUtils.set(key,1,2);
            List<String> tokens = new ArrayList<>(20);
            for(int i = 0; i <20;i++){
                tokens.add(UUID.randomUUID().toString());
            }
            redisUtils.lCommit(Constants.AES_TOKEN_CACHE,tokens);
            token=redisUtils.rightPop(Constants.AES_TOKEN_CACHE).toString();
            redisUtils.set(Constants.ACT_USER_TOKEN_CACHE+inDto.getUserId(),token.toString(),30);
            return OutDTO.ok().put("token",AESUtil.encryptData(token.toString(),Constants.AES_TOKEN_PASSWORD));
        }
    }

    /**
     * 批量解锁卡片
     */
    @ApiLog(title = "批量解锁卡片",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @RequestMapping(value = "/batch/unlockGoods", method = RequestMethod.POST)
    public OutDTO unlockGoods(@RequestBody InDto inDto) {
        mineApiService.checkUserNew(inDto);
        String ids = inDto.getString("ids");
        if (StringUtils.isEmpty(ids)){
            return OutDTO.error500("参数[ids]不能为空");
        }
        List<Integer> idList = Arrays.asList(ids.split(",")).stream().map(id -> Integer.valueOf(id)).collect(Collectors.toList());
        groupApiService.unlockGoodsByIds(idList);
        return OutDTO.ok();
    }


    /**
     * 注销
     */
    @ApiLog(title = "注销", businessType = BusinessType.SEARCH, indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/logoff")
    public OutDTO logoff(@RequestBody InDto inDto) {
        AppUserInfoDto appUserInfoDto = mineApiService.checkUserNew(inDto);
        mineApiService.checkUserOff(appUserInfoDto.getId());
        com.tzy.app.domain.AppAccount appAccount = new com.tzy.app.domain.AppAccount();
        appAccount.setAccount(appUserInfoDto.getAccount());
        appAccount.setDelFlg(1);
        appAccount.setLogOffTime(new Date());
        appAccountService.updateByAccount(appAccount);
        return OutDTO.ok();
    }

    /**
     * 赠送卡密
     */
    @ApiLog(title = "赠送卡密",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @RequestMapping(value = "/giveawayGoods", method = RequestMethod.POST)
    public OutDTO giveawayGoods(@RequestBody InDto inDto) throws Exception {
        String ids = inDto.getString("ids");
        if (StringUtils.isEmpty(ids)){
            return OutDTO.error500("参数[ids]不能为空");
        }
        Object receiveUserId = inDto.get("receiveUserId");
        if(receiveUserId==null){
            return OutDTO.error500("被赠与方不能为空");
        }
        Integer groupInfoId = (Integer) inDto.get("groupInfoId");
        AppUserInfoDto appUserInfo = mineApiService.checkUserNew(inDto);
        String key="app_order_receive"+appUserInfo.getId();
        if(redisUtils.hasKey(key)){
            return OutDTO.error500("操作频繁,请稍后再试!");
        }
        redisUtils.set(key,1,10);
        Set<Long> idSet = Arrays.stream(ids.split(",")).map(Long::valueOf).collect(Collectors.toSet());
        orderInfoService.giveawayGoods(appUserInfo,(Integer)receiveUserId,new ArrayList<>(idSet),groupInfoId);
        redisUtils.del(key);
        return OutDTO.ok();
    }

    private List<GiveReceiveGoods> getGiveReceiveGoods(ReceiveGoodsDTO ro,String type) {
        boolean isGive=Constants.ORDER_SUB_TYPE_GIVE_AWAY.equals(type);
        Long orderId=isGive?ro.getRefOrderId():ro.getOrderId();
        GiveReceiveGoods cond = new GiveReceiveGoods();
        cond.setOrderId(orderId);
        cond.setType(Constants.ORDER_SUB_TYPE_RECEIVE);
        return suidRich.select(cond);
    }

    /**
     * 赠送,接收卡密记录
     * V2:兼容新库卡密
     */
    @ApiLog(title = "赠送,接收卡密记录",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @RequestMapping(value = "/giveOrReceiveGoodsRecord", method = RequestMethod.POST)
    @ApiVersion(2.0)
    public OutDTO giveOrReceiveGoodsRecordV2(@RequestBody InDto inDto) {
        AppUserInfoDto userInfo = mineApiService.checkUserNew(inDto);
        String type = inDto.getString("type");
        if(!(Constants.ORDER_SUB_TYPE_GIVE_AWAY.equals(type)|| Constants.ORDER_SUB_TYPE_RECEIVE.equals(type))){
            return OutDTO.error500("类型不存在");
        }

        String keyword = inDto.getString("keyword");
        PageInfo<ReceiveGoodsDTO> orderPage=orderInfoService.getGiveReceiveOrder(userInfo.getId(),type,keyword,inDto.getPageNo(),inDto.getPageSize());
        orderPage.getList().forEach(g -> g.setGoods(getGiveReceiveGoods(g,type)));

        String countSql=QuerySql.getQuerySql("com.tzy.sportcard.api.dto.ReceiveGoodsDTO.getGiveReceiveGoodsCount");
        String giveCount = preparedSql.selectFun(countSql, new Object[]{userInfo.getId(),Constants.ORDER_SUB_TYPE_GIVE_AWAY});
        String receiveCount = preparedSql.selectFun(countSql, new Object[]{userInfo.getId(),Constants.ORDER_SUB_TYPE_RECEIVE});

        return OutDTO.ok().put("receiveOrders",orderPage.getList()).put("total",orderPage.getTotal())
                .put("receiveCount",receiveCount).put("giveCount",giveCount);
    }


    /**
     * 查看接受的卡密订单
     */
    @ApiLog(title = "查看接受的卡密订单",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @RequestMapping(value = "/readReceiveOrder", method = RequestMethod.POST)
    public OutDTO readReceiveOrder(@RequestBody InDto inDto) {
        AppUserInfoDto userInfo = mineApiService.checkUserNew(inDto);
        CardGroupOrderInfo condition = new CardGroupOrderInfo();
        condition.setOrderSubType(Constants.ORDER_SUB_TYPE_RECEIVE);
        condition.setUserId(userInfo.getId().longValue());
        condition.setReadFlag(0);

        CardGroupOrderInfo updateOrder = new CardGroupOrderInfo();
        updateOrder.setReadFlag(1);
        updateOrder.setUpdateTime(DateUtils.getTimestampNow());
        suidRich.update(condition,updateOrder);
        return OutDTO.ok();
    }


    /**
     * 查询赠送卡密历史用户
     */
    @ApiLog(title = "查询赠送卡密历史用户",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @RequestMapping(value = "/getGiveHistoryUser", method = RequestMethod.POST)
    public OutDTO getGiveHistoryUser(@RequestBody InDto inDto) {
        mineApiService.checkUserNew(inDto);
        String sql=QuerySql.getQuerySql("com.tzy.common.dto.GiveUserDTO.getGiveHistoryUser");
        String keyword = inDto.getString("keyword");
        StringBuffer querySql=new StringBuffer("");
        if(StringUtils.isNotEmpty(keyword)){
            querySql.append(" and  u.nickname ILIKE '%").append(keyword).append("%'");
        }
        String aftSql=" GROUP BY u.code,u.id,u.nickname,u.avatar order by  num desc";
        sql=sql+querySql.toString()+aftSql;
        int offSet=(inDto.getPageNo()-1)*inDto.getPageSize();
        List<GiveUserDTO> giveUsers = preparedSql.selectSomeField(sql, new GiveUserDTO(), new Object[]{inDto.getUserId()},offSet,inDto.getPageSize());
        return OutDTO.ok().put("giveUsers",giveUsers);
    }


    /**
     * 添加发票抬头信息
     */
    @ApiLog(title = "添加发票抬头信息",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ApiOperation("添加发票抬头信息,参数:AppUserInvoice")
    @ResponseBody
    @RequestMapping(value = "/invoice/add", method = RequestMethod.POST)
    public OutDTO addInvoice(@RequestBody InDto inDto) {
        AppUserInfoDto userInfo = mineApiService.checkUserNew(inDto);
        AppUserInvoice userInvoice = new AppUserInvoice();
        BeanUtil.populate(userInvoice,inDto.getData());

        if(StringUtils.isEmpty(userInvoice.getInvoiceName())){
            return OutDTO.error500("发票抬头信息为空!");
        }

        AppUserInvoice condition = new AppUserInvoice();
        condition.setUserId(userInfo.getId().longValue());
        condition.setTaxId(userInvoice.getTaxId()); // 税号
        condition.setInvoiceName(userInvoice.getInvoiceName()); // 抬头名称
        condition.setEmail(userInvoice.getEmail()); // 邮箱
        /*if(Constants.INVOICE_TYPE_COMPANY.equals(userInvoice.getType())){
            if(StringUtils.isEmpty(userInvoice.getTaxId())){
                return OutDTO.error500("发票信息为空!");
            }
            condition.setTaxId(userInvoice.getTaxId());
        }else{
            condition.setInvoiceName(userInvoice.getInvoiceName());
        }*/
        List<AppUserInvoice> existInvoice = suidRich.select(condition);
        if(!CollectionUtils.isEmpty(existInvoice)){
            return OutDTO.error(10500, "发票信息重复!");
        }
        userInvoice.setCreateTime(DateUtils.getTimestampNow());
        userInvoice.setUserId(userInfo.getId().longValue());
        userInvoice.setStatus(1);
        if(userInvoice.getDefaultFlag()){
            // 如果设置了默认抬头、把该用户所有的抬头改成false
            cardGroupOrderInfoService.updateInvoice(userInvoice.getUserId());
        }
        suidRich.insert(userInvoice);
        return OutDTO.ok();
    }

    /**
     * 修改发票抬头信息
     */
    @ApiLog(title = "修改发票抬头信息",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ApiOperation("修改发票抬头信息,参数:AppUserInvoice")
    @ResponseBody
    @RequestMapping(value = "/invoice/update", method = RequestMethod.POST)
    public OutDTO updateInvoice(@RequestBody InDto inDto) {
        AppUserInfoDto userInfo = mineApiService.checkUserNew(inDto);
        AppUserInvoice userInvoice = new AppUserInvoice();
        BeanUtil.populate(userInvoice,inDto.getData());

        if(StringUtils.isEmpty(userInvoice.getInvoiceName())||userInvoice.getId()<1){
            return OutDTO.error500("发票抬头信息为空!");
        }
        userInvoice.setCreateTime(null);
        userInvoice.setUpdateTime(DateUtils.getTimestampNow());
        if(userInvoice.getDefaultFlag()){
            // 如果设置了默认抬头、把该用户所有的抬头改成false
            cardGroupOrderInfoService.updateInvoice(userInvoice.getUserId());
        }
        suidRich.update(userInvoice);
        return OutDTO.ok();
    }

    /**
     * 删除发票抬头信息
     */
    @ApiLog(title = "删除发票抬头信息",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ApiOperation("删除发票抬头信息,参数:id")
    @ResponseBody
    @RequestMapping(value = "/invoice/del", method = RequestMethod.POST)
    public OutDTO delInvoice(@RequestBody InDto inDto) {
        Integer id = (Integer) inDto.get("id");
        if(id==null){
            return OutDTO.error500("id为空");
        }
        AppUserInfoDto userInfo = mineApiService.checkUserNew(inDto);
        AppUserInvoice userInvoice = new AppUserInvoice();
        userInvoice.setId(id.longValue());
        suidRich.delete(userInvoice);
        return OutDTO.ok();
    }
    /**
     * 获取发票抬头信息
     */
    @ApiLog(title = "获取发票抬头信息",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ApiOperation("获取发票抬头信息,无参数")
    @ResponseBody
    @RequestMapping(value = "/invoice/list", method = RequestMethod.POST)
    public OutDTO getInvoice(@RequestBody InDto inDto) {
        AppUserInfoDto userInfo = mineApiService.checkUserNew(inDto);
        AppUserInvoice userInvoice = new AppUserInvoice();
        userInvoice.setUserId(userInfo.getId().longValue());
        List<AppUserInvoice> invoiceList = suidRich.selectOrderBy(userInvoice,"id",new OrderType[] {OrderType.DESC});
        return OutDTO.ok().put("invoiceList",invoiceList);
    }

    /**
     * 获取申请开票金额v3
     */
    @ApiLog(title = "获取申请开票金额v3",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ApiOperation("获取申请开票金额,参数:merchantId")
    @ApiVersion(3.3)
    @ResponseBody
    @RequestMapping(value = "/invoice/amount", method = RequestMethod.POST)
    public OutDTO openInvoiceAmountV3(@RequestBody InDto inDto) {
        Integer merchantId = inDto.getIntegerParam("merchantId");
        if (merchantId == null) {
            return OutDTO.error500("merchantId为空");
        }
        UserInfo user = UserUtils.getSimpleUserInfo(true);
        if (user == null) {
            return OutDTO.error500("请先登录!");
        }
        String orderType = inDto.getString("orderType");
        if(Strings.isEmpty(orderType)){ // 默认拼团订单
            orderType = "group";
        }
        InvoiceOrderQuery query = new InvoiceOrderQuery()
                .setMerchantId(merchantId)
                .setUserId(user.getId())
                .setLimitTime(!checkUserInvoiceOpen(user.getId()))
                .setOrderType(orderType);
        query.setPageNo(inDto.getPageNo());
        query.setPageSize(inDto.getPageSize());
        PageInfo<SimpleOrderDTO> orderPage = orderInfoService.getMerchantInvoiceOrderV3(query);
        if (inDto.getPageNo() == 1 && CollectionUtils.isEmpty(orderPage.getList())) {
            return OutDTO.error500("不存在需要开票的订单!");
        }
        List<InvoiceMerchantDTO> merchants = orderInfoService.getInvoiceMerchantV3(user.getId(), merchantId, !checkUserInvoiceOpen(user.getId()), orderType);
        BigDecimal invoiceAmount = CollectionUtils.isEmpty(merchants) ? BigDecimal.ZERO : merchants.get(0).getInvoiceAmount();
        return OutDTO.ok().put("invoiceAmount", invoiceAmount).put("orderPage", orderPage);
    }

    /**
     * 获取申请开票金额v2
     */
    @ApiLog(title = "获取申请开票金额v2",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ApiOperation("获取申请开票金额,参数:merchantId")
    @ApiVersion(1.0)
    @ResponseBody
    @RequestMapping(value = "/invoice/amount", method = RequestMethod.POST)
    public OutDTO openInvoiceAmountV2(@RequestBody InDto inDto) {
        Integer merchantId = inDto.getIntegerParam("merchantId");
        if (merchantId == null) {
            return OutDTO.error500("merchantId为空");
        }
        UserInfo user = UserUtils.getSimpleUserInfo(true);
        if (user == null) {
            return OutDTO.error500("请先登录!");
        }
        InvoiceOrderQuery query = new InvoiceOrderQuery().setMerchantId(merchantId).setUserId(user.getId()).setLimitTime(!checkUserInvoiceOpen(user.getId()));
        query.setPageNo(inDto.getPageNo());
        query.setPageSize(inDto.getPageSize());
        PageInfo<SimpleOrderDTO> orderPage = orderInfoService.getMerchantInvoiceOrder(query);
        if (inDto.getPageNo() == 1 && CollectionUtils.isEmpty(orderPage.getList())) {
            return OutDTO.error500("不存在需要开票的订单!");
        }
        List<InvoiceMerchantDTO> merchants = orderInfoService.getInvoiceMerchant(user.getId(), merchantId, !checkUserInvoiceOpen(user.getId()));
        BigDecimal invoiceAmount = CollectionUtils.isEmpty(merchants) ? BigDecimal.ZERO : merchants.get(0).getInvoiceAmount();
        return OutDTO.ok().put("invoiceAmount", invoiceAmount).put("orderPage", orderPage);
    }

    /**
     * 申请开票
     */
    @ApiLog(title = "申请开票v3",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ApiOperation("申请开票v3,参数:merchantId,invoiceId")
    @ApiLimitRule(seconds = 1, limitCount = 1, type = LimitRule.USER, msg = "操作频繁,请稍后再试!")
    @ResponseBody
    @RequestMapping(value = "/invoice/open", method = RequestMethod.POST)
    @ApiVersion(3.3)
    public OutDTO openInvoiceV3(@RequestBody InDto inDto) {
        UserInfo user = UserUtils.getSimpleUserInfo(true);
        if(user==null){
            return OutDTO.error500("请先登陆!");
        }
        Integer merchantId = inDto.getIntegerParam("merchantId");
        if (merchantId == null) {
            return OutDTO.error500("merchantId为空");
        }
        Integer invoiceId = inDto.getIntegerParam("invoiceId");
        if (invoiceId == null) {
            return OutDTO.error500("invoiceId为空");
        }
        String orderType = inDto.getString("orderType");
        if(Strings.isEmpty(orderType)){ // 默认拼团订单
            orderType = "group";
        }
        String orderIds =  inDto.getString("orderIds");
        String key="invoice_"+user.getId();
        if(redisUtils.hasKey(key)){
            return OutDTO.error500("申请频繁,请稍后再试!");
        }
        AppUserInvoiceRecord invoiceRecord=orderInfoService.openInvoiceV3(user,merchantId,invoiceId,orderIds, orderType);
        redisUtils.del(key);
        return OutDTO.ok().put("invoiceRecord",invoiceRecord);
    }

    /**
     * 申请开票
     */
    @ApiLog(title = "申请开票v2",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ApiOperation("申请开票v2,参数:merchantId,invoiceId")
    @ApiLimitRule(seconds = 1, limitCount = 1, type = LimitRule.USER, msg = "操作频繁,请稍后再试!")
    @ResponseBody
    @RequestMapping(value = "/invoice/open", method = RequestMethod.POST)
    @ApiVersion(1.0)
    public OutDTO openInvoiceV2(@RequestBody InDto inDto) {
        UserInfo user = UserUtils.getSimpleUserInfo(true);
        if(user==null){
            return OutDTO.error500("请先登陆!");
        }
        Integer merchantId = inDto.getIntegerParam("merchantId");
        if (merchantId == null) {
            return OutDTO.error500("merchantId为空");
        }
        Integer invoiceId = inDto.getIntegerParam("invoiceId");
        if (invoiceId == null) {
            return OutDTO.error500("invoiceId为空");
        }
        String orderIds =  inDto.getString("orderIds");
        String key="invoice_"+user.getId();
        if(redisUtils.hasKey(key)){
            return OutDTO.error500("申请频繁,请稍后再试!");
        }
        AppUserInvoiceRecord invoiceRecord=orderInfoService.openInvoiceV2(user,merchantId,invoiceId,orderIds);
        redisUtils.del(key);
        return OutDTO.ok().put("invoiceRecord",invoiceRecord);
    }

    /**
     * 获取开票记录
     */
    @ApiLog(title = "获取开票记录",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ApiOperation("获取开票记录,参数:status")
    @ResponseBody
    @RequestMapping(value = "/invoice/mine", method = RequestMethod.POST)
    public OutDTO mineInvoice(@RequestBody InDto inDto) {
        UserInfo user = UserUtils.getSimpleUserInfo();
        AppAssert.notNull(user,MsgConstants.NEED_LOGIN);
        Integer merchantId = null;
        Integer userId = null;
        if(StringUtils.isNotEmpty(inDto.getString("merchant"))){
            AppAssert.notNull(user.getMerchantId(),MsgConstants.NEED_LOGIN);
            merchantId = user.getMerchantId();
        }else{
            userId = user.getId();
        }
        PageInfo<AppUserInvoiceRecord> recordList = appUserInvoiceRecordService.page(inDto.getPageNo(), inDto.getPageSize(), merchantId, userId, (Integer) inDto.get("status"), inDto.getString("keyword"));
        return OutDTO.ok().put("invoiceRecord",recordList.getList());

    }

    /**
     * 获取开票记录
     */
    @ApiLog(title = "获取开票记录",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ApiOperation("获取开票记录,参数:status")
    @ApiVersion(3.3)
    @ResponseBody
    @RequestMapping(value = "/invoice/mine", method = RequestMethod.POST)
    public OutDTO mineInvoiceV3(@RequestBody InDto inDto) {
        UserInfo user = UserUtils.getSimpleUserInfo();
        AppAssert.notNull(user,MsgConstants.NEED_LOGIN);
        Integer merchantId = null;
        Integer userId = null;
        if(StringUtils.isNotEmpty(inDto.getString("merchant"))){
            AppAssert.notNull(user.getMerchantId(),MsgConstants.NEED_LOGIN);
            merchantId = user.getMerchantId();
        }else{
            userId = user.getId();
        }
        String orderType = inDto.getString("orderType");
        PageInfo<AppUserInvoiceRecord> recordList = appUserInvoiceRecordService.pageV3(inDto.getPageNo(), inDto.getPageSize(),
                merchantId, userId,
                (Integer) inDto.get("status"),
                inDto.getString("keyword"), orderType);
        return OutDTO.ok().put("invoiceRecord",recordList.getList());

    }

    /**
     * 获取开票关联订单
     */
    @ApiLog(title = "获取开票关联订单",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ApiOperation("查询发票关联订单,参数:invoiceId")
    @ApiVersion(1.0)
    @ResponseBody
    @RequestMapping(value = "/invoice/order", method = RequestMethod.POST)
    public OutDTO mineInvoiceOrder(@RequestBody InDto inDto) {
        mineApiService.checkUserNew(inDto);
        Integer invoiceId = (Integer) inDto.get("invoiceId");
        if (invoiceId == null) {
            return OutDTO.error500("invoiceId为空");
        }

        SimpleOrderDTO condition = new SimpleOrderDTO();
        condition.setInvoiceId(invoiceId);
        Condition beeCondition = new ConditionImpl();
        beeCondition.orderBy("id", OrderType.DESC);
        beeCondition.start((inDto.getPageNo() - 1) * inDto.getPageSize()).size(inDto.getPageSize());
        List<SimpleOrderDTO> orders = suidRich.select(condition, beeCondition);
        return OutDTO.ok().put("invoiceOrders",orders);
    }

    /**
     * 获取开票商家
     */
    @ApiLog(title = "获取开票商家v2",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ApiOperation("获取开票商家,参数:无")
    @ApiVersion(1.0)
    @ResponseBody
    @RequestMapping(value = "/invoice/merchant", method = RequestMethod.POST)
    public OutDTO getInvoiceMerchantV2(@RequestBody InDto inDto) {
        UserInfo user = UserUtils.getSimpleUserInfo(true);
        if(user==null){
            return OutDTO.error500("请先登陆!");
        }
        List<InvoiceMerchantDTO> list = orderInfoService.getInvoiceMerchant(user.getId(),null,!checkUserInvoiceOpen(user.getId()));
        return OutDTO.ok().put("merchants",list);
    }

    /**
     * 获取开票商家
     */
    @ApiLog(title = "获取开票商家v3",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ApiOperation("获取开票商家,参数:无")
    @ApiVersion(3.3)
    @ResponseBody
    @RequestMapping(value = "/invoice/merchant", method = RequestMethod.POST)
    public OutDTO getInvoiceMerchantV3(@RequestBody InDto inDto) {
        UserInfo user = UserUtils.getSimpleUserInfo(true);
        if(user==null){
            return OutDTO.error500("请先登陆!");
        }
        String orderType = inDto.getString("orderType");
        if(Strings.isEmpty(orderType)){ // 默认拼团订单
            orderType = "group";
        }
        List<InvoiceMerchantDTO> list = orderInfoService.getInvoiceMerchantV3(user.getId(),null,!checkUserInvoiceOpen(user.getId()), orderType);
        return OutDTO.ok().put("merchants",list);
    }
    @ApiLog(title = "获取开票商家v4",businessType = BusinessType.SEARCH,indto = "{{invoiceMerchantQuery}}")
    @ApiOperation("获取开票商家,参数:用户id")
    @ApiVersion(4.5)
    @ResponseBody
    @RequestMapping(value = "/invoice/merchant", method = RequestMethod.POST)
    public OutDTO getInvoiceMerchantV4(@RequestBody InDto inDto){
        UserInfo user = UserUtils.getSimpleUserInfo(true);
        if(user==null){
            return OutDTO.error500("请先登陆!");
        }
        String orderType = inDto.getString("orderType");
        if(Strings.isEmpty(orderType)){ // 默认拼团订单
            orderType="group";
        }
        InvoiceMerchantQuery invoiceMerchantQuery = new InvoiceMerchantQuery();
        invoiceMerchantQuery.setOrderType(orderType);
        invoiceMerchantQuery.setLimitTime(!checkUserInvoiceOpen(user.getId()));
        invoiceMerchantQuery.setMerchantName(inDto.getString("merchantName"));
        invoiceMerchantQuery.setPageNo(inDto.getPageNo());
        invoiceMerchantQuery.setPageSize(inDto.getPageSize());
        invoiceMerchantQuery.setUserId(user.getId());
        PageInfo<InvoiceMerchantDTO> invoiceMerchantV4 = orderInfoService.getInvoiceMerchantV4(invoiceMerchantQuery);
        return OutDTO.ok().buildPage(inDto,Math.toIntExact(invoiceMerchantV4.getTotal()),"merchants",invoiceMerchantV4.getList());
    }
    private boolean checkUserInvoiceOpen(Integer userId) {
        return orderInfoService.checkUserInvoiceOpen(userId);
    }

    /**
     * 获取商家用户拉黑情况
     */
    @ApiLog(title = "获取商家用户拉黑情况",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ApiOperation("获取商家用户拉黑情况")
    @ResponseBody
    @RequestMapping(value = "/getUserBackStatus", method = RequestMethod.POST)
    public OutDTO getUserBackStatus(@RequestBody InDto inDto) {
        Integer merchantAppUserId = inDto.getIntegerParam("merchantAppUserId");
        if (inDto.get("userId") == null || merchantAppUserId == null) {
            return OutDTO.error500("参数为空");
        }
        MerchantInfo merchantInfo = merchantApiService.getMerchantByAppUserId(merchantAppUserId);
        if (merchantInfo == null) {
            return OutDTO.error500("商家不存在");
        }
        AppMerchantFans condition = new AppMerchantFans();
        condition.setUserId(Long.valueOf(inDto.getString("userId")));
        condition.setMerchantId(merchantInfo.getId().longValue());
        List<AppMerchantFans> fansList = suidRich.select(condition, "type");
        String status = "non";
        if (!fansList.isEmpty()) {
            List<String> types = fansList.stream().map(f -> f.getType()).collect(Collectors.toList());
            if (types.contains(Constants.FANS_TYPE_USER_DISLIKE)) {
                status = Constants.FANS_TYPE_USER_DISLIKE;
            } else if (types.contains(Constants.FANS_TYPE_MERCHANT_DISLIKE)) {
                status = Constants.FANS_TYPE_MERCHANT_DISLIKE;
            }
        }
        return OutDTO.ok().put("status", status);
    }

    @ApiLog(title = "新版个人中卡查询",businessType = BusinessType.UPDATE,indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/card/new")
    public OutDTO selectWinCardNew(@RequestBody InDto inDto){
        AppUserInfoDto user = mineApiService.checkUserNew(inDto);
        GoodsQuery condition = new GoodsQuery().setUserId(user.getId()).setKeyword(inDto.getString("keyword"))
                .setRefType(inDto.getString("refType")).setPageNo(inDto.getPageNo()).setPageSize(inDto.getPageSize());
        Object del = inDto.get("delFlag");
        int delFlag=del==null?0:(Integer)del;
        condition.setDelFlag(delFlag);
        String sport = inDto.getString("sport");
        if(StringUtils.isNotEmpty(sport)){
            condition.setSportList(groupApiService.getSportList(sport));
        }

        PageInfo<AppUserWinCard> cards = appUserWinCardService.selectWinCard(condition);
        List<CardCountDTO> count = appUserWinCardService.selectWinCardCount(condition.getUserId(),delFlag);
        return OutDTO.ok().put("cards", cards)
                .put("countMap", count.stream().collect(Collectors.toMap(CardCountDTO::getSport, CardCountDTO::getCount)))
                .put("totalCount", count.stream().mapToLong(CardCountDTO::getCount).sum());
    }

    @ApiLog(title = "修改中卡仓库状态",businessType = BusinessType.UPDATE,indto = "{{inDto}}")
    @ResponseBody
    @RequestMapping(value = "/card/edit/status", method = RequestMethod.POST)
    public OutDTO updateWinCardStatus(@RequestBody InDto inDto){
        AppUserInfoDto user = mineApiService.checkUserNew(inDto);
        Integer status = (Integer) inDto.get("status");
        AppUserWinCard card = new AppUserWinCard();
        card.setUserId(user.getId());
        card.setId(Long.valueOf(inDto.getString("id")));
        card.setDelFlag(status);
        if(1==status){
            card.setDelTime(new Date());
        }
        suidRich.update(card);
        return OutDTO.ok();
    }

    @ApiLog(title = "我的优惠券", businessType = BusinessType.SEARCH, indto = "{{inDto}}")
    @PostMapping("/coupon")
    public OutDTO findMineCoupon(@RequestBody InDto inDto){
        if (null == inDto) return OutDTO.error(10000, "参数不能为空");
        OutDTO checkUser = mineApiService.checkUser(inDto);
        if (!checkUser.isSuccess()) return checkUser;
        MineCardDTO mineCardDTO = mineApiService.findMineCard(inDto);
        List<CouponInfo> couponInfoList = mineCardDTO.getCouponInfos();
        PageInfo<CouponInfo> pageInfo = new PageInfo<>(couponInfoList);
        return OutDTO.ok().put("couponInfos", pageInfo)
                .put("merchantTotal", mineCardDTO.getMerchantTotal())
                .put("platformTotal", mineCardDTO.getPlatformTotal());
    }

    @ApiVersion(3.6)
    @ApiLog(title = "我的优惠券", businessType = BusinessType.SEARCH, indto = "{{inDto}}")
    @PostMapping("/coupon")
    public OutDTO findMineCouponV2(@RequestBody InDto inDto){
        if (null == inDto) return OutDTO.error(10000, "参数不能为空");

        AppUserInfoDto appUserInfoDto;
        try {
            appUserInfoDto = mineApiService.checkUserNew(inDto);
        } catch (Exception e) {
            return OutDTO.error500(e.getMessage());
        }

        MineCardDTO mineCardDTO = mineApiService.findMineCardV2(inDto, appUserInfoDto);
        List<CouponInfo> couponInfoList = mineCardDTO.getCouponInfos();
        PageInfo<CouponInfo> pageInfo = new PageInfo<>(couponInfoList);
        return OutDTO.ok().put("couponInfos", pageInfo)
                .put("merchantTotal", mineCardDTO.getMerchantTotal())
                .put("platformTotal", mineCardDTO.getPlatformTotal());
    }

    @ApiLog(title = "预售订单主动退款", businessType = BusinessType.SEARCH, indto = "{{inDto}}")
    @PostMapping("/order/refund")
    public OutDTO refundPreSaleOrder(@RequestBody InDto inDto) {
        if (null == inDto) {
            return OutDTO.error500("参数不能为空!");
        }
        Integer orderId = (Integer) inDto.get("orderId");
        if (orderId == null) {
            return OutDTO.error500("orderId不能为空!");
        }
        AppUserInfoDto user = mineApiService.checkUserNew(inDto);
        cardGroupOrderInfoService.refundPreSaleOrder(user.getId(), orderId.longValue());
        return OutDTO.ok();
    }


    @ApiLog(title = "首页消息提醒(5合1版)", businessType = BusinessType.SEARCH, indto = "{{inDto}}")
    @PostMapping("/msg/recommend")
    public OutDTO getUserMsgRecommend(@RequestBody InDto inDto) throws Exception {
        Integer userId = inDto.getUserId();
        Future<List<LockCardOrderCunt>> unlockGoodsFuture= pools.submit(() ->  mineApiService.selectLockCardOrderCount(userId));
        Future<List<RefundReqDTO>> refundFuture= pools.submit(() ->  cardGroupOrderInfoService.getUserRefundReq(userId));
        AppUserInfoDto user = mineApiService.checkUserNew(inDto);
        OutDTO outDTO = OutDTO.ok()
                //.put("refuseCount", refuseWaitShipping(user))
                .put("refuseCount", 0)
                //.put("merchantInfo", waitShippingCount(user))
                .put("merchantInfo",new ArrayList<>())
                .put("receiveCount", getUserReceiveCount(user.getId()))
                .put("unReadeCount", getUserMsgUnReadeCount(user.getId()))
                //.put("waitApplyOrderFlag", hasWaitApplyCardOrder(user.getId()));
                .put("waitApplyOrderFlag", false);
        return outDTO.put("unlockGoods", unlockGoodsFuture.get()).put("refundReqs", refundFuture.get());
    }

    @ApiLog(title = "订单页消息提醒", businessType = BusinessType.SEARCH, indto = "{{inDto}}")
    @PostMapping("/order/recommend")
    public OutDTO getOrderMsgRecommend(@RequestBody InDto inDto) throws Exception {
        if (inDto.getUserId() == null) {
            return OutDTO.error500(MsgConstants.NEED_LOGIN);
        }
        return OutDTO.ok().put("giftMsg", groupActService.getGiftMsg(inDto.getUserId()));
    }

    public int getUserMsgUnReadeCount(Integer userId) {
        //未读消息
        TzySysNoticeRecord noticeRecord = new TzySysNoticeRecord();
        noticeRecord.setToUserid(userId.longValue());
        return noticeRecordService.selectUnreaderTotal(noticeRecord);
    }

    private int getUserReceiveCount(Integer userId) {
        //有无赠送卡密
        String sql = QuerySql.getQuerySql("com.tzy.sportcard.api.domain.MineCount.getReceiveOrderCount");
        String roCount = preparedSql.selectFun(sql, new Object[]{userId});
        return Integer.parseInt(roCount);
    }

    @ApiLog(title = "用户配置", businessType = BusinessType.SEARCH, indto = "{{inDto}}")
    @PostMapping("/config")
    public OutDTO getUserConfig(@RequestBody InDto inDto) {
        if(inDto.getUserId()==null){
            return OutDTO.error500("参数缺失!");
        }
        AppUserInfoDto user = mineApiService.checkUserNew(inDto);
        UserConfigDTO config= mineApiService.getUserConfig(inDto.getUserId());
        if (StringUtils.isNotEmpty(config.getEffectsType()) && config.getEffectsType().contains(Constants.LIVE_EFFECTS)
                && !memberBenefitsService.checkUseHasBenefitsByType(user.getMemberLevel(), Constants.LIVE_EFFECTS)) {
            config.setEffectsType(config.getEffectsType().replace(Constants.LIVE_EFFECTS, ""));
        }
        SimpleBenefitsDTO result = memberBenefitsService.querySimpleBenefits(inDto.getUserId());
        return OutDTO.ok().put("userConfig",config).put("result", result);
    }


    /**
     * @return
     */
    @ApiLog(title = "修改个人信息V2", businessType = BusinessType.UPDATE, indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/edit/user")
    public OutDTO editUserV2(@RequestBody InDto inDto) throws Exception {
        AppUserInfoDto existUser = mineApiService.checkUserNew(inDto);
        AppUserParam param = inDto.buildParam(new AppUserParam());
        // 去空格
        if (StringUtils.isNotEmpty(param.getNickname())){
            param.setNickname(param.getNickname().replaceAll("\\s+", ""));
            if(!QiniuUtil.checkText(param.getNickname())) {
                return OutDTO.error500("存在不合规的文字!");
            }
        }
        com.tzy.app.domain.AppBaseUser user = new com.tzy.app.domain.AppBaseUser();
        BeanUtils.copyBeanNonProp(param,user);
        user.setId(inDto.getUserId().longValue());
        if (user.getEffectsType() != null) {
            String effectsTypeStr = user.getEffectsType();
            if (effectsTypeStr.contains(Constants.LIVE_EFFECTS)) {
                if (!memberBenefitsService.checkUseHasBenefitsByType(existUser.getMemberLevel(), Constants.LIVE_EFFECTS)) {
                    return OutDTO.error500("该会员等级不支持直播间特效");
                }
            }
        }
        if (existUser.getBlacklist() != 1) {
            if (!StringUtils.isEmpty(user.getNickname())) {
                List<SysDictData> data = commonCacheService.getCommonDictData(Constants.APP_NAME_BACK_LIST_CACHE, null, "1", Constants.APP_NAME_BACK_LIST_CACHE, 600);
                if (!CollectionUtils.isEmpty(data)) {
                    Set<String> backNames = data.stream().map(SysDictData::getDictValue).collect(Collectors.toSet());
                    if (backNames.contains(user.getNickname())) {
                        return OutDTO.error500("昵称不可用!");
                    }
                }
                // 用户昵称不能与商家名称相同
                boolean available = tzyMerchantInfoService.checkNickname(user.getNickname());
                if(!available){
                    return OutDTO.error500("昵称不能与商家同名!");
                }
            }
        }
        String key = LocalDate.now() + "_" + inDto.getUserId();
        if (StringUtils.isNotEmpty(user.getPayAmountOfDay())) {
            redisUtils.hdel(Constants.USER_PAY_REMIND, key);
        }
        if (StringUtils.isNotEmpty(user.getOpenPayAmount())) {
            redisUtils.hdel(Constants.USER_PAY_REMIND, key);
        }
        appBaseUserService.updateAppBaseUser(user);
        redisUtils.del("app_cache_user_cfg:"+inDto.getUserId());
        return OutDTO.ok().put("userConfig",mineApiService.getUserConfig(inDto.getUserId()));
    }

    /**
     * 用户实名认证
     * @return
     */
    @ApiLog(title = "修改个人信息V2", businessType = BusinessType.UPDATE, indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/user/verify")
    @ApiLimitRule(seconds = 2, limitCount = 1, type = LimitRule.USER, msg = "操作频繁,请稍后再试!")
    public OutDTO userFaceVerifyV2(@RequestBody InDto inDto) throws Exception {
        AppUserInfoDto existUser = mineApiService.checkUserNew(inDto);
        Integer userId =  inDto.getIntegerParam("userId");
        Integer faceVerify =  inDto.getIntegerParam("faceVerify");
        if (userId != null && userId != existUser.getId().intValue()) {
            return OutDTO.error500("参数错误!");
        }
        if (faceVerify == null) {
            return OutDTO.error500("参数缺失!");
        }
        if (StringUtils.isEmpty(existUser.getUserCertData())) {
            return OutDTO.error500("实名异常,请重试!");
        }
        com.tzy.app.domain.AppBaseUser user = new com.tzy.app.domain.AppBaseUser();
        user.setId(inDto.getUserId().longValue());
        String userCertData = AESUtil.decryptData(existUser.getUserCertData(), Constants.AES_USER_PASSWORD);
        JSONObject jsonObject = JSON.parseObject(userCertData);
        String certNo = jsonObject.getString("certNo");
        String certName = jsonObject.getString("certName");
        String signStr = certName.trim() + certNo.trim();
        if (userId != null) {
            signStr += userId.toString().trim();
        }
        String sign = AESUtil.encryptData(signStr, Constants.AES_TOKEN_PASSWORD);
        if (!sign.equalsIgnoreCase(inDto.getSign())) {
            return OutDTO.error500("参数错误!");
        }
        //限制次数
        String signNo = DigestUtils.md5DigestAsHex(certNo.getBytes());
        int existNoNum = mineApiService.getExistUserNum(signNo);
        if (existNoNum > idCardMaxNum) {
            return OutDTO.error500("您绑定的账号已超过上限!");
        }

        jsonObject.put("status", "1");
        user.setUserCertData(AESUtil.encryptData(JSON.toJSONString(jsonObject), Constants.AES_USER_PASSWORD));
        user.setFaceVerify(1);
        int i = appBaseUserService.updateAppBaseUser(user);
        if(i>0){
            JSONObject appBaseUser = new JSONObject();
            appBaseUser.put("id", user.getId());
            // MD5身份证号加密
            appBaseUser.put("idCard", user.getIdCard());
            appBaseUser.put("userCertData", user.getUserCertData());
            //如果更新成功则进行发送mq 同步数据
            log.info("用户实名认证成功 ,发送mq->QUEUE_USER_REALNESS_PASS,用户ID:{}",inDto.getUserId());
            rabbitTemplate.convertAndSend(MqConstans.QUEUE_USER_REALNESS_PASS, com.alibaba.fastjson2.JSON.toJSONString(appBaseUser));
        }
        redisUtils.del("app_cache_user_cfg:"+inDto.getUserId());
        return OutDTO.ok().put("userConfig", mineApiService.getUserConfig(inDto.getUserId()));
    }

    /**
     * 可进行实名认证类型
     * @return
     */
    @ApiLog(title = "可进行实名认证类型", businessType = BusinessType.SEARCH, indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/canVerifyType")
    public OutDTO canVerifyType(@RequestBody InDto inDto) {
        //根据token获取用户信息
        AppUserInfoDto existUser = mineApiService.checkUserNew(inDto);
        return mineApiService.checkUserVerifyType(existUser);
    }

    /**
     * 已经实名认证类型
     * @return
     */
    @ApiLog(title = "已经实名认证类型", businessType = BusinessType.SEARCH, indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/hasVerifyType")
    public OutDTO hasVerifyType(@RequestBody InDto inDto) {
        //根据token获取用户信息
        AppUserInfoDto existUser = mineApiService.checkUserNew(inDto);
        return mineApiService.userHasVerifyType(existUser);
    }

    @ApiLog(title = "获取用户关注情况",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ApiOperation("获取用户关注情况")
    @ResponseBody
    @PostMapping(value = "/follow/type")
    public OutDTO getUserFollowByType(@RequestBody InDto inDto) {
        Integer userId = inDto.getIntegerParam("userId");
        Integer followId = inDto.getIntegerParam("followId");
        String followType = inDto.getString("followType");
        if (userId == null || followId == null || StringUtils.isEmpty(followType)) {
            return OutDTO.error500("参数为空");
        }
        AppMerchantFans condition = new AppMerchantFans();
        condition.setUserId(userId.longValue());
        condition.setCollectionTypeId(followId.longValue());
        condition.setType(followType);
        List<AppMerchantFans> fansList = suidRich.select(condition,"type");
        int followCount=mineApiService.getFollowCountByType(followType,followId);
        return OutDTO.ok().put("hasFollow",!fansList.isEmpty()).put("followCount",followCount);
    }

    @ApiLog(title = "编辑用户关注情况",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ApiOperation("编辑用户关注情况")
    @ResponseBody
    @PostMapping(value = "/edit/follow/type")
    public OutDTO editUserFollowByType(@RequestBody InDto inDto) {
        Integer userId = inDto.getIntegerParam("userId");
        Integer followId = inDto.getIntegerParam("followId");
        String followType = inDto.getString("followType");
        if (userId == null || followId == null || StringUtils.isEmpty(followType)) {
            return OutDTO.error500("参数为空");
        }
        Integer dirType = inDto.getIntegerParam("dirType");
        AppMerchantFans condition = new AppMerchantFans();
        condition.setUserId(userId.longValue());
        condition.setCollectionTypeId(followId.longValue());
        condition.setType(followType);
        if(dirType==null||dirType==1){
            List<AppMerchantFans> fansList = suidRich.select(condition, "id");
            if(!CollectionUtils.isEmpty(fansList)){
                return OutDTO.error500("您已关注,请勿重复点击");
            }
            condition.setCreateTime(DateUtils.getTimestampNow());
            suidRich.insert(condition);
        }else {
            suidRich.delete(condition);
        }
        int followCount=mineApiService.getFollowCountByType(followType,followId);
        editRefNum(followType, followId, followCount);
        return OutDTO.ok().put("followCount",followCount);
    }

    private void editRefNum(String followType, Integer followId, int followCount) {
        if (Constants.FOLLOW_TOP_CARD_LIKE.equals(followType)) {
            MerchantActGoods merchantActGoods = new MerchantActGoods();
            merchantActGoods.setId(followId.longValue());
            merchantActGoods.setLikeNum(followCount);
            suidRich.update(merchantActGoods);
        }
    }


    /**
     * 查询订单精美卡片数量
     *
     * @param inDto
     * @return
     */
    @ApiLog(title = "查询订单精美卡片数量v3", businessType = BusinessType.SEARCH, indto = "{{inDto}}")
    @ResponseBody
    @ApiVersion(value = 2.0)
    @ApiOperation("查询订单精美卡片数量v3")
    @PostMapping(value = "/gift/card")
    public OutDTO getUserGiftCard(@RequestBody InDto inDto) {
        List<SimpleUserMerchant> merchants = orderInfoService.getUserGiftCard(inDto.getUserId(),inDto.getPageSize(),inDto.getOffset());
        return OutDTO.ok().put("merchants",merchants);
    }


    /**
     * 生成精美卡片运费订单v2
     *
     * @param inDto
     * @return
     */
    @ApiLog(title = "生成精美卡片运费订单v3", businessType = BusinessType.SEARCH, indto = "{{inDto}}")
    @ResponseBody
    @ApiVersion(value = 2.3)
    @ApiOperation("生成精美卡片运费订单v3,参数:merchantId,addressId")
    @PostMapping(value = "/gift/card/prepay")
    @ApiLimitRule(seconds = 1,limitCount=1,type= LimitRule.USER,msg="操作频繁,请稍后再试!")
    public OutDTO shippingPrePayV3(@RequestBody InDto inDto) {
        Integer merchantId = inDto.getIntegerParam("merchantId");
        Integer addressId = inDto.getIntegerParam("addressId");
        Integer cardId = inDto.getIntegerParam("cardId");
        Integer shippingNum = inDto.getIntegerParam("shippingNum");
        if (merchantId == null || addressId == null || cardId == null || shippingNum == null || shippingNum < 1) {
            return OutDTO.error500("参数为空!");
        }
        List<GiftCardManage> giftCards = merchantApiService.selectGiftCard(merchantId, cardId);
        if (CollectionUtils.isEmpty(giftCards)) {
            return OutDTO.error500("精美卡片类型不存在!");
        }
        GiftCardManage giftCard=giftCards.get(0);
        if (shippingNum > giftCard.getMaxCardNum()) {
            return OutDTO.error500("数据提交异常!");
        }
        AppUserInfoDto user = mineApiService.checkUserNew(inDto);
        BigDecimal totalCost = BigDecimal.ZERO;
        if (shippingNum < giftCard.getMinCardNum()) {
            totalCost = getShippingCost(shippingNum, cardId,merchantId);
        }
        AppUserPoint shipRecord = new AppUserPoint().setUserId(user.getId()).setType(Constants.POINT_TYPE_GIFT_CARD_PRE + cardId)
                .setMerchantId(merchantId).setRefId(cardId);
        return orderInfoService.shippingPayV3(shipRecord, totalCost, shippingNum, inDto.getString("payType"), addressId);
    }


    /**
     * 精美卡片初始化
     *
     * @param inDto
     * @return
     */
    @ApiLog(title = "精美卡片初始化", businessType = BusinessType.SEARCH, indto = "{{inDto}}")
    @ResponseBody
    @ApiVersion(value = 2.0)
    @ApiOperation("精美卡片初始化")
    @PostMapping(value = "/gift/card/init")
    public OutDTO initUserGiftCard(@RequestBody InDto inDto) {
        List<SysDictData> dictData = dictDataService.selectDictDataByCache(Constants.GROUP_GIFT_CARD, null, "1", true);
        if (CollectionUtils.isEmpty(dictData)) {
            return OutDTO.ok();
        }
        AppUserInfoDto appUserInfoDto = mineApiService.checkUserNew(inDto);
        // 初始化会员等级
        memberBenefitsService.initMemberBeneifts(appUserInfoDto.getMemberLevel(), appUserInfoDto.getId().longValue());
        String key = "init_card_key" + inDto.getUserId();
        boolean hasKey = redisUtils.hasKey(key);
        if (!hasKey && !redisUtils.hHasKey("init_user_gift_card", inDto.getUserId().toString())) {
            Map<String, String> param = ImmutableMap.of("userId", inDto.getUserId().toString());
            rabbitTemplate.convertAndSend(MqConstans.QUEUE_INIT_USER_GIFT_CARD, JSON.toJSONString(param));
        }
        if (!hasKey) {
            redisUtils.set(key, 1, 600);
        }
        return OutDTO.ok();
    }

    /**
     * 用户个人中心数量显示
     */
    @Deprecated
    @ApiLog(title = "用户个人中心数量显示",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/center/num")
    @ApiVersion(2.0)
    public OutDTO getUserCenterNum() {
        UserInfo user = UserUtils.getSimpleUserInfo();
        if (user == null) {
            return OutDTO.error500("请先登录!");
        }
        MineCount mineCount = mineApiService.selectUserCenterCount(user.getId(), null);
        return OutDTO.ok().put("mineCount", mineCount);
    }

    /**
     * 用户个人中心数量显示
     */
    @ApiLog(title = "用户个人中心数量显示",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/center/num")
    @ApiVersion(3.8)
    public OutDTO getUserCenterNumV2() {
        UserInfo user = UserUtils.getSimpleUserInfo();
        if (user == null) {
            return OutDTO.error500("请先登录!");
        }
        InDto inDto = new InDto();
        inDto.setUserId(user.getId());
        AppUserInfoDto appUserInfoDto = mineApiService.checkUserNew(inDto);
        MineCount mineCount = mineApiService.selectUserCenterCount(user.getId(), appUserInfoDto);
        int followMerchantsCount=merchantApiService.getFollowMerchants(user.getId());
        int mineGoodsFavoriteCount=mineApiService.getGoodsFavoriteCount(user.getId());
        return OutDTO.ok().put("mineCount", mineCount).put("followMerchantsCount",followMerchantsCount).put("mineFavoriteGoodsCount",mineGoodsFavoriteCount);
    }

    /**
     * 商家个人中心数量显示
     */
    @ApiLog(title = "商家个人中心数量显示",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @ApiVersion(2.0)
    @PostMapping(value = "/merchant/center/num")
    @RequireRoles(value = {UserType.USER_ROLE_SHIPPING, UserType.USER_ROLE_DELIVERY, UserType.USER_ROLE_PUBLICITY, UserType.USER_ROLE_LIVING})
    public OutDTO getMerchantCenterNum() {
        UserInfo user = UserUtils.getSimpleUserInfo(true);
        if (user == null || user.getMerchantId() == null) {
            return OutDTO.error500("权限不足!");
        }
        MerchantGoods merchantCount=mineApiService.selectMerchantCenterCountV2(user.getMerchantId());
        MerchantInfo merchantInfo = merchantApiService.getMerchantInfo(user.getMerchantId());
        SimpleUser merchantAppUser = mineApiService.selectAppUserByMerchantId(user.getMerchantId());
        return OutDTO.ok().put("merchantCount",merchantCount).put("merchantInfo",merchantInfo).put("merchantAppUser",merchantAppUser);
    }

    /**
     * 用户申请自提
     */
    @ApiLog(title = "用户申请自提",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/apply/pickup/code")
    @ApiVersion(2.0)
    @ApiLimitRule(seconds = 1, limitCount = 1, type = LimitRule.USER, msg = "操作频繁,请稍后再试!")
    public OutDTO applyPickUpCode(@RequestBody InDto inDto) {
        UserInfo user = UserUtils.getSimpleUserInfo();
        if (user == null) {
            return OutDTO.error500("请先登录!");
        }
        Integer merchantId = inDto.getIntegerParam("merchantId");
        if (merchantId == null) {
            return OutDTO.error500("参数缺失!");
        }
        String code =RandomUtil.random(8);
        int count=merchantApiService.applySelfPickUpCode(user.getId(),merchantId,code);
        return OutDTO.ok().put("count", count).put("code", code);
    }

    /**
     * 商家核销用户码
     */
    @ApiLog(title = "商家核销用户码",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/verify/pickup/code")
    @ApiVersion(2.0)
    @RequireRoles(value = {UserType.USER_ROLE_SHIPPING, UserType.USER_ROLE_DELIVERY})
    @ApiLimitRule(seconds = 1, limitCount = 1, type = LimitRule.USER, msg = "操作频繁,请稍后再试!")
    public OutDTO verifyPickUpCode(@RequestBody InDto inDto) throws ParseException {
        UserInfo user = UserUtils.getSimpleUserInfo();
        if (user == null||user.getMerchantId()==null) {
            return OutDTO.error500("权限不足!");
        }
        String code = inDto.getString("code");
        if(StringUtils.isEmpty(code)){
            return OutDTO.error500("自提码为空!");
        }
        Integer merchantId = user.getMerchantId();
        CardGroupOrderInfo condition = new CardGroupOrderInfo();
        condition.setMerchantId(merchantId.longValue());
        condition.setStatus(103L);
        condition.setSelfPickupCode(code);
        List<CardGroupOrderInfo> orders = suidRich.select(condition);
        if (CollectionUtils.isEmpty(orders)) {
            return OutDTO.error500("自提码无效!");
        }
        if (new Date().getTime() - orders.get(0).getSelfPickupTime().getTime() > DateUtils.day2second * 1000) {
            return OutDTO.error500("自提码已过期!");
        }
        int count = merchantApiService.verifyPickUpCode(merchantId, code);
        if (count == 0) {
            return OutDTO.error500("自提码无效!");
        }
        Set<Long> mallOrderIds = orders.stream().filter(o -> Constants.ORDER_TYPE_SHOP.equals(o.getOrderType())).map(CardGroupOrderInfo::getId).collect(Collectors.toSet());
        if(!CollectionUtils.isEmpty(mallOrderIds)){
            Date afterNumWorkDay = DateUtils.getNumWorkDayByNow(new Date(), 5);
            com.tzy.sportcard.group.domain.CardGroupOrderInfo updateOrder = new com.tzy.sportcard.group.domain.CardGroupOrderInfo();
            updateOrder.setClosePaymentStatus(100);
            updateOrder.setClosePaymentTime(afterNumWorkDay);
            orderInfoService.updateOrderByIds(updateOrder, mallOrderIds);
        }
        orders.stream().filter(o -> Constants.ORDER_TYPE_SHOP.equals(o.getOrderType()) && o.getGiveOrderId() == null)
                .forEach(order -> asyncAppService.addPointByMallOrder(orderInfoService.selectCardGroupOrderInfoById(order.getId())));
        // 商城订单完结
        /*orders.stream().filter(o -> Constants.ORDER_TYPE_SHOP.equals(o.getOrderType()) && o.getGiveOrderId() == null)
                .forEach(order -> {
                    Map<String, Object> param = ImmutableMap.of("type", Constants.TYPE_SHOP_POINT, "orderId", order.getId());
                    MqUtils.sendMq(MqConstans.QUEUE_ORDER_OVER, JSONTools.obj2json(param));
                });*/
        return OutDTO.ok().put("count", count);
    }

    /**
     * 用户申请积分退款
     */
    @ApiLog(title = "用户申请积分退款",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/point/refund")
    @ApiVersion(2.0)
    @ApiLimitRule(seconds = 1, limitCount = 1, type = LimitRule.USER, msg = "操作频繁,请稍后再试!")
    public OutDTO pointRefund(@RequestBody InDto inDto){
        UserInfo user = UserUtils.getSimpleUserInfo();
        if (user == null) {
            return OutDTO.error500("请先登录!");
        }
        PointRefundRecord param = inDto.buildParam(new PointRefundRecord());
        if (param.getMerchantId() == null || StringUtils.isEmpty(param.getPointType())) {
            return OutDTO.error500("参数为空!");
        }
        param.setUserId(user.getId());
        mineApiService.pointRefund(param);
        return OutDTO.ok();
    }

    /**
     * 用户申请积分退款前置校验
     */
    @ApiLog(title = "用户申请积分退款前置校验",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/point/refund/check")
    @ApiVersion(2.0)
    @ApiLimitRule(seconds = 1, limitCount = 1, type = LimitRule.USER, msg = "操作频繁,请稍后再试!")
    public OutDTO checkPointRefund(@RequestBody InDto inDto) {
        UserInfo user = UserUtils.getSimpleUserInfo();
        if (user == null) {
            return OutDTO.error500("请先登录!");
        }
        Integer merchantId = inDto.getIntegerParam("merchantId");
        if(merchantId==null){
            return OutDTO.error500("商家id为空!");
        }
        RefundCheckDTO checkDTO=mineApiService.checkPointRefund(user.getId(),merchantId);
        return OutDTO.ok().put("checkResult",checkDTO);
    }

    /**
     * 用户审批商家退款申请
     */
    @ApiLog(title = "用户审批商家退款申请",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/check/refund/req")
    @ApiVersion(2.0)
    @ApiLimitRule(seconds = 1, limitCount = 1, type = LimitRule.USER, msg = "操作频繁,请稍后再试!")
    public OutDTO checkRefundReq(@RequestBody InDto inDto) {
        UserInfo user = UserUtils.getSimpleUserInfo(true);
        if (user == null) {
            return OutDTO.error500("请先登录!");
        }
        Integer groupInfoId = inDto.getIntegerParam("groupInfoId");
        if (groupInfoId == null) {
            return OutDTO.error500("groupInfoId为空!");
        }
        boolean checkFlag = inDto.getBooleanParam("checkFlag");
        int serverStatus = checkFlag ? 20 : 10;
        int count = orderInfoService.checkRefundReq(user.getId(), groupInfoId, serverStatus);
        if (count==0){
            //防止用户从多个入口发起多次请求
            return OutDTO.ok("您已处理过退款审批了");
        }
        groupReviewService.editRefRefundReqReview(groupInfoId, serverStatus, user.getId());
        //发送im修改小卡片状态

        noticeService.sendRefundChange(Long.valueOf(user.getId()), Long.valueOf(groupInfoId),checkFlag?1:2);
        return OutDTO.ok();
    }
    @PostMapping("/queryRefundStatus")
    @ResponseBody
    @ApiVersion(1.0)
    @ApiOperation(value = "用户审批退款状态查询",notes = "")
    public AjaxResult queryRefundStatus(@RequestBody List<Long> idList){
        log.info("用户退款审批状态查询参数：{}",idList);
        List<OrderChangeRecord> orderChangeRecord = orderChangeRecordService.getOrderChangeRecord(idList);
        List<HashMap<String,Object>> result=new ArrayList<>();
        for (OrderChangeRecord changeRecord : orderChangeRecord) {
            HashMap<String, Object> item = new HashMap<>();
            item.put("id",changeRecord.getId());
            String refundStatus="";
            int status=changeRecord.getStatus();
            if (status==0){
                refundStatus="pending";
            }else if (status==1){
                refundStatus="agree";
            }else if (status==2){
                refundStatus="refuse";
            }else if (status==3){
                refundStatus="pass";
            }
            item.put("refundStatus",refundStatus);
            result.add(item);
        }
        return AjaxResult.success(result);
    }
    @ApiLog(title = "用户拼豆订单",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/cost/card/merchant")
    @ApiVersion(1.0)
    @ApiLimitRule(seconds = 1, limitCount = 2, type = LimitRule.USER, msg = "操作频繁,请稍后再试!")
    public OutDTO userCostCardMerchant(@RequestBody InDto inDto) {
        UserInfo user = UserUtils.getSimpleUserInfo(true);
        AppAssert.notNull(user, MsgConstants.NEED_LOGIN);
        boolean payed = inDto.getBooleanParam("payed");
        List<SimpleMerchantInfo> merchants = orderInfoService.getUserCostCardMerchant(user.getId(), payed);
        return OutDTO.ok().put("merchants", merchants);
    }

    @ApiLog(title = "用户拼豆订单",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/cost/card/order")
    @ApiVersion(1.0)
    @ApiLimitRule(seconds = 1, limitCount = 2, type = LimitRule.USER, msg = "操作频繁,请稍后再试!")
    public OutDTO userCostCardOrders(@RequestBody InDto inDto) {
        UserInfo user = UserUtils.getSimpleUserInfo(true);
        AppAssert.notNull(user, MsgConstants.NEED_LOGIN);
        OrderQuery orderQuery = inDto.buildParam(new OrderQuery());
        orderQuery.setUserId(user.getId()).setPageNo(inDto.getPageNo()).setPageSize(inDto.getPageSize());
        PageInfo<CostOrderDTO> pageOrder = orderInfoService.userCostCardOrders(orderQuery);
        return OutDTO.ok().put("pageOrder", pageOrder);
    }

    @ApiLog(title = "拼豆订单运费下单",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/apply/card/cost")
    @ApiVersion(1.0)
    @ApiLimitRule(seconds = 5, limitCount = 1, type = LimitRule.USER, msg = "操作频繁,请稍后再试!")
    public OutDTO userCardShippingCost(@RequestBody InDto inDto) {
        UserInfo user = UserUtils.getSimpleUserInfo(true);
        AppAssert.notNull(user, MsgConstants.NEED_LOGIN);
        String ids = inDto.getString("ids");
        if (StringUtils.isEmpty(ids)) {
            return OutDTO.error500(MsgConstants.PARAM_EMPTY);
        }
        Integer merchantId = inDto.getIntegerParam("merchantId");
        if (merchantId == null) {
            return OutDTO.error500(MsgConstants.PARAM_EMPTY);
        }
        String payType = inDto.getString("payType");
        if (StringUtils.isEmpty(payType)) {
            return OutDTO.error500(MsgConstants.PARAM_EMPTY);
        }
        List<Long> idList = Arrays.stream(ids.split(",")).map(Long::valueOf).collect(Collectors.toList());
        if (idList.size() > 100) {
            return OutDTO.error500(MsgConstants.PARAM_EMPTY);
        }
        orderInfoService.checkOrderInfoStatus(idList);
        int totalNum = orderInfoService.getCardNum(user.getId(), idList);
        MerchantInfo merchant = merchantApiService.getMerchantInfo(merchantId);
        BigDecimal shippingCost;
        if (StringUtils.isNotEmpty(merchant.getShippingCostConfig())) {
            MerchantCostConfig costConfig = JSONTools.jsonStr2obj(merchant.getShippingCostConfig(), MerchantCostConfig.class);
            if (totalNum > costConfig.getMaxNum() || totalNum < costConfig.getMinNum()) {
                return OutDTO.error500(MsgConstants.MERCHANT_COND_MET);
            }
            shippingCost = MerchantCostConfig.buildMerchantShippingCost(costConfig, totalNum);
        } else {
            shippingCost = getShippingCost(totalNum, 1, merchantId);
        }
        Integer addressId = inDto.getIntegerParam("addressId");
        if (addressId != null) {
            TzyShippingAddress address = addressService.selectUserAddress(user.getId(), addressId);
            if (address == null) {
                return OutDTO.error500(MsgConstants.PARAM_EMPTY);
            }
            com.tzy.sportcard.group.domain.CardGroupOrderInfo editOrder = new com.tzy.sportcard.group.domain.CardGroupOrderInfo();
            editOrder.setShippingAddressId(address.getId());
            editOrder.setShippingAddressLinkname(address.getLinkname());
            editOrder.setShippingAddress(address.getAddress() + address.getAddressMore());
            editOrder.setShippingAddressPhone(address.getPhone());
            orderInfoService.updateOrderByIds(editOrder, new HashSet<>(idList));
        }
        ShippingPayParam payParam = new ShippingPayParam().setCardType(ShippingPayParam.CARD_TYPE_POINT).setUserId(user.getId()).setOrderIds(idList)
                .setTotalCost(shippingCost).setShippingNum(totalNum).setPayType(payType);
        ShippingPayResult payResult = orderInfoService.cardShippingPayNew(payParam);
        return OutDTO.ok().put("payResult", payResult);
    }

    @ApiLog(title = "拼豆订单运费下单",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/v2/apply/card/cost")
    @ApiVersion(1.0)
    @ApiLimitRule(seconds = 5, limitCount = 1, type = LimitRule.USER, msg = "操作频繁,请稍后再试!")
    public OutDTO userCardShippingCostV2(@RequestBody InDto inDto) {
        UserInfo user = UserUtils.getSimpleUserInfo(true);
        AppAssert.notNull(user, MsgConstants.NEED_LOGIN);
        String ids = inDto.getString("ids");
        if (StringUtils.isEmpty(ids)) {
            return OutDTO.error500(MsgConstants.PARAM_EMPTY);
        }
        Integer merchantId = inDto.getIntegerParam("merchantId");
        if (merchantId == null) {
            return OutDTO.error500(MsgConstants.PARAM_EMPTY);
        }
        String payType = inDto.getString("payType");
        if (StringUtils.isEmpty(payType)) {
            return OutDTO.error500(MsgConstants.PARAM_EMPTY);
        }
        List<Long> idList = Arrays.stream(ids.split(",")).map(Long::valueOf).collect(Collectors.toList());
        if (idList.size() > 100) {
            return OutDTO.error500(MsgConstants.PARAM_EMPTY);
        }
        orderInfoService.checkOrderInfoStatus(idList);
        int totalNum = orderInfoService.getCardNum(user.getId(), idList);
        MerchantInfo merchant = merchantApiService.getMerchantInfo(merchantId);
        BigDecimal shippingCost;
        if (StringUtils.isNotEmpty(merchant.getShippingCostConfig())) {
            MerchantCostConfig costConfig = JSONTools.jsonStr2obj(merchant.getShippingCostConfig(), MerchantCostConfig.class);
            if (totalNum > costConfig.getMaxNum() || totalNum < costConfig.getMinNum()) {
                return OutDTO.error500(MsgConstants.MERCHANT_COND_MET);
            }
            shippingCost = MerchantCostConfig.buildMerchantShippingCost(costConfig, totalNum);
        } else {
            shippingCost = getShippingCost(totalNum, 1, merchantId);
        }
        Integer addressId = inDto.getIntegerParam("addressId");
        if (addressId != null) {
            TzyShippingAddress address = addressService.selectUserAddress(user.getId(), addressId);
            if (address == null) {
                return OutDTO.error500(MsgConstants.PARAM_EMPTY);
            }
            com.tzy.sportcard.group.domain.CardGroupOrderInfo editOrder = new com.tzy.sportcard.group.domain.CardGroupOrderInfo();
            editOrder.setShippingAddressId(address.getId());
            editOrder.setShippingAddressLinkname(address.getLinkname());
            editOrder.setShippingAddress(address.getAddress() + address.getAddressMore());
            editOrder.setShippingAddressPhone(address.getPhone());
            orderInfoService.updateOrderByIds(editOrder, new HashSet<>(idList));
        }
        ShippingPayParam payParam = new ShippingPayParam().setCardType(ShippingPayParam.CARD_TYPE_POINT).setUserId(user.getId()).setOrderIds(idList)
                                                          .setTotalCost(shippingCost).setShippingNum(totalNum).setPayType(payType);

        ShippingPayResult payResult = orderInfoService.cardShippingPayV2(payParam);
        return OutDTO.ok().put("payResult", payResult);
    }
    @ApiLog(title = "提醒-用户点击我的页面",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/recommend")
    @ApiVersion(1.0)
    @ApiLimitRule(seconds = 1, limitCount = 2, type = LimitRule.USER, msg = "操作频繁,请稍后再试!")
    public OutDTO getUserCenterRecommend(@RequestBody InDto inDto) {
        UserInfo user = UserUtils.getSimpleUserInfo(true);
        AppAssert.notNull(user, MsgConstants.NEED_LOGIN);
        OutDTO dto = OutDTO.ok();
        if (user.getMerchantId() != null) {
            dto.put("sendCard", orderInfoService.selectMerchantNoticePointOrder(user.getMerchantId()));
        }
        String curDayKey = Constants.REFUSE_CARD_NOTICE + DateUtils.getDate() + user.getId();
        boolean isApplyShipment = false;
        if (!redisUtils.hasKey(curDayKey)) {
            redisUtils.set(curDayKey, "1", Constants.dayForSecond);
            boolean isRefuseNotice = redisUtils.hasKey(Constants.REFUSE_CARD_NOTICE + user.getId());
            isApplyShipment = !isRefuseNotice && orderInfoService.selectUserNoticePointOrder(user.getId());

        }
        dto.put("applyShipment", isApplyShipment);
        return dto;
    }

    @ApiLog(title = "提醒-用户点击我的页面",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/refuse/notice")
    @ApiVersion(1.0)
    @ApiLimitRule(seconds = 1, limitCount = 2, type = LimitRule.USER, msg = "操作频繁,请稍后再试!")
    public OutDTO refuseCardNotice(@RequestBody InDto inDto) {
        UserInfo user = UserUtils.getSimpleUserInfo(true);
        AppAssert.notNull(user, MsgConstants.NEED_LOGIN);
        String type = inDto.getString("type");
        if (StringUtils.isEmpty(type)) {
            redisUtils.set(Constants.REFUSE_CARD_NOTICE + user.getId(), "1", Constants.dayForSecond * 30);
        }
        return OutDTO.ok();
    }

    @ApiLog(title = "更新个人资料",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/edit/detail")
    public OutDTO editUserDetail(@RequestBody AppUserDetailInfo appUserDetailInfo) {
        if(appUserDetailInfo == null){
            return OutDTO.error500("参数为空");
        }
        UserInfo user = UserUtils.getSimpleUserInfo(true);
        AppAssert.notNull(user, MsgConstants.NEED_LOGIN);
        appUserDetailInfo.setUserId(user.getId().longValue());
        int rs = appUserDetailInfoService.update(appUserDetailInfo);
        if(rs == 0){
            return OutDTO.error500("更新失败");
        }
        return OutDTO.ok();
    }

    @ApiLog(title = "更新个人资料",businessType = BusinessType.SEARCH,indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/user/detail")
    public OutDTO getUserDetail() {
        UserInfo user = UserUtils.getSimpleUserInfo(true);
        AppAssert.notNull(user, MsgConstants.NEED_LOGIN);
        AppUserDetailInfo appUserDetailInfo = appUserDetailInfoService.getByUserId(user.getId());
        return OutDTO.ok().put("user", appUserDetailInfo);
    }

    @ApiLog(title = "用户填写邀请码",businessType = BusinessType.UPDATE,indto = "{{inDto}}")
    @ResponseBody
    @PostMapping(value = "/user/invited")
    @ApiLimitRule(seconds = 1, limitCount = 2, type = LimitRule.COMMON, msg = "操作频繁,请稍后再试!")
    public OutDTO fillUserInviteCode(@RequestBody AppUserDetailInfo appUserDetailInfo) {
        UserInfo user = UserUtils.getSimpleUserInfo(true);
        AppAssert.notNull(user, MsgConstants.NEED_LOGIN);
        // 检查appUserDetailInfo.getInviteCode() 不能为空 且长度为6位
        if(StringUtils.isEmpty(appUserDetailInfo.getInviteCode())){
            return OutDTO.error500("邀请码不能为空");
        }

        if(appUserDetailInfo.getInviteCode().length() != 6){
            return OutDTO.error500("邀请码长度必须为6位");
        }
        String inviteCode = appUserDetailInfo.getInviteCode();
        AppUserDetailInfo parentUser = appUserDetailInfoService.getByUserInviteCode(appUserDetailInfo.getInviteCode());
        if(parentUser == null){
            return OutDTO.error500("无效的邀请码");
        }
        appUserDetailInfo = appUserDetailInfoService.updateInviteCode(user,appUserDetailInfo);
        List<CouponInfo> coupons = appUserDetailInfoService.sendInviteCodeComplete(appUserDetailInfo,inviteCode,parentUser);

        return OutDTO.ok().put("user", appUserDetailInfo).put("parentUser",parentUser).put("coupons", coupons);
    }
}
