package com.ruoyi.ss.user.service.impl;

import com.github.pagehelper.PageHelper;
import com.ruoyi.common.auth.ali.AliAuthService;
import com.ruoyi.common.auth.wx.WxAuthService;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.domain.entity.SmUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.core.redis.RedisLock;
import com.ruoyi.common.core.redis.enums.RedisLockKey;
import com.ruoyi.common.enums.UserType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.common.utils.qiniu.QiNiuUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.common.valid.face.FaceResponseData;
import com.ruoyi.common.valid.face.FaceUtils;
import com.ruoyi.common.valid.face.enums.FaceIncorrect;
import com.ruoyi.common.valid.face.enums.FaceScoreResult;
import com.ruoyi.common.valid.liveness.LivenessResponseQueryData;
import com.ruoyi.common.valid.liveness.LivenessResponseTokenData;
import com.ruoyi.common.valid.liveness.LivenessUtils;
import com.ruoyi.common.valid.liveness.enums.LivenessQueryResult;
import com.ruoyi.ss.device.domain.DeviceQuery;
import com.ruoyi.ss.device.domain.vo.DeviceVO;
import com.ruoyi.ss.device.service.DeviceService;
import com.ruoyi.ss.realName.domain.RealName;
import com.ruoyi.ss.realName.domain.enums.RealNameType;
import com.ruoyi.ss.realName.service.RealNameService;
import com.ruoyi.ss.realName.service.RealNameValidator;
import com.ruoyi.ss.recordBalance.domain.enums.RecordBalanceBstType;
import com.ruoyi.ss.recordBalance.service.RecordBalanceService;
import com.ruoyi.ss.risk.domain.enums.RiskSubmitType;
import com.ruoyi.ss.risk.service.RiskService;
import com.ruoyi.ss.riskInfo.domain.RiskInfoVO;
import com.ruoyi.ss.riskInfo.service.RiskInfoService;
import com.ruoyi.ss.store.domain.StoreQuery;
import com.ruoyi.ss.store.domain.StoreVo;
import com.ruoyi.ss.store.service.StoreService;
import com.ruoyi.ss.user.domain.SmUserQuery;
import com.ruoyi.ss.user.domain.SmUserVO;
import com.ruoyi.ss.user.domain.dto.UserFaceDTO;
import com.ruoyi.ss.user.domain.dto.UserRealNameDTO;
import com.ruoyi.ss.user.domain.vo.UserRealNameVO;
import com.ruoyi.ss.user.domain.vo.UserRealRefreshVO;
import com.ruoyi.ss.user.mapper.SmUserMapper;
import com.ruoyi.ss.user.service.ISmUserService;
import com.ruoyi.ss.user.service.UserValidator;
import com.ruoyi.system.domain.enums.config.ConfigKey;
import com.ruoyi.system.domain.enums.verificationCode.CodeBusinessType;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.IVerificationCodeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 普通用户信息Service业务层处理
 *
 * @author qzz
 * @date 2024-01-24
 */
@Service
@Slf4j
public class SmUserServiceImpl implements ISmUserService
{
    @Autowired
    private SmUserMapper smUserMapper;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private DeviceService deviceService;

    @Autowired
    private StoreService storeService;

    @Autowired
    private RecordBalanceService recordBalanceService;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private RealNameService realNameService;

    @Autowired
    private ISysConfigService sysConfigService;

    @Autowired
    private IVerificationCodeService verificationCodeService;

    @Autowired
    private UserValidator userValidator;

    @Autowired
    private RiskInfoService riskInfoService;

    // 活体检测返回地址
    public static final String LIVENESS_RETURN_URL = SpringUtils.getRequiredProperty("liveness.returnUrl");

    @Autowired
    private WxAuthService wxAuthService;

    @Autowired
    private RiskService riskService;

    @Autowired
    private RedisLock redisLock;

    @Autowired
    private RealNameValidator realNameValidator;

    @Autowired
    private AliAuthService aliAuthService;

    /**
     * 查询普通用户信息
     *
     * @param userId 普通用户信息主键
     * @return 普通用户信息
     */
    @Override
    public SmUserVO selectSmUserByUserId(Long userId) {
        return smUserMapper.selectSmUserByUserId(userId);
    }

    /**
     * 查询普通用户信息列表
     *
     * @param dto 查询条件
     * @return 普通用户信息
     */
    @Override
    public List<SmUserVO> selectSmUserList(SmUserQuery dto)
    {
        List<SmUserVO> list = smUserMapper.selectSmUserList(dto);
        desensitization(list);
        return list;
    }

    /**
     * 数据脱敏
     * @param list
     */
    @Override
    public void desensitization(List<SmUserVO> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        list.forEach(item -> {
            if (item != null) {
                item.setPassword(null);
                item.setPayPassword(null);
            }
        });
    }

    @Override
    public int selectCount(SmUserQuery dto) {
        return smUserMapper.selectCount(dto);
    }

    @Override
    public SmUserVO selectUserByPhone(String phoneNumber) {
        if (phoneNumber == null) {
            return null;
        }
        SmUserQuery dto = new SmUserQuery();
        dto.setEqPhonenumber(phoneNumber);
        List<SmUserVO> list = smUserMapper.selectSmUserList(dto);
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        return list.get(0);
    }

    @Override
    public int changeType(Long userId, UserType userType) {
        // 修改数据库中的数据
        SmUser smUser = new SmUser();
        smUser.setUserId(userId);
        smUser.setType(userType.getType());
        return smUserMapper.updateSmUser(smUser);
    }

    @Override
    @Transactional
    public int addBalance(Long userId, BigDecimal amount, String reason, RecordBalanceBstType bstType, Long bstId) {
        if (userId == null || amount == null) {
            log.info("增加余额错误：userId 或 amount 为空");
            return 0;
        }
        ServiceUtil.assertion(BigDecimal.ZERO.compareTo(amount) > 0, "增加的金额不允许小于0");
        // 余额+0，则啥也不动
        if (BigDecimal.ZERO.compareTo(amount) == 0) {
            return 1;
        }

        // 查询用户
        SmUserVO user = selectSmUserByUserId(userId);

        // 修改余额
        int updateCount = smUserMapper.addBalance(userId, amount, user.getBalance());
        ServiceUtil.assertion(updateCount != 1, "增加用户余额失败，请重试");

        // 余额变动记录
        int record = recordBalanceService.record(userId, user.getBalance(), amount, reason, bstType, bstId);
        ServiceUtil.assertion(record != 1, "用户余额变动记录失败");

        return updateCount;
    }

    @Override
    @Transactional
    public int subtractBalance(Long userId, BigDecimal amount, boolean check, String reason, RecordBalanceBstType bstType, Long bstId) {
        if (userId == null || amount == null) {
            log.info("减少余额错误：userId 或 amount 为空");
            return 0;
        }
        ServiceUtil.assertion(BigDecimal.ZERO.compareTo(amount) > 0, "减少的金额需要大于0");
        // 余额-0，则啥也不动
        if (BigDecimal.ZERO.compareTo(amount) == 0) {
            return 1;
        }

        // 查询用户
        SmUserVO user = selectSmUserByUserId(userId);

        // 更新用户余额
        int updateCount = smUserMapper.subtractBalance(userId, amount, check, user.getBalance());
        ServiceUtil.assertion(updateCount != 1, "减少用户余额失败，请重试");

        // 余额变动记录
        int record = recordBalanceService.record(userId, user.getBalance(), amount.negate(), reason, bstType, bstId);
        ServiceUtil.assertion(record != 1, "用户余额变动记录失败");

        return updateCount;
    }


    @Override
    @Transactional
    public void updateWxOpenId(Long userId, String openId) {
        // 判断该openId是否已经被绑定
        SmUserVO repeatUser = smUserMapper.selectSmUserByWxOpenId(openId);
        ServiceUtil.assertion(repeatUser != null && !Objects.equals(repeatUser.getUserId(), userId), "该微信已被绑定");

        // 修改openId
        SmUser user = new SmUser();
        user.setUserId(userId);
        user.setWxOpenId(openId);
        smUserMapper.updateSmUser(user);
    }

    @Override
    public SmUserVO selectSimpleById(Long userId) {
        return smUserMapper.selectSimpleById(userId);
    }

    @Override
    public boolean logicDel(List<Long> userIds) {
        this.validatePreLogicDel(userIds);
        return smUserMapper.logicDelByIds(userIds);
    }

    @Override
    public boolean isExistUsers(List<Long> userIds) {
        if (CollectionUtils.isEmpty(userIds)) {
            return true;
        }
        Map<Long, SmUserVO> userMap = this.selectMapByUserIds(userIds);
        for (Long userId : userIds) {
            SmUserVO user = userMap.get(userId);
            if (user == null) {
                return false;
            }
        }
        return true;
    }

    @Override
    public SmUserVO selectSmUserByWxOpenId(String openid) {
        return smUserMapper.selectSmUserByWxOpenId(openid);
    }

    @Override
    @Transactional
    public boolean updatePassword(Long userId, String password) {
        ServiceUtil.assertion(userId == null || password == null, "参数错误：用户id或密码为空");
        SmUser user = new SmUser();
        user.setUserId(userId);
        user.setPassword(SecurityUtils.encryptPassword(password));
        int updateCount = smUserMapper.updateSmUser(user);
        ServiceUtil.assertion(updateCount != 1, "修改密码失败，请刷新后重试");
        return true;
    }

    /**
     * ids查询列表
     *
     * @param ids
     */
    @Override
    public List<SmUserVO> selectByIds(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return Collections.emptyList();
        }
        SmUserQuery query = new SmUserQuery();
        query.setUserIds(ids);
        return this.selectSmUserList(query);
    }

    /**
     * 将用户设置为商家
     *
     * @param userId
     */
    @Override
    public int setMch(Long userId) {
        if (userId == null) {
            return 0;
        }
        SmUser user = new SmUser();
        user.setUserId(userId);
        user.setIsMch(true);
        return smUserMapper.updateSmUser(user);
    }

    /**
     * 更新用户服务费
     *
     * @param data
     * @return
     */
    @Override
    public int updateServiceRate(SmUser data) {
        return smUserMapper.updateServiceRate(data);
    }

    @Override
    public int resetService(Long userId) {
        if (userId == null) {
            return 0;
        }
        SmUser data = new SmUser();
        data.setUserId(userId);
        return this.updateServiceRate(data);
    }

    @Override
    public BigDecimal selectSumOfBalance(SmUserQuery query) {
        return smUserMapper.selectSumOfBalance(query);
    }

    @Override
    public UserRealNameVO realName(UserRealNameDTO dto) {
        SmUserVO user = selectSmUserByUserId(dto.getUserId());
        ServiceUtil.assertion(user == null, "用户信息不存在");
        ServiceUtil.assertion(StringUtils.isBlank(dto.getRealName()), "用户姓名不允许为空");
        ServiceUtil.assertion(StringUtils.isBlank(dto.getRealIdCard()), "用户身份证不允许为空");

        // 普通实名，判断身份证是否重复
        if (RealNameType.NORMAL.getType().equals(dto.getType())) {
            realNameValidator.checkIdCard(user.getUserId(), dto.getRealIdCard());
        }

        // 获取活体检测token
        LivenessResponseTokenData tokenData = LivenessUtils.getToken(LIVENESS_RETURN_URL, null, null);
        ServiceUtil.assertion(tokenData == null || StringUtils.isBlank(tokenData.getToken()), "获取活体检测token失败");

        // 存入缓存:10分钟
        String cacheKey = CacheConstants.LIVENESS_TOKEN + dto.getUserId();
        UserFaceDTO faceDto = new UserFaceDTO();
        faceDto.setUserId(dto.getUserId());
        faceDto.setToken(tokenData.getToken());
        faceDto.setName(dto.getRealName());
        faceDto.setMobile(dto.getRealPhone());
        faceDto.setIdCard(dto.getRealIdCard());
        faceDto.setType(dto.getType());
        faceDto.setRiskInfoId(dto.getRiskInfoId());
        redisCache.setCacheObject(cacheKey, faceDto, 10, TimeUnit.MINUTES);

        // 返回数据
        UserRealNameVO vo = new UserRealNameVO();
        vo.setFaceToken(tokenData.getToken());
        vo.setJumpUrl(LivenessUtils.JUMP_URL + "?token=" + tokenData.getToken());
        return vo;

    }

    @Override
    public int readMchLicence(Long userId) {
        SmUser data = new SmUser();
        data.setUserId(userId);
        data.setReadMchLicence(true);
        return smUserMapper.updateSmUser(data);
    }

    @Override
    public int addRiskCount(Long userId, int count) {
        return smUserMapper.addRiskCount(userId, count);
    }

    @Override
    public UserRealRefreshVO refreshRealName(Long userId) {
        // 结果
        UserRealRefreshVO vo = new UserRealRefreshVO();
        vo.setPass(false);

        if (userId == null) {
            return vo;
        }

        // 通过userId获取用户提交的信息
        String cacheKey = CacheConstants.LIVENESS_TOKEN + userId;
        UserFaceDTO face = redisCache.getCacheObject(cacheKey);
        ServiceUtil.assertion(face == null, "token不存在");

        // 获取活体图像及三要素信息
        LivenessResponseQueryData livenessResult = LivenessUtils.queryResult(face.getToken());
        ServiceUtil.assertion(livenessResult == null, "活体信息不存在");
        ServiceUtil.assertion(!LivenessQueryResult.PASS.getResult().equals(livenessResult.getResult()), "活体检测未通过");
        ServiceUtil.assertion(StringUtils.isBlank(livenessResult.getFaceImage()), "活体检测未通过");

        // 将Base64上传到七牛云中
        String imageUrl = livenessResult.getFaceImage();
        try {
            imageUrl = QiNiuUtils.DOMAIN + "/" + QiNiuUtils.uploadBase64(imageUrl);
        } catch (Exception e) {
            throw new ServiceException("上传文件失败:" + e.getMessage());
        }

        // 判断活体是否匹配
        FaceResponseData faceIdb = FaceUtils.getFaceIdb(face.getIdCard(), face.getName(), livenessResult.getFaceImage(), null);
        ServiceUtil.assertion(faceIdb == null, "人证比对数据不存在");
        ServiceUtil.assertion(!FaceIncorrect.isSuccess(faceIdb.getIncorrect()), "人证比对未通过：" + faceIdb.getMsg());
        ServiceUtil.assertion(!FaceScoreResult.isSame(faceIdb.getScore()), "人证比对未通过，分数：" + faceIdb.getScore());

        // 通过，构造实名认证表
        RealName realName = new RealName();
        realName.setUserId(userId);
        realName.setName(face.getName());
        realName.setIdCard(face.getIdCard());
        realName.setMobile(face.getMobile());
        realName.setScore(faceIdb.getScore());
        realName.setFaceImage(imageUrl);
        realName.setType(face.getType());

        // 普通实名
        if (RealNameType.NORMAL.getType().equals(face.getType())) {
            int result = this.handleNormalRealName(realName, cacheKey);
            ServiceUtil.assertion(result != 1, "实名认证失败");
        }
        // 风控实名，修改用户风控信息
        else {
            int result = this.handleRiskRealName(realName, cacheKey, face.getRiskInfoId());
            ServiceUtil.assertion(result != 1, "风控实名失败");
        }

        vo.setRealNameType(face.getType());
        vo.setInfoId(face.getRiskInfoId());
        vo.setPass(true);

        return vo;
    }

    /**
     * 处理风控实名认证成功
     * @param realName 实名认证
     * @param cacheKey 缓存key
     */
    private int handleRiskRealName(RealName realName, String cacheKey, Long riskInfoId) {
        // 查询用户是否有风控信息
        RiskInfoVO info = riskInfoService.selectRiskInfoByInfoId(riskInfoId);
        ServiceUtil.assertion(info == null, "风控材料信息不存在");

        Integer result = transactionTemplate.execute(status -> {
            // 插入实名认证信息
            int insert = realNameService.insertRealName(realName);
            ServiceUtil.assertion(insert != 1, "新增实名认证失败");

            // 修改实名认证ID
            riskInfoService.updateRealName(riskInfoId, realName.getId());

            // 删除缓存
            redisCache.deleteObject(cacheKey);

            return insert;
        });

        return result == null ? 0 : result;
    }

    /**
     * 处理普通实名认证成功
     * @param realName 实名认证
     * @param cacheKey 缓存key
     */
    private int handleNormalRealName(RealName realName, String cacheKey) {

        // 修改实名认证信息（用户表）
        SmUser data = new SmUserQuery();
        data.setUserId(realName.getUserId());
        data.setRealName(realName.getName());
        data.setRealIdCard(realName.getIdCard());
        data.setRealPhone(realName.getMobile());
        data.setIsReal(true);

        // 数据库操作
        Integer result = transactionTemplate.execute(status -> {
            // 修改用户实名信息
            int update = smUserMapper.updateSmUser(data);
            ServiceUtil.assertion(update != 1, "更新用户信息失败");

            // 插入实名认证表
            int insert = realNameService.insertRealName(realName);
            ServiceUtil.assertion(insert != 1, "新增实名认证失败");

            // 删除缓存
            redisCache.deleteObject(cacheKey);

            return update;
        });

        return result == null ? 0 : result;
    }

    @Override
    public List<SmUserVO> selectByUserIds(List<Long> userIds) {
        SmUserQuery query = new SmUserQuery();
        query.setUserIds(userIds);
        return smUserMapper.selectSmUserList(query);
    }

    @Override
    public boolean getChangeMobileCodeOld(Long userId) {
        SmUserVO user = selectSmUserByUserId(userId);
        ServiceUtil.assertion(user == null, "用户不存在");
        ServiceUtil.assertion(StringUtils.isBlank(user.getPhonenumber()), "用户没有手机号，无法获取验证码");

        return verificationCodeService.sendMobileCode(user.getPhonenumber(), CodeBusinessType.CHANGE_MOBILE_OLD);
    }

    @Override
    public int updateMobile(Long userId, String mobile) {
        if (userId == null || StringUtils.isBlank(mobile)) {
            return 0;
        }

        Integer result = transactionTemplate.execute(status -> {
            SmUser data = new SmUser();
            data.setUserId(userId);
            data.setPhonenumber(mobile);
            int update = smUserMapper.updateSmUser(data);
            ServiceUtil.assertion(update != 1, "更新手机号失败");

            int phoneCount = this.selectCountByPhone(mobile);
            ServiceUtil.assertion(phoneCount > 1, "手机号重复，无法修改");

            return update;
        });

        return result == null ? 0 : result;
    }

    @Override
    public int bindWxMobile(Long userId, String mobileCode) {
        if (StringUtils.isBlank(mobileCode)) {
            log.warn("微信手机号临时授权码为空");
            return 0;
        }
        String mobile = wxAuthService.getWxPhoneNumber(mobileCode);
        ServiceUtil.assertion(StringUtils.isBlank(mobile), "获取微信手机号失败");
        return this.bindMobile(userId, mobile);
    }

    private int bindMobile(Long userId, String mobile) {
        if (StringUtils.isBlank(mobile) || userId == null) {
            return 0;
        }
        Integer result = transactionTemplate.execute(status -> {
            // 更新
            int update = smUserMapper.bindMobile(userId, mobile);
            ServiceUtil.assertion(update != 1, "绑定手机号失败，请刷新后重试");

            // 后校验
            SmUserVO vo = this.selectSmUserByUserId(userId);
            userValidator.checkMobile(vo.getUserId(), vo.getPhonenumber());

            return update;
        });

        return result == null ? 0 : result;
    }

    @Override
    public UserRealNameVO riskRealName(RiskInfoVO info) {
        UserRealNameDTO dto = new UserRealNameDTO();
        dto.setType(RealNameType.RISK.getType());

        ServiceUtil.assertion(info == null, "风控信息不存在");
        ServiceUtil.assertion(info.getRiskIsFinished() != null && info.getRiskIsFinished(), "当前风控已结束，无需实名认证");
        ServiceUtil.assertion(CollectionUtils.isEmpty(info.getRiskSubmitType())
                || !info.getRiskSubmitType().contains(RiskSubmitType.REAL_NAME.getCode()),
                "当前风控无需提交实名认证"
        );
        dto.setRiskInfoId(info.getInfoId());
        dto.setUserId(info.getUserId());

        SmUserVO user = this.selectSmUserByUserId(info.getUserId());
        if (user.getIsReal()) {
            if (user.getRealName() != null) {
                dto.setRealName(user.getRealName());
            }
            if (user.getRealPhone() != null) {
                dto.setRealPhone(user.getRealPhone());
            }
            if (user.getRealIdCard() != null) {
                dto.setRealIdCard(user.getRealIdCard());
            }
        }
        return this.realName(dto);
    }

    /**
     * @param openId
     * @return
     */
    @Override
    public SmUserVO selectSmUserByAliOpenId(String openId) {
        if (StringUtils.isBlank(openId)) {
            return null;
        }
        SmUserQuery query = new SmUserQuery();
        query.setEqAliOpenId(openId);
        return this.selectOne(query);
    }

    @Override
    public SmUserVO registerAli(String openId, String mobile) {
        if (StringUtils.isBlank(openId)) {
            return null;
        }

        String name = "支付宝" + openId.substring(openId.length() - 4);

        // 添加用户
        SmUserVO data = new SmUserVO();
        data.setUserName(name);
        data.setAliOpenId(openId);
        data.setPhonenumber(mobile);
        data.setType(UserType.USER.getType());
        int insert = this.insertSmUser(data);
        ServiceUtil.assertion(insert != 1, "注册支付宝用户失败");

        return this.selectSmUserByUserId(data.getUserId());
    }

    @Override
    public SmUserVO registerWx(String openId, String mobile) {
        if (StringUtils.isBlank(openId)) {
            return null;
        }

        String name = "微信" + openId.substring(openId.length() - 4);

        // 添加用户
        SmUserVO data = new SmUserVO();
        data.setUserName(name);
        data.setWxOpenId(openId);
        data.setPhonenumber(mobile);
        data.setType(UserType.USER.getType());
        int insert = this.insertSmUser(data);
        ServiceUtil.assertion(insert != 1, "注册微信用户失败");

        return this.selectSmUserByUserId(data.getUserId());
    }

    @Override
    public int bindAliMobile(Long userId, String ciphertext) {
        if (userId == null || StringUtils.isBlank(ciphertext)) {
            return 0;
        }
        String mobile = aliAuthService.getPhoneNumber(ciphertext);
        if (StringUtils.isBlank(mobile)) {
            return 0;
        }
        return this.bindMobile(userId, mobile);
    }

    @Override
    public int resetRealName(Long userId) {
        return smUserMapper.resetRealName(userId);
    }

    private SmUserVO selectOne(SmUserQuery query) {
        PageHelper.startPage(1, 1);
        List<SmUserVO> list = this.selectSmUserList(query);
        return CollectionUtils.getFirst(list);
    }

    private int selectCountByPhone(String phone) {
        return smUserMapper.selectCountByPhone(phone);
    }

    /**
     * 逻辑删除前校验
     * @param userIds
     */
    private void validatePreLogicDel(List<Long> userIds) {
        ServiceUtil.assertion(CollectionUtils.isEmpty(userIds), "用户id不能为空");
        ServiceUtil.assertion(!isExistUsers(userIds), "用户不存在");

        DeviceQuery deviceQuery = new DeviceQuery();
        deviceQuery.setUserIds(userIds);
        List<DeviceVO> deviceList = deviceService.selectSmDeviceList(deviceQuery);
        ServiceUtil.assertion(CollectionUtils.isNotEmpty(deviceList), "用户存在设备，不允许删除");


        StoreQuery storeQuery = new StoreQuery();
        storeQuery.setUserIds(userIds);
        List<StoreVo> storeList = storeService.selectSmStoreList(storeQuery);
        ServiceUtil.assertion(CollectionUtils.isNotEmpty(storeList), "用户存在店铺，不允许删除");
    }

    /**
     * 查询用户映射表
     * @param userIds
     */
    private Map<Long, SmUserVO> selectMapByUserIds(List<Long> userIds) {
        if (CollectionUtils.isEmpty(userIds)) {
            return Collections.emptyMap();
        }
        SmUserQuery dto = new SmUserQuery();
        dto.setUserIds(userIds);
        List<SmUserVO> list = smUserMapper.selectSmUserList(dto);
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyMap();
        }
        return list.stream().collect(Collectors.toMap(SmUserVO::getUserId, item -> item));
    }

    /**
     * 新增普通用户信息
     *
     * @param smUser 普通用户信息
     * @return 结果
     */
    @Override
    public int insertSmUser(SmUser smUser)
    {
        // 前置校验
        userValidator.checkBeforeInsert(smUser);

        // 设置用户默认信息
        if (StringUtils.hasText(smUser.getPassword())) {
            smUser.setPassword(SecurityUtils.encryptPassword(smUser.getPassword()));
        }
        smUser.setCreateTime(DateUtils.getNowDate());
        // 若用户没有单独设置服务费，则使用系统默认服务费
        if (smUser.getServiceRate() == null) {
            BigDecimal serviceRate = sysConfigService.getBigDecimal(ConfigKey.SERVICE_FEE_RATE);
            smUser.setServiceRate(serviceRate);
        }

        // 若有wxOpenId，则加锁
        if (StringUtils.hasText(smUser.getWxOpenId())) {
            ServiceUtil.assertion(!redisLock.lock(RedisLockKey.ADD_USER_WX_OPEN_ID, smUser.getWxOpenId()), "请勿频繁注册相同的微信openId");
        }
        try {
            // 若有手机号，则加锁
            if (StringUtils.hasText(smUser.getPhonenumber())) {
                ServiceUtil.assertion(!redisLock.lock(RedisLockKey.ADD_USER_MOBILE, smUser.getPhonenumber()), "请勿频繁注册相同的手机号");
            }
            try {
                Integer result = transactionTemplate.execute(status -> {
                    // 插入数据
                    int insert = smUserMapper.insertSmUser(smUser);
                    ServiceUtil.assertion(insert != 1, "新增用户失败，请刷新后重试");

                    // 后校验
                    SmUserVO vo = this.selectSmUserByUserId(smUser.getUserId());
                    userValidator.afterCheck(vo);

                    return insert;
                });

                return result == null ? 0 : result;
            } finally {
                redisLock.unlock(RedisLockKey.ADD_USER_MOBILE, smUser.getPhonenumber());
            }
        } finally {
            redisLock.unlock(RedisLockKey.ADD_USER_WX_OPEN_ID, smUser.getWxOpenId());
        }
    }

    // 根据微信openId查询信息
    private SmUserVO selectUserByWxOpenId(String wxOpenId) {
        if (wxOpenId == null) {
            return null;
        }
        return smUserMapper.selectSmUserByWxOpenId(wxOpenId);
    }

    /**
     * 修改普通用户信息
     *
     * @param smUser 普通用户信息
     * @return 结果
     */
    @Override
    public int updateSmUser(SmUser smUser)
    {
        if (StringUtils.hasText(smUser.getPassword())) {
            smUser.setPassword(SecurityUtils.encryptPassword(smUser.getPassword()));
        }
        smUser.setUpdateTime(DateUtils.getNowDate());

        Integer result = transactionTemplate.execute(status -> {
            int update = smUserMapper.updateSmUser(smUser);
            ServiceUtil.assertion(update != 1, "更新用户失败");

            SmUserVO vo = this.selectSmUserByUserId(smUser.getUserId());
            userValidator.afterCheck(vo);

            return update;
        });

        return result == null ? 0 : result;
    }

    /**
     * 批量删除普通用户信息
     *
     * @param userIds 需要删除的普通用户信息主键
     * @return 结果
     */
    @Override
    public int deleteSmUserByUserIds(Long[] userIds)
    {
        return smUserMapper.deleteSmUserByUserIds(userIds);
    }

    /**
     * 删除普通用户信息信息
     *
     * @param userId 普通用户信息主键
     * @return 结果
     */
    @Override
    public int deleteSmUserByUserId(Long userId)
    {
        return smUserMapper.deleteSmUserByUserId(userId);
    }

    /**
     * 根据用户名获取普通用户信息
     * @param username
     * @return
     */
    @Override
    public SmUserVO selectUserByUserName(String username) {
        if (username == null) {
            return null;
        }
        SmUserQuery smUser = new SmUserQuery();
        smUser.setUserName(username);
        List<SmUserVO> list = smUserMapper.selectSmUserList(smUser);
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        return list.get(0);
    }
}
