package com.ruoyi.web.controller.system;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gson.JsonObject;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.MenuExtInfo;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginBody;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.domain.model.RegisterBody;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.enums.*;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.GradeYearUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.service.SysLoginService;
import com.ruoyi.framework.web.service.SysPermissionService;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.framework.web.service.UserCommonService;
import com.ruoyi.mingxue.service.IMxjbTagExtService;
import com.ruoyi.mxjb.service.IMxjbSelectedGroupService;
import com.ruoyi.system.domain.vo.RouterVo;
import com.ruoyi.system.service.*;
import com.ruoyi.syzy.domain.BBusiWishLocationSubmits;
import com.ruoyi.syzy.service.IBBusiWishLocationSubmitsService;
import com.ruoyi.syzy.service.IBBusiWishUniversitySubmitMarjorsService;
import com.ruoyi.voluntary.domain.*;
import com.ruoyi.voluntary.dto.GradeClassDto;
import com.ruoyi.voluntary.dto.SchoolDto;
import com.ruoyi.voluntary.service.*;
import com.ruoyi.web.domain.CommDto;
import com.ruoyi.web.domain.Constant;
import com.ruoyi.web.service.StudyService;
import com.ruoyi.web.service.VoluntaryService;
import com.ruoyi.web.service.ZyService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.compress.utils.Sets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 登录验证
 *
 * @author voluntary
 */
@RestController
@Api(tags = "前端登陆")
public class SysLoginController {
    private static final Logger log = LoggerFactory.getLogger(SysLoginController.class);
    @Autowired
    private IMxjbTagExtService mxjbTagExtService;
    @Autowired
    private SysLoginService loginService;

    @Autowired
    private ISysMenuService menuService;

    @Autowired
    private SysPermissionService permissionService;
    @Autowired
    private IBusiSchoolsService schoolsService;
    @Autowired
    private IMxjbTagExtService tagExtService;
    @Autowired
    private IBusiClassesService classesService;
    @Autowired
    private ISysUserService userService;
    @Autowired
    private IBusiCardsService cardsService;
    @Autowired
    private ISysLocationsService locationsService;
    @Autowired
    private ShortMessageService shortMessageService;
    @Autowired
    private TokenService tokenService;
    @Autowired
    private IMxjbSelectedGroupService selectedGroupService;
    @Autowired
    private ISysConfigService configService;
    @Autowired
    private UserCommonService userCommonService;
    @Autowired
    private IBBusiWishUniversitySubmitMarjorsService marjorsService;
    @Autowired
    private StudyService studyService;
    @Autowired
    private IBBusiWishLocationSubmitsService wishLocationSubmitsService;
    @Autowired
    private VoluntaryService voluntaryService;

    /**
     * 登录方法
     *
     * @param loginBody 登录信息
     * @return 结果
     */
    @PostMapping("/login")
    public AjaxResult login(@RequestBody LoginBody loginBody, @RequestParam(value = "type", defaultValue = "front") String type) {
        loginBody.setType(type);
        String username = loginBody.getUsername();

        //手机号或卡号登录
        SysUser user = userService.selectUserByUserNameOrPhone(loginBody.getUsername());

        //如果是卡第一次登录，则创建user
        if (CardType.isFrontCard(username, type)) {
            BusiCards card = cardsService.selectBusiCardsByCardNo(username);
            //校验卡
            this.validateCard(card);
            if (StringUtils.isNull(user)) {
                String password = card.getPassword();
                user = new SysUser();
                user.setUserName(username);
                //旧版卡12位且以00开头
                user.setPhonenumber(CardType.isOldVipCard(username)?(1+username.substring(username.length()-10)):username);
                user.setPassword(SecurityUtils.encryptPassword(password));
                user.setPassword2(password);
                user.setCardId(card.getCardId());
                user.setIsBind(BoolValues.no.getValue());
                user.setUserType(type);
                if (null != card.getDistributeSchoolId()) {
                    BusiSchools userSchool = schoolsService.selectBusiSchoolsById(card.getDistributeSchoolId());
                    user.setProvinceName(locationsService.getNameByLocationId(userSchool.getPro()));
                }
                Long[] roleIds = new Long[]{RoleType.frontVip.getValue()};
                user.setRoleIds(roleIds);
                //处理是否锁定成绩（主要用于补充：如06-20开启，用户06-21注册的情况）
                if (userCommonService.isInIntervalDate(user)) {
                    user.setScoreLock(BoolValues.other.getValue());//初始化时在时间范围内直接更新为中间锁定状态
                } else {
                    user.setScoreLock(BoolValues.no.getValue());
                }
                userService.insertUser(user);
            }
        } else if(null == user) {
            return AjaxResult.error("用户登陆异常: " + username);
        }
        //设置userType=front
        user.setUserType(type);
        userService.updateUserStatus(user);

        //将登录名换成user表中的userName
        loginBody.setUsername(user.getUserName());
        //user存在，校验前端登录卡过期时间outTime
        if (CardType.isFrontCard(username, type)) {
            //用户是否有登录权限 前端登录前端，后端登录后端
            if (!userService.hasLoginFrontPermission(user)) {
                return AjaxResult.error("该用户不允许登录前端系统");
            }

            //已绑卡校验卡状态：已绑定的卡才校验
            if (StringUtils.isNotNull(user.getCardId()) && BoolValues.isTrue(user.getIsBind())) {
                //已绑定卡
                BusiCards card = cardsService.selectBusiCardsByCardId(user.getCardId());
                if (StringUtils.isNull(card)) {
                    card = cardsService.selectBusiCardsByCardNo(user.getUserName());
                }
                this.validateCard(card);
            } else {

            }
        } else if (AccessFromType.isBack(type)) {//用户是否有登录权限 前端登录前端，后端登录后端
            if (userService.hasLoginFrontPermission(user)) {
                return AjaxResult.error("该用户不允许登录后端系统");
            }
        }

        // 生成令牌
        AjaxResult ajax = AjaxResult.success();
        String token = loginService.login(loginBody);
        ajax.put(Constants.TOKEN, token);

        return ajax;
    }

    private void validateCard(BusiCards card) {
        if (StringUtils.isNull(card)) {
            throw new BaseException("卡不存在，请联系管理员");
        }
        //校验是否已分配
        if (CardStatus.isUndistributed(card.getStatus())) {
            throw new BaseException("卡未分配，请联系管理员");
        }

        Date nowDate = Date.from(LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant());
        if (CardStatus.isExpired(card.getStatus()) || (null != card.getOutDate() && card.getOutDate().getTime() < nowDate.getTime())) {
            SysUser user = userService.selectUserByUserNameOrPhone(card.getCardNo());
            String phonenumber = StringUtils.EMPTY;
            if(StringUtils.isNotNull(user)&&StringUtils.isNotEmpty(user.getPhonenumber())){
                phonenumber = user.getPhonenumber();
            }
            throw new BaseException("卡已失效，请使用手机号["+phonenumber+"]登录后重新绑定卡");
        }
        if (BoolValues.isTrue(card.getIsRefund())) {
            throw new BaseException("卡已退费，请联系管理员");
        } else if (null != card.getCloseId()||CardStatus.isClosed(card.getStatus())) {
            throw new BaseException("卡已关闭，请联系管理员");
        }
    }

    @Data
    private class ExtSysUser extends SysUser {
        Map userStat;
    }

    /**
     * 获取用户信息
     *
     * @return 用户信息
     */
    @GetMapping("getInfo")
    public AjaxResult getInfo(HttpServletRequest request) {
        SysUser user = userCommonService.getUserBasicInfo(userService.selectUserById(SecurityUtils.getLoginUser().getUser().getUserId()));        // 角色集合
        Set<String> roles = permissionService.getRolePermission(user);
        //当前角色及菜单
        List<Map<String, Object>> roleList = userService.getUserRoleList(user);
        // 权限集合
        Set<String> permissions = permissionService.getMenuPermission(user);
//        user.setIsPreEnrollSupport(BoolValues.yes.getValue());
        //处理是否可查看学科类菜单入口  用户是否学科类敏感（会隐藏学科类菜单入口）
        user.setIsK9Sensitive(isK9Sensitive(user));

        ExtSysUser extSysUser = new ExtSysUser();
        extSysUser.setUserStat(studyService.getUserStats());
        String enableAuthModify = configService.selectConfigByKey("user.auth.modify");
        BeanUtil.copyProperties(user, extSysUser);
        extSysUser.setEnableAuthModify(StringUtils.isNotBlank(enableAuthModify) && enableAuthModify.contains(user.getUserName()));
        AjaxResult ajax = AjaxResult.success(extSysUser);
        ajax.put("roles", roles);
        ajax.put("roleList", roleList);
        ajax.put("permissions", permissions);

        //获取app的配置文件
        String type = request.getHeader("type");
        //H5 wechat frontApp均需要菜单
        if(AccessFromType.isFrontApp(type)||AccessFromType.isH5(type)||AccessFromType.isWechat(type)){
            String appConfig = configService.selectConfigByKey("app.common.config");
            JSONObject json = JSONObject.parseObject(appConfig);
            ajax.put("appConfig", json);
        }

        return ajax;
    }

    private Boolean isK9Sensitive(SysUser user){
        //处理是否可查看学科类菜单入口  用户是否学科类敏感（会隐藏学科类菜单入口）
        if(null==user.getCreateTime()){
            //注册时间小于设置时间则为新账号
            return true;
        }else {
            Date registerTime = DateUtils.parseDate(configService.selectConfigByKey( "sensitive.register.createtime"));
            if(user.getCreateTime().before(registerTime)){ //2022-07-01 00:00:00
                //注册时间小于设置时间则为新账号（学科不敏感，显示学科类）
                return false;
            }else {
                return true;
            }
        }
    }

    /**
     * 获取路由信息
     *
     * @return 路由信息
     */
    @GetMapping("getRouters")
    public AjaxResult getRouters() {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        SysUser user = loginUser.getUser();
        Set<Long> excludeSubMenuIdSet = AccessFromType.isBack(user.getUserType()) ? Sets.newHashSet(2162L) : Sets.newHashSet();
        if(!"湖南".equalsIgnoreCase(user.getProvinceName())){
            excludeSubMenuIdSet.add(2933L);
            excludeSubMenuIdSet.add(2935L);
            excludeSubMenuIdSet.add(2936L);
            excludeSubMenuIdSet.add(2937L);
            excludeSubMenuIdSet.add(2938L);
        }
        List<SysMenu> menus = AccessFromType.isBack(user.getUserType())
                ? menuService.selectMenuTreeByUserId(user, 0L, excludeSubMenuIdSet)
                : menuService.selectMenuTreeByUserId(user, 2162L, excludeSubMenuIdSet);
        return AjaxResult.success(menuService.buildMenus(menus, tagExtService.buildPcMenuExtMap()));
    }

    /**
     * 获取路由信息
     *
     * @return 路由信息
     */
    @GetMapping("getFrontInitialRouters")
    public AjaxResult getFrontInitialRouters() {
        List<SysMenu> menus = menuService.selectIntialMenu();
        menus = menus.stream().filter(routerVo -> routerVo.getMenuId() != 2933L).collect(Collectors.toList());
        List<RouterVo> routerList = menuService.buildMenus(menus, tagExtService.buildPcMenuExtMap());
        return AjaxResult.success(routerList);
    }

    @PostMapping("/improveUserInfo")
    @Log(title = "完善个人信息", businessType = BusinessType.UPDATE)
    public AjaxResult improveUserInfo(@RequestBody RegisterBody body) {
        log.info("请求参数:{}",JSONObject.toJSONString(body));
        //姓名、省份（前两个必填）、性别、分数、选科组合
        if (StringUtils.isBlank(body.getName())) {
            return AjaxResult.error("姓名[name]不能为空");
        }
        if (StringUtils.isBlank(body.getProvinceName())) {
            return AjaxResult.error("省份[provinceName]不能为空");
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        SysUser user = userService.selectUserById(SecurityUtils.getUserId());
        log.info("获取用户信息前:{}",JSONObject.toJSONString(user));
        Date now = new Date();

        //手机先登录再绑定卡时，将手机的密码覆盖卡密码
        user.setNickName(body.getName());
        user.setProvinceName(body.getProvinceName());

        String enableAuthModify = configService.selectConfigByKey("user.auth.modify");
        Boolean canModifyExamMajor = StringUtils.isNotBlank(enableAuthModify) && enableAuthModify.contains(user.getUserName());
        if(!canModifyExamMajor) {
            user.setCustomSchoolName(body.getCustomSchoolName());
            user.setCustomClassName(body.getCustomClassName());
        }
        Boolean obsoleted = false;
        if (StringUtils.isBlank(user.getExamType())) {
            user.setExamType(body.getExamType());
        } else if(canModifyExamMajor && StringUtils.isNotBlank(body.getExamType()) && !body.getExamType().equals(user.getExamType())) { // 有变化
            user.setExamType(body.getExamType());
            obsoleted = true;
        }
        if(Constant.EXAM_TYPE_ZG.equals(user.getExamType())) {
            if(StringUtils.isBlank(user.getExamMajor()) || canModifyExamMajor && (obsoleted = !user.getExamMajor().equals(body.getExamMajor()))) {
                user.setExamMajor(body.getExamMajor());
                Map<String, String> examMajorMap = wishLocationSubmitsService.examMajors(user.getProvinceName()).stream().collect(
                        Collectors.toMap(BBusiWishLocationSubmits::getCourse, BBusiWishLocationSubmits::getCourseName));
                user.setExamMajorName(examMajorMap.get(body.getExamMajor()));
            }
        } else {
            user.setExamMajor("");
            user.setExamMajorName("");
        }

        //用户性别（0男 1女 2未知）
        user.setSex(body.getSex());

        //锁分阶段不允许修改分数及组合
        if (!BoolValues.isTrue(user.getScoreLock())) {//-1（中间状态），0（非锁分状态）时可以修改
            user.setScore(body.getScore());
            //组合
            Integer groupId = selectedGroupService.selectGroupIdByMode(body.getMode());
            user.setGroupId(groupId);
        }

        if(obsoleted) {
            user.setSeatInput(0);
            user.setScore(0);
        }

        user.setUpdateTime(now);
        //处理是否锁定成绩（主要用于补充：如06-20开启，用户06-21注册的情况）
        if (BoolValues.isOther(user.getScoreLock())) {// 中间状态-1时不修改此值
            if (userCommonService.isInIntervalDate(user)) {
                user.setScoreLock(BoolValues.yes.getValue());//初始化时在时间范围内直接将成绩锁定
            } else {
                user.setScoreLock(BoolValues.no.getValue());
            }
        }

        if(obsoleted) {
            voluntaryService.obsoleteWishRecord(user.getUserId());
        }

        userService.updateUserStatus(user);
        log.info("获取用户信息后（改分后）:{}",JSONObject.toJSONString(user));

        //刷新token
        tokenService.refreshToken(loginUser.setUser(user));
        return AjaxResult.success("更新个人信息成功");
    }

    @PostMapping("/bindCard")
    @Log(title = "绑定卡或换卡绑定（先用手机号登录）", businessType = BusinessType.UPDATE)
    public AjaxResult bindCard(@RequestBody RegisterBody body) {
        //卡号username、密码password、学校schoolId、年级gradeId、班级classId
        LoginUser loginUser = SecurityUtils.getLoginUser();
        SysUser user = loginUser.getUser();
        Long oldCardId = null;
        if (BoolValues.isTrue(user.getIsBind()) && null != user.getCardId()) {
//            return AjaxResult.error("用户已经绑定卡，无需重新操作");
            //已绑定卡的先将原来的卡重置为已过期
            oldCardId = user.getCardId();
        }
        if (null == body.getGradeId() && null != body.getGaokaoYear()) {
            BusiGrade busiGrade = userCommonService.getGradeByYear(body.getGaokaoYear());
            if (null != busiGrade) {
                body.setGradeId(busiGrade.getId());
            }
        }

        //校验卡是否已经激活被使用
        BusiCards card = cardsService.selectBusiCardsByCardNo(body.getUsername());
        if (null != card && (BoolValues.isTrue(card.getIsUsed()) || null != card.getActiveTime())) {
            return AjaxResult.error("该卡[" + body.getUsername() + "]已被绑定，请更换新卡");
        }
        //校验：先用卡号A登录，再用手机号B注册并绑定卡A的情况（些时user表中会存在2个一样username的记录）
        SysUser alreadyUser = userService.selectUserByUserNameOrPhone(body.getUsername());
        if (null != alreadyUser) {
            if (BoolValues.isTrue(alreadyUser.getIsBind())) {
                return AjaxResult.error("卡号已存在");
            } else {
                //删除旧记录
                userService.deleteUserById(alreadyUser.getUserId());
            }
        }

        checkCardPassword(card, body.getPassword());

        Date now = new Date();
        //处理year与outDate
        Integer year = getYearAndOutDate(card, body);

        //手机先登录再绑定卡时，将手机的密码覆盖卡密码
        user.setUserName(card.getCardNo());
        user.setCardId(card.getCardId());
        user.setIsBind(BoolValues.yes.getValue());
        //角色切换为vip
        Long[] roleIds = new Long[]{RoleType.frontVip.getValue()};
        user.setRoleIds(roleIds);
        user.setCurrentRoleId(RoleType.frontVip.getValue());
        user.setUpdateTime(now);
        user.setYear(year);

        user.setGaokaoYear(body.getGaokaoYear());
        user.setScore(body.getScore());
        user.setGroupId(selectedGroupService.selectGroupIdByMode(body.getMode()));
        user.setClassId(body.getClassId());
        user.setCustomClassName(body.getCustomClassName());

        if (StringUtils.isNotBlank(card.getDistributeExamType())) {
            user.setExamType(card.getDistributeExamType());
        } else {
            user.setExamType(body.getExamType());
        }
        user.setGradeId(body.getGradeId());
        if (body.getSchoolId() != null && body.getSchoolId() > 0) {
            user.setSchoolId(body.getSchoolId());
            user.setCustomSchoolName("");
            // 如果卡省份与用户手机注册时选择的省份冲突，则以卡省份为基准
            BusiSchools cardDistributeSchool = schoolsService.selectBusiSchoolsById(body.getSchoolId());
            if (StringUtils.isNotNull(cardDistributeSchool)) {
                String cardProvinceName = locationsService.getNameByLocationId(cardDistributeSchool.getPro());
                user.setProvinceName(cardProvinceName);
            } else {
                return AjaxResult.error("省份不能为空");
            }
        } else {
            user.setSchoolId(-1L);
            if (StringUtils.isNotBlank(card.getDistributeLocation())) {
                user.setProvinceName(card.getDistributeLocation());
            }
            user.setCustomSchoolName(body.getCustomSchoolName());
            if (StringUtils.isBlank(user.getProvinceName())) {
                return AjaxResult.error("省份不能为空");
            }
        }
        if(Constant.EXAM_TYPE_ZG.equals(user.getExamType())) {
            user.setExamMajor(body.getExamMajor());
            Map<String, String> examMajorMap = wishLocationSubmitsService.examMajors(user.getProvinceName()).stream().collect(
                    Collectors.toMap(BBusiWishLocationSubmits::getCourse, BBusiWishLocationSubmits::getCourseName));
            user.setExamMajorName(examMajorMap.get(body.getExamMajor()));
        } else {
            user.setExamMajor("");
            user.setExamMajorName("");
        }
        // 0=All,1=Prov,2=School
        if (null != card.getDistributeType() && card.getDistributeType() > 0) {
            if (!card.getDistributeLocation().equals(user.getProvinceName())) {
                return AjaxResult.error("卡绑定省份不一致");
            }
            if (card.getDistributeType().equals(2)) {
                if (!card.getDistributeSchoolId().equals(body.getSchoolId())) {
                    return AjaxResult.error("卡绑定学校不一致");
                }
            }
        }

        //覆盖分数、组合
//        user.setScore(body.getScore());
        //处理是否锁定成绩（主要用于补充：如06-20开启，用户06-21注册的情况）
        if (userCommonService.isInIntervalDate(user)) {
            user.setScoreLock(BoolValues.yes.getValue());//初始化时在时间范围内直接将成绩锁定
        } else {
            user.setScoreLock(BoolValues.no.getValue());
        }
        userService.updateUser(user);

        //更新用户卡信息
        card.setIsUsed(BoolValues.yes.getValue());
        card.setStatus(CardStatus.actived.getValue());
        card.setNickName(user.getNickName());
        card.setSchoolId(user.getSchoolId());
        card.setGradeId(user.getGradeId());
        card.setClassId(user.getClassId());
        card.setYear(year);
        //将密码覆盖
        card.setPassword(userService.selectUserCleartextPassword(user.getUserId()));
        card.setActiveTime(now);
        card.setUpdateTime(now);

        cardsService.updateBusiCards(card);

        //用户信息更新后，最后再将旧卡置为过期卡
        if (null != oldCardId) {
            BusiCards oldCard = cardsService.selectBusiCardsByCardId(oldCardId);
            oldCard.setStatus(CardStatus.expired.getValue());
            oldCard.setOutDate(now);
            oldCard.setUpdateTime(now);
            cardsService.updateBusiCards(oldCard);
        }

        //刷新token
        tokenService.refreshToken(loginUser.setUser(user));

        return AjaxResult.success("绑卡成功");
    }

    private void checkCardPassword(BusiCards card, String password) {
        if (StringUtils.isNull(card)) {
            throw new BaseException("卡不存在");
        } else if (!card.getPassword().equals(password)) {
            throw new BaseException(("卡密码错误"));
        } else if (BoolValues.isTrue(card.getIsUsed()) || null != card.getActiveTime()) {
            throw new BaseException(("卡已使用"));
        }
    }

    private Integer getYearAndOutDate(BusiCards card, RegisterBody body) {
        Integer year = card.getYear();
        Date outDate = null;
        if (CardType.isExperienceCard(card.getType())) {
            //体验卡只能使用90天
            LocalDate localDate = LocalDate.now();
            String cardInitTime = configService.selectConfigByKey("card_init_time");
            localDate = localDate.plusDays(Long.valueOf(cardInitTime));
            ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault());
            outDate = Date.from(zonedDateTime.toInstant());

            if (null == year) {
                year = Calendar.getInstance().get(Calendar.YEAR);
            }
            card.setOutDate(outDate);
            return year;
        }

        if (null == year) {
            //卡分配时有学年以这个为基准，如无学年则以学生填写的年级对应学年为基准
            year = GradeYearUtils.getRealYear(Calendar.getInstance().get(Calendar.YEAR), body.getGradeId().intValue());
        }
        outDate = GradeYearUtils.getCardOutDate(year);

        //过期时间：以毕业年的08-31为基准
        card.setOutDate(outDate);

        return year;
    }

    @PostMapping("/bindPhone")
    @Log(title = "绑定手机号（先用卡登录）", businessType = BusinessType.UPDATE)
    public AjaxResult bindPhone(@RequestBody RegisterBody body,Boolean isMock) {
        if(null==isMock){
            isMock=false;
        }
        //手机号 username、验证码 code
        //{"username":"15111095682","code":"6553","name":"莫进侠","schoolId":1,"gradeId":500,"classId":1,
        // "provinceName":"湖南","sex":"1","mode":"物理,地理,化学","score":611}
        String phonenumber = body.getUsername();
        if(!isMock){
            if(body.getValidPhone()){
                Boolean isSuccess = shortMessageService.checkCode(phonenumber, body.getCode());
                if (!isSuccess) {
                    return AjaxResult.error("验证码错误");
                }
            }
        }
        if (StringUtils.isNull(body.getClassId()) && StringUtils.isBlank(body.getCustomClassName())) {
            return AjaxResult.success("请选择或输入班级");
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        SysUser user = loginUser.getUser();
        user.setPhonenumber(phonenumber);
        user.setIsBind(BoolValues.yes.getValue());
        user.setNickName(body.getName());

        user.setGaokaoYear(body.getGaokaoYear());
        user.setScore(body.getScore());
        user.setGroupId(selectedGroupService.selectGroupIdByMode(body.getMode()));
        user.setClassId(body.getClassId());
        user.setCustomClassName(body.getCustomClassName());
        if (null != body.getClassId()) {
            BusiClasses classes = classesService.selectBusiClassesByClassId(body.getClassId());
            user.setSchoolId(classes.getSchoolId());
            user.setGradeId(classes.getGradeId());
        } else if (null != body.getSchoolId() && body.getSchoolId() > 0) {
            user.setSchoolId(body.getSchoolId());
            user.setGradeId(body.getGradeId());
        } else if(StringUtils.isNotBlank(body.getCustomSchoolName())) {
            user.setSchoolId(-1L);
            user.setCustomSchoolName(body.getCustomSchoolName());
        }
        if (null == body.getGradeId() && null != body.getGaokaoYear()) {
            BusiGrade busiGrade = userCommonService.getGradeByYear(body.getGaokaoYear());
            if (null != busiGrade) {
                body.setGradeId(busiGrade.getId());
            }
        }
        user.setProvinceName(body.getProvinceName());
        //用户性别（0男 1女 2未知）
        user.setSex(body.getSex());

        BusiCards card = cardsService.selectBusiCardsByCardNo(user.getUserName());
        //处理year与outDate
        Integer year = getYearAndOutDate(card, body);

        // 处理省份名称：如果卡分配学校则以其所在省份为准，如无分配学校则以页面填写的为基准
        if (null != card.getDistributeExamType()) {
            user.setExamType(card.getDistributeExamType());
        } else {
            user.setExamType(body.getExamType());
        }
        if (StringUtils.isBlank(user.getExamType())) {
            return AjaxResult.error("请选择考生类型");
        }
        if (null != card.getDistributeSchoolId()) {
            user.setSchoolId(card.getDistributeSchoolId());
        }
        if (StringUtils.isNotBlank(card.getDistributeLocation())) {
            user.setProvinceName(card.getDistributeLocation());
        } else if (null != user.getSchoolId() && user.getSchoolId() > 0) {
            BusiSchools cardDistributeSchool = schoolsService.selectBusiSchoolsById(user.getSchoolId());
            if (StringUtils.isNotNull(cardDistributeSchool)) {
                String cardProvinceName = locationsService.getNameByLocationId(cardDistributeSchool.getPro());
                user.setProvinceName(cardProvinceName);
            }
        }
        if(Constant.EXAM_TYPE_ZG.equals(user.getExamType())) {
            user.setExamMajor(body.getExamMajor());
            Map<String, String> examMajorMap = wishLocationSubmitsService.examMajors(user.getProvinceName()).stream().collect(
                    Collectors.toMap(BBusiWishLocationSubmits::getCourse, BBusiWishLocationSubmits::getCourseName));
            user.setExamMajorName(examMajorMap.get(body.getExamMajor()));
        } else {
            user.setExamMajor("");
            user.setExamMajorName("");
        }
        user.setYear(year);
        user.setUpdateTime(new Date());
        //处理是否锁定成绩（主要用于补充：如06-20开启，用户06-21注册的情况）
        if (userCommonService.isInIntervalDate(user)) {
            user.setScoreLock(BoolValues.yes.getValue());//初始化时在时间范围内直接将成绩锁定
        } else {
            user.setScoreLock(BoolValues.no.getValue());
        }
        //此处不需要更新角色
        userService.updateUserStatus(user);

        Date now = new Date();

        //更新用户卡信息
        card.setIsUsed(BoolValues.yes.getValue());
        card.setStatus(CardStatus.actived.getValue());
        card.setNickName(user.getNickName());
        card.setSchoolId(user.getSchoolId());
        card.setGradeId(user.getGradeId());
        card.setClassId(user.getClassId());
        card.setYear(year);
        //将密码覆盖
        card.setActiveTime(now);
        card.setUpdateTime(now);
        cardsService.updateBusiCards(card);

        //刷新token
        tokenService.refreshToken(loginUser.setUser(user));

        return AjaxResult.success();
    }

    @GetMapping("/getRegisterGaokaoYears")
    public AjaxResult getRegisterGaokaoYears() {
        return AjaxResult.success(userCommonService.listGaokaoYears());
    }

    @GetMapping("/getAreaSchoolTree")
    public AjaxResult getAreaSchoolTree(String provinceName, @RequestParam(required = false) String username) {
        SysLocations sysLocations = locationsService.selectSysLocationsBySname(provinceName);
        if (null == sysLocations) {
            return AjaxResult.error("省份不正确: " + provinceName);
        }
        String provCode = sysLocations.getCode().substring(0, 2);
        Map<Long, CommDto.AreaSchoolNodeDto> areaSchoolNodeDtoMap = Maps.newHashMap();
        List<CommDto.AreaSchoolNodeDto> cityList = Lists.newArrayList();
        for (SysLocations loc : locationsService.selectSysLocationsByCode(provCode)) {
            CommDto.AreaSchoolNodeDto node = new CommDto.AreaSchoolNodeDto(loc.getName(), loc.getId(), 0, Lists.newArrayList());
            if (loc.getLevel().equals(2)) {
                cityList.add(node);
            } else if (loc.getLevel() > 2) {
                CommDto.AreaSchoolNodeDto parentNode = areaSchoolNodeDtoMap.get(loc.getParentId());
                if (null != parentNode) {
                    parentNode.getChildren().add(node);
                } else {
                    log.error("错误的结点: {}", loc.getId());
                }
            }
            areaSchoolNodeDtoMap.put(loc.getId(), node);
        }
        BusiSchools schoolCond = new BusiSchools();
        schoolCond.setPro(sysLocations.getId());
        BusiCards card;
        Long distributeSchoolId = null;
        if (StringUtils.isBlank(username)) {
            username = SecurityUtils.getLoginUser().getUser().getUserName();
        }
        if (StringUtils.isNotBlank(username) && null != (card = cardsService.selectBusiCardsByCardNo(username))
                && !CardType.isOldVipCard(card.getCardNo()) && !CardType.isECard(card.getType())) {
            String agentAllSchool = configService.selectConfigByKey("agent.all.school");
            if (StringUtils.isBlank(agentAllSchool) || !Arrays.asList(Convert.toLongArray(agentAllSchool)).contains(card.getAgentId())) {
                //仅能看到卡分配的学校
                distributeSchoolId = card.getDistributeSchoolId();
                if (null != distributeSchoolId) {
                    schoolCond.setId(distributeSchoolId);
                }
            }
        }
        if (null != schoolCond.getId()) { // 绑定学校时只输出一个
            BusiSchools s = schoolsService.selectBusiSchoolsById(schoolCond.getId());
            CommDto.AreaSchoolNodeDto areaNode = areaSchoolNodeDtoMap.get(s.getArea());
            CommDto.AreaSchoolNodeDto cityNode = areaSchoolNodeDtoMap.get(s.getCity());
            cityList.clear();
            if (null != areaNode && null != cityNode) {
                areaNode.getChildren().add(new CommDto.AreaSchoolNodeDto(s.getName(), s.getId(), 1, null));
                cityNode.setChildren(Lists.newArrayList(areaNode));
                cityList.add(cityNode);
            }
        } else { // 未绑定学校时需要增加其他
            for (BusiSchools s : schoolsService.selectBusiSchoolsList(schoolCond)) {
                CommDto.AreaSchoolNodeDto parentNode = areaSchoolNodeDtoMap.get(s.getArea());
                if (null != parentNode) {
                    List<CommDto.AreaSchoolNodeDto> children = parentNode.getChildren();
                    children.add(new CommDto.AreaSchoolNodeDto(s.getName(), s.getId(), 1, null));
                }
            }
            for (CommDto.AreaSchoolNodeDto city : cityList) {
                for (CommDto.AreaSchoolNodeDto area : city.getChildren()) {
                    area.getChildren().add(new CommDto.AreaSchoolNodeDto("其他", -1 * area.getValue(), 1, null));
                }
            }
        }
        return AjaxResult.success(cityList);
    }

    /**
     * 获取代理商下的学校
     *
     * @return
     */
    @GetMapping("/getAreaAndSchool")
    public AjaxResult getAreaAndSchool(String username, String password, String provinceName) {
        Boolean isSelf = false;
        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
            isSelf = true;
            username = SecurityUtils.getLoginUser().getUser().getUserName();
        }

        BusiCards card = cardsService.selectBusiCardsByCardNo(username);
        JSONObject rootObject = new JSONObject();

        if (StringUtils.isNull(card)) {
            return AjaxResult.error("卡不存在");
        } else if (StringUtils.isNotNull(card)) {
            //校验密码
            if (!isSelf) {
                checkCardPassword(card, password);
            }

            this.validateCard(card);

            List<SchoolDto> schoolList = new ArrayList<>();
            if (CardType.isOldVipCard(card.getCardNo())||CardType.isECard(card.getType())){
                //电子卡,返回所有学校
                schoolList = schoolsService.getAreaSchoolsByAgentId(card.getAgentId(), true);
            } else {
                String agentAllSchool = configService.selectConfigByKey("agent.all.school");
                if (StringUtils.isNotBlank(agentAllSchool) && Arrays.asList(Convert.toLongArray(agentAllSchool)).contains(card.getAgentId())) {
                    //看到代理商下面的所有学校
                    schoolList = schoolsService.getAreaSchoolsByAgentId(card.getAgentId());
                } else {
                    //仅能看到卡分配的学校
                    Long distributeSchoolId = card.getDistributeSchoolId();
                    if (null != distributeSchoolId) {
                        schoolList = schoolsService.getAreaSchoolsByDistributeSchool(distributeSchoolId);
                    } else {
                        if (null == card.getAgentId()) {
                            //无代理商时查看所有学校
                            schoolList = schoolsService.getAreaSchoolsByAgentId(null, true);
                        } else {
                            //无分配学校时看到代理商下所有学校
                            schoolList = schoolsService.getAreaSchoolsByAgentId(card.getAgentId());
                        }
                    }
                }
            }
            //限定学校
            if (StringUtils.isNotBlank(provinceName)) {
                schoolList = schoolList.stream().filter(n -> n.getArea().contains(provinceName)).collect(Collectors.toList());
            }
            if (null == card.getDistributeSchoolId() || card.getDistributeSchoolId() <= 0) {
                SchoolDto otherSchool = new SchoolDto();
                otherSchool.setSchoolId(-1L);
                otherSchool.setSchoolName("其他");
                schoolList.add(otherSchool);
            }
            rootObject.put("schools", schoolList);
        }

        return AjaxResult.success(rootObject);
    }

    /**
     * 查询位置编码列表
     */
    @ApiOperation("查询位置编码-前端（关联卡）")
    @GetMapping("/mingxue/SysLocations/getFrontLocationsList")
    public AjaxResult getFrontLocationsList(SysLocations sysLocations, @RequestParam(required = false) String username, @RequestParam(required = false) String password)
    {
        List<String> locations = null;
        if (null != sysLocations.getLevel() && sysLocations.getLevel() == 1 || null == sysLocations.getParentId() || sysLocations.getParentId().equals(1L)) {
            //根据登录卡的位置
            SysUser user = SecurityUtils.getLoginUser().getUser();
            Boolean isSelf = false;
            if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
                isSelf = true;
                username = user.getUserName();
            }
            BusiCards card = cardsService.selectBusiCardsByCardNo(username);
            if (null == card) {
                return AjaxResult.error("无卡: " + username);
            }
            //校验密码
            if (!isSelf) {
                checkCardPassword(card, password);
            }
            this.validateCard(card);

            if(StringUtils.isNotNull(card.getDistributeSchoolId())) {
                sysLocations.setId(schoolsService.selectBusiSchoolsById(card.getDistributeSchoolId()).getPro());
            } else if (StringUtils.isNotBlank(card.getDistributeLocation())) {
                sysLocations.setSname(card.getDistributeLocation());
            } else {
                locations = marjorsService.locations();
            }
        }
        List<SysLocations> list = locationsService.selectSysLocationsList(sysLocations);

        if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(locations)){
            List<SysLocations> resultList = new ArrayList<>();
            for (SysLocations ll : list) {
                if (locations.contains(ll.getSname())) {
                    resultList.add(ll);
                }
            }
            return AjaxResult.success(resultList);
        }
        return AjaxResult.success(list);
    }

    @GetMapping("/getGradeClasses")
    @ApiOperation("查询学校所有年级班级")
    public AjaxResult getGradeClasses(Long schoolId) {
        List<GradeClassDto> gradeClassDtoList = new ArrayList<>();
        if (null == schoolId) {
            schoolId = SecurityUtils.getLoginUser().getUser().getSchoolId();
        }
        gradeClassDtoList = classesService.getGradeClasses(schoolId);

        return AjaxResult.success(gradeClassDtoList);
    }

    @PostMapping("/transferCard")
    @ApiOperation("换卡绑定-废弃")
    @Transactional
    public AjaxResult transferCard(@RequestBody LoginBody body) {
        SysUser user = SecurityUtils.getLoginUser().getUser();
        if (!BoolValues.isTrue(user.getIsBind())) {
            return AjaxResult.error("用户未绑定卡不能更换卡");
        }

        BusiCards newCard = cardsService.selectBusiCardsByCardNo(body.getUsername());
        this.checkCardPassword(newCard, body.getPassword());
        Date now = new Date();

        BusiCards oldCard = cardsService.selectBusiCardsByCardId(user.getCardId());
        //绑定新的卡
        newCard.setUpdateTime(now);
        newCard.setStatus(oldCard.getStatus());
        newCard.setOutDate(oldCard.getOutDate());
        newCard.setAgentId(oldCard.getAgentId());
        newCard.setPassword(oldCard.getPassword());
        newCard.setYear(oldCard.getYear());
        newCard.setNickName(oldCard.getNickName());
        newCard.setActiveTime(oldCard.getActiveTime());
        newCard.setIsUsed(oldCard.getStatus());
        newCard.setIsSettlement(oldCard.getStatus());
        newCard.setIsRefund(oldCard.getStatus());
        newCard.setIsPay(oldCard.getStatus());
        newCard.setClassId(oldCard.getClassId());
        newCard.setGradeId(oldCard.getGradeId());
        newCard.setSchoolId(oldCard.getSchoolId());
        newCard.setOpenId(oldCard.getOpenId());
        newCard.setRefundTime(oldCard.getRefundTime());
        newCard.setCloseTime(oldCard.getCloseTime());
        newCard.setPayTime(oldCard.getPayTime());
        cardsService.updateBusiCards(newCard);

        //旧卡废弃
        oldCard.setStatus(CardStatus.expired.getValue());
        oldCard.setUpdateTime(now);
        cardsService.updateBusiCards(oldCard);

        //user需要更换：cardId
        user.setCardId(newCard.getCardId());
        user.setUpdateTime(now);
        userService.updateUserStatus(user);

        return AjaxResult.success();
    }

    /**
     * 用户注销，之后不能再登录
     * @param loginBody
     * @return
     */
    @Log(title = "用户注销", businessType = BusinessType.LOGOUT)
    @PostMapping("/logoutPhysical")
    public AjaxResult logout(@RequestBody LoginBody loginBody) {
        SysUser currentUser = SecurityUtils.getLoginUser().getUser();
        userService.deleteUserById(currentUser.getUserId());
        //卡重置为已过期
        if(null!=currentUser.getCardId()){
            BusiCards card = cardsService.selectBusiCardsByCardId(currentUser.getCardId());
            card.setStatus(CardStatus.expired.getValue());
            card.setUpdateTime(new Date());
            cardsService.updateBusiCards(card);
        }
        return AjaxResult.success("注销成功",currentUser);
    }

    /**
     *
     * @param loginBody
     * @return
     */
    @Log(title = "用户注销恢复", businessType = BusinessType.LOGOUT)
    @PostMapping("/logoutPhysicalRestore")
    public AjaxResult logoutRestore(@RequestBody LoginBody loginBody) {
        SysUser currentUser = SecurityUtils.getLoginUser().getUser();
        currentUser.setDelFlag(UserStatus.OK.getCode());
        userService.updateUserStatus(currentUser);
        //卡重置为已激活
        if(null!=currentUser.getCardId()){
            BusiCards card = cardsService.selectBusiCardsByCardId(currentUser.getCardId());
            card.setStatus(CardStatus.actived.getValue());
            card.setUpdateTime(new Date());
            cardsService.updateBusiCards(card);
        }
        return AjaxResult.success("注销恢复成功");
    }

    @ApiOperation("根据手机验证码修改卡用户密码-web端或app端使用，此时未登录")
    @PostMapping(value = "resetCardPassword")
    public AjaxResult resetCardPassword(String mobile,String smsCode,String newPassword) {
        List<SysUser> users = userService.selectUserByPhonenumber(mobile);
        if(CollectionUtils.isEmpty(users)){
            return AjaxResult.error("该手机号未绑定账号！");
        }
        SysUser currentUser=users.get(0);

        boolean isSuccess = shortMessageService.checkCode(mobile, smsCode);
        if(!isSuccess){
            return AjaxResult.error("验证码验证失败");
        }

        SysUser user = new SysUser();
        user.setPassword(newPassword);
        user.setUserId(currentUser.getUserId());
        user.setUserName(currentUser.getUserName());
        int count = userService.resetPwd(user);

        return AjaxResult.success("修改密码成功");
    }
}
