package com.bizzan.bitrade.controller;

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dm.model.v20151123.SingleSendMailRequest;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.bizzan.bitrade.constant.CommonStatus;
import com.bizzan.bitrade.constant.MemberLevelEnum;
import com.bizzan.bitrade.constant.MemberOperationType;
import com.bizzan.bitrade.constant.NotifyType;
import com.bizzan.bitrade.constant.SysConstant;
import com.bizzan.bitrade.controller.sdk.NECaptchaVerifier;
import com.bizzan.bitrade.controller.sdk.NESecretPair;
import com.bizzan.bitrade.entity.*;
import com.bizzan.bitrade.entity.transform.AuthMember;
import com.bizzan.bitrade.event.MemberEvent;
import com.bizzan.bitrade.service.CountryService;
import com.bizzan.bitrade.service.LocaleMessageSourceService;
import com.bizzan.bitrade.service.MemberOperationLogService;
import com.bizzan.bitrade.service.MemberService;
import com.bizzan.bitrade.service.SysConfigService;
import com.bizzan.bitrade.system.IpSearch;
import com.bizzan.bitrade.util.*;
import com.bizzan.bitrade.vendor.provider.support.EmailProvider;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.mail.MessagingException;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.bizzan.bitrade.constant.SysConstant.*;
import static org.springframework.util.Assert.isTrue;
import static org.springframework.util.Assert.notNull;

/**
 * 会员注册
 *
 * @author QQ 2232965526
 * @date 2020年12月29日
 */
@Api(value = "RegisterController" , tags = "注册接口，包含注册，国家代码，邮件验证码发送")
@Controller
@Slf4j
public class RegisterController extends BaseController {

    @Value("${spring.mail.username}")
    private String from;
    @Value("${spark.system.host}")
    private String host;
    @Value("BBBDs")
    private String company;

    @Value("${aliyun.mail-sms.region}")
    private String e_Region;
    @Value("${aliyun.mail-sms.access-key-id}")
    private String e_accessKeyId;
    @Value("${aliyun.mail-sms.access-secret}")
    private String e_accessSecret;
    @Value("${aliyun.mail-sms.from-address}")
    private String emailFromAddress;
    @Value("${aliyun.mail-sms.from-alias}")
    private String emailAlias;
    @Value("${aliyun.mail-sms.email-tag}")
    private String emailTag;

    @Autowired
    private EmailProvider aliyunEmailAPI;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private MemberService memberService;

    @Autowired
    private IdWorkByTwitter idWorkByTwitter;

    @Autowired
    private MemberEvent memberEvent;

    @Autowired
    private CountryService countryService;
    @Autowired
    private SysConfigService sysConfigService;
    @Autowired
    private MemberOperationLogService memberOperationLogService;

    @Resource
    private LocaleMessageSourceService localeMessageSourceService;

    @Value("${dev.mode.vcode:}")
    private String devModeVcode;
    @Value("${dev.mode.enabled:false}")
    private boolean devModeEnabled;

    @Value("${member.default.avatar-url:}")
    private String defaultAvatarUrl;

    private String userNameFormat = "U%06d";
    //短信上行验证
    @Value("${netease.captcha.id:}")
    private String captchaId;
    @Value("${netease.captcha.secret-id:}")
    private String captchaSecretId;
    @Value("${netease.captcha.secret-key:}")
    private String captchaSecretKey;

    private NECaptchaVerifier verifier;

    // 邮件配置（使用已有的 aliyun.mail-sms 凭据）
    private IClientProfile profile;
    private IAcsClient client;
    private SingleSendMailRequest request = new SingleSendMailRequest();

    @PostConstruct
    private void init() {
        if (!captchaId.isEmpty()) {
            verifier = new NECaptchaVerifier(captchaId, new NESecretPair(captchaSecretId, captchaSecretKey));
        }
        if (!e_accessKeyId.isEmpty()) {
            profile = DefaultProfile.getProfile(e_Region.isEmpty() ? "cn-hangzhou" : e_Region, e_accessKeyId, e_accessSecret);
            client = new DefaultAcsClient(profile);
        }
    }

    /**
     * 注册支持的国家
     *
     * @return
     */
    @ApiOperation(value="获取国家列表", notes="获取国家列表")
    @RequestMapping(value = "/support/country", method = RequestMethod.POST)
    @ResponseBody
    public MessageResult allCountry() {
        MessageResult result = success();
        List<Country> list = countryService.getAllCountry();
        result.setData(list);
        return result;
    }
    //阿里云注册支持的国家
//    @RequestMapping(value = "/support/country", method = RequestMethod.POST)
//    @ResponseBody
//    public MessageResult allCountry() {
//        MessageResult result = success();
//        List<Country> list = countryService.getAllCountry();
//        List<CountryVo> countryList = new ArrayList<>();
//        list.stream().peek(
//                e->{
//                    CountryVo countryVo = new CountryVo();
//                    countryVo.setZhName(e.getZhName());
//                    countryVo.setAreaCode(e.getAreaCode());
//                    countryVo.setEnName(e.getEnName());
//                    countryVo.setLanguage(e.getLanguage());
//                    countryVo.setLocalCurrency(e.getLocalCurrency());
//                    countryVo.setSort(e.getSort());
//                    countryVo.setTranslation(localeMessageSourceService.getMessage("COUNTRY_TRANSLATIONS_"+e.getEnName()));
//                    countryList.add(countryVo);
//                }
//        ).collect(Collectors.toList());
//        result.setData(countryList);
//        return result;
//    }

    /**
     * 检查用户名是否重复
     *
     * @param username
     * @return
     */
    @ApiOperation(value="检测用户名是否重复", notes="检测用户名是否重复")
    @PostMapping(value = "/register/check/username")
    @ResponseBody
    public MessageResult checkUsername(String username) {
        MessageResult result = success();
        if (memberService.usernameIsExist(username)) {
            result.setCode(500);
            result.setMessage(localeMessageSourceService.getMessage("ACTIVATION_FAILS_USERNAME"));
        }
        return result;
    }
    
    @RequestMapping(value = "/config/get")
    @ResponseBody
    public MessageResult getConfig() {
        MessageResult result = success();
        result.setData(sysConfigService.get());
        return result;
    }


    /**
     * 激活邮件  暂时注掉
     *
     * @param key
     * @param request
     * @return
     * @throws Exception
     */
    //@RequestMapping(value = "/register/active")
    @Transactional(rollbackFor = Exception.class)
    public String activate(String key, HttpServletRequest request) throws Exception {
        if (StringUtils.isEmpty(key)) {
            request.setAttribute("result", localeMessageSourceService.getMessage("INVALID_LINK"));
            return "registeredResult";
        }
        ValueOperations valueOperations = redisTemplate.opsForValue();
        Object info = valueOperations.get(key);
        LoginByEmail loginByEmail = null;
        if (info instanceof LoginByEmail) {
            loginByEmail = (LoginByEmail) info;
        }
        if (loginByEmail == null) {
            request.setAttribute("result", localeMessageSourceService.getMessage("INVALID_LINK"));
            return "registeredResult";
        }
        if (memberService.emailIsExist(loginByEmail.getEmail())) {
            request.setAttribute("result", localeMessageSourceService.getMessage("ACTIVATION_FAILS_EMAIL"));
            return "registeredResult";
        } else if (memberService.usernameIsExist(loginByEmail.getUsername())) {
            request.setAttribute("result", localeMessageSourceService.getMessage("ACTIVATION_FAILS_USERNAME"));
            return "registeredResult";
        }
        //删除redis里存的键值
        valueOperations.getOperations().delete(key);
        valueOperations.getOperations().delete(loginByEmail.getEmail());
        //不可重复随机数
        String loginNo = String.valueOf(idWorkByTwitter.nextId());
        //盐
        String credentialsSalt = ByteSource.Util.bytes(loginNo).toHex();
        //生成密码
        String password = Md5.md5Digest(loginByEmail.getPassword() + credentialsSalt).toLowerCase();
        Member member = new Member();

        member.setMemberLevel(MemberLevelEnum.GENERAL);
        Location location = new Location();
        location.setCountry(loginByEmail.getCountry());
        member.setLocation(location);
        Country country = new Country();
        country.setZhName(loginByEmail.getCountry());
        member.setCountry(country);
        member.setUsername(loginByEmail.getUsername());
        member.setPassword(password);
        member.setEmail(loginByEmail.getEmail());
        member.setSalt(credentialsSalt);
        member.setAvatar(defaultAvatarUrl); // 默认用户头像
        Member member1 = memberService.save(member);

        if (member1 != null) {
        	// Member为@entity注解类，与数据库直接映射，因此，此处setPromotionCode会直接同步到数据库
            member1.setPromotionCode(GeneratorUtil.getPromotionCode(member1.getId()));
            memberEvent.onRegisterSuccess(member1, loginByEmail.getPromotion().trim());
        }
        return "registeredResult";
    }

    /**
     * 邮箱注册
     *
     * @param loginByEmail
     * @param bindingResult
     * @return
     */
    @ApiOperation(value="邮箱注册", notes="邮箱注册")
    @PostMapping("/register/email")
    @ResponseBody
    public MessageResult registerByEmail(@Valid LoginByEmail loginByEmail, BindingResult bindingResult, HttpServletRequest request) throws Exception {
        log.info("[注册流程-邮箱] 开始, email={}, username={}, country={}, promotion={}, ip={}",
                loginByEmail.getEmail(), loginByEmail.getUsername(), loginByEmail.getCountry(),
                loginByEmail.getPromotion(), getRemoteIp(request));
        MessageResult result = BindingResultUtil.validate(bindingResult);
        if (result != null) {
            log.info("[注册流程-邮箱] 参数校验失败, result={}", result.getMessage());
            return result;
        }
        String email = loginByEmail.getEmail();
        if (memberService.emailIsExist(email)) {
            log.info("[注册流程-邮箱] 邮箱已存在, email={}", email);
            return error(localeMessageSourceService.getMessage("EMAIL_ALREADY_BOUND"));
        }
        if (memberService.usernameIsExist(loginByEmail.getUsername())) {
            log.info("[注册流程-邮箱] 用户名已存在, username={}", loginByEmail.getUsername());
            return error(localeMessageSourceService.getMessage("USERNAME_ALREADY_EXISTS"));
        }
        if (StringUtils.hasText(loginByEmail.getPromotion().trim())) {
            if (!memberService.userPromotionCodeIsExist(loginByEmail.getPromotion())) {
                log.info("[注册流程-邮箱] 推广码不存在, promotion={}", loginByEmail.getPromotion());
                return error(localeMessageSourceService.getMessage("USER_PROMOTION_CODE_EXISTS"));
            }
        }
        // isTrue(memberService.userPromotionCodeIsExist(loginByEmail.getPromotion()),localeMessageSourceService.getMessage("USER_PROMOTION_CODE_EXISTS"));
        ValueOperations valueOperations = redisTemplate.opsForValue();

        boolean isDevBypass = !devModeVcode.isEmpty() && devModeVcode.equals(loginByEmail.getCode());
        if (!isDevBypass) {
	        Object code = valueOperations.get(SysConstant.EMAIL_REG_CODE_PREFIX + email);
	        if (code == null) {
	            log.info("[注册流程-邮箱] Redis验证码不存在或已过期, email={}", email);
	            return error(localeMessageSourceService.getMessage("VERIFICATION_CODE_NOT_EXISTS"));
	        }
	        if (!code.toString().equals(loginByEmail.getCode())) {
	            log.info("[注册流程-邮箱] 验证码错误, email={}, expected={}, actual={}", email, code, loginByEmail.getCode());
	            return error(localeMessageSourceService.getMessage("VERIFICATION_CODE_INCORRECT"));
	        } else {
	            valueOperations.getOperations().delete(SysConstant.EMAIL_REG_CODE_PREFIX + email);
	            log.info("[注册流程-邮箱] 验证码校验通过, 已删除Redis缓存, email={}", email);
	        }
        }

        //if(!invokeUtil.register(loginByEmail.getUsername(), loginByEmail.getPassword(), 2))
        //	return error(localeMessageSourceService.getMessage("REGISTRATION_FAILED"));
        

        //不可重复随机数
        String loginNo = String.valueOf(idWorkByTwitter.nextId());
        //盐
        String credentialsSalt = ByteSource.Util.bytes(loginNo).toHex();
        //生成密码
        String password = Md5.md5Digest(loginByEmail.getPassword() + credentialsSalt).toLowerCase();
        Member member = new Member();

        member.setMemberLevel(MemberLevelEnum.GENERAL);
        Location location = new Location();
        location.setCountry(loginByEmail.getCountry());
        member.setLocation(location);
        Country country = new Country();
        country.setZhName(loginByEmail.getCountry());
        member.setCountry(country);
        member.setUsername(loginByEmail.getUsername());
        member.setPassword(password);
        member.setEmail(loginByEmail.getEmail());
        member.setSalt(credentialsSalt);
        member.setRegistrationIp(getRemoteIp(request));
        member.setAvatar(defaultAvatarUrl); // 默认用户头像
        log.info("[注册流程-邮箱] 准备写入数据库, username={}, email={}", loginByEmail.getUsername(), email);
        Member member1 = memberService.save(member);

        if (member1 != null) {
            log.info("[注册流程-邮箱] 用户保存成功, memberId={}, username={}", member1.getId(), member1.getUsername());
            // Member为@entity注解类，与数据库直接映射，因此，此处setPromotionCode会直接同步到数据库
        	member1.setPromotionCode(getPromotionCode());
            log.info("[注册流程-邮箱] 推广码已设置, memberId={}, promotionCode={}", member1.getId(), member1.getPromotionCode());
            memberEvent.onRegisterSuccess(member1, loginByEmail.getPromotion().trim());
            memberOperationLogService.save(member1.getId(), MemberOperationType.REGISTER, getRemoteIp(request), null, "email");
            log.info("[注册流程-邮箱] 注册完成, memberId={}, 已触发Kafka注册事件", member1.getId());
            return success(localeMessageSourceService.getMessage("REGISTRATION_SUCCESS"));
        } else {
            log.warn("[注册流程-邮箱] 用户保存失败, username={}, email={}", loginByEmail.getUsername(), email);
            return error(localeMessageSourceService.getMessage("REGISTRATION_FAILED"));
        }
    }

//    @Async
//    public void sentEmail(ValueOperations valueOperations, LoginByEmail loginByEmail, String email) throws MessagingException, IOException, TemplateException {
//        //缓存邮箱和注册信息
//        String token = UUID.randomUUID().toString().replace("-", "");
//        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
//        MimeMessageHelper helper = null;
//        helper = new MimeMessageHelper(mimeMessage, true);
//        helper.setFrom(from);
//        helper.setTo(email);
//        helper.setSubject(company);
//        Map<String, Object> model = new HashMap<>(16);
//        model.put("username", loginByEmail.getUsername());
//        model.put("token", token);
//        model.put("host", host);
//        model.put("name", company);
//        Configuration cfg = new Configuration(Configuration.VERSION_2_3_26);
//        cfg.setClassForTemplateLoading(this.getClass(), "/templates");
//        Template template = cfg.getTemplate("activateEmail.ftl");
//        String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
//        helper.setText(html, true);
//        //发送邮件
//        javaMailSender.send(mimeMessage);
//        valueOperations.set(token, loginByEmail, 12, TimeUnit.HOURS);
//        valueOperations.set(email, "", 12, TimeUnit.HOURS);
//    }

    /**
     * 手机注册
     *
     * @param loginByPhone
     * @param bindingResult
     * @return
     * @throws Exception
     */
    @ApiOperation(value="手机注册", notes="手机注册")
    @PostMapping("/register/for_phone")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public MessageResult loginByPhone(
            @Valid LoginByPhone loginByPhone,
            BindingResult bindingResult,HttpServletRequest request) throws Exception {
        log.info("[注册流程-手机] 开始, phone={}, username={}, country={}, promotion={}, ip={}",
                loginByPhone.getPhone(), loginByPhone.getUsername(), loginByPhone.getCountry(),
                loginByPhone.getPromotion(), getRemoteIp(request));
        MessageResult result = BindingResultUtil.validate(bindingResult);
        if (result != null) {
            log.info("[注册流程-手机] 参数校验失败, result={}", result.getMessage());
            return result;
        }

//        if ("中国".equals(loginByPhone.getCountry())) {
//            Assert.isTrue(ValidateUtil.isMobilePhone(loginByPhone.getPhone().trim()), localeMessageSourceService.getMessage("PHONE_EMPTY_OR_INCORRECT"));
//        }
        String ip = request.getHeader("X-Real-IP");
        String phone = loginByPhone.getPhone();
        ValueOperations valueOperations = redisTemplate.opsForValue();

        isTrue(!memberService.phoneIsExist(phone), localeMessageSourceService.getMessage("PHONE_ALREADY_EXISTS"));
        isTrue(!memberService.usernameIsExist(loginByPhone.getUsername()), localeMessageSourceService.getMessage("USERNAME_ALREADY_EXISTS"));
        if (StringUtils.hasText(loginByPhone.getPromotion().trim())) {
        	isTrue(memberService.userPromotionCodeIsExist(loginByPhone.getPromotion()),localeMessageSourceService.getMessage("USER_PROMOTION_CODE_EXISTS"));
        }
        //校验是否通过验证码 短信上行
//         isTrue(verifier.verify(loginByPhone.getValidate(),""),localeMessageSourceService.getMessage("VERIFICATION_CODE_INCORRECT"));
//        腾讯防水注册校验
//        isTrue(gtestCon.watherProof(loginByPhone.getTicket(),loginByPhone.getRandStr(),ip),localeMessageSourceService.getMessage("VERIFICATION_PICTURE_NOT_CORRECT"));
//         短信验证码检查
        boolean isDevBypass = !devModeVcode.isEmpty() && devModeVcode.equals(loginByPhone.getCode());
        if (!isDevBypass) {
	        Object code =valueOperations.get(SysConstant.PHONE_REG_CODE_PREFIX + phone);
	        notNull(code, localeMessageSourceService.getMessage("VERIFICATION_CODE_NOT_EXISTS"));
	        if (!code.toString().equals(loginByPhone.getCode())) {
	            return error(localeMessageSourceService.getMessage("VERIFICATION_CODE_INCORRECT"));
	        } else {
	            valueOperations.getOperations().delete(SysConstant.PHONE_REG_CODE_PREFIX + phone);
	        }
        }

        //if(!invokeUtil.register(loginByPhone.getUsername(), loginByPhone.getPassword(), 1))
        //	return error(localeMessageSourceService.getMessage("REGISTRATION_FAILED"));

        //不可重复随机数
        String loginNo = String.valueOf(idWorkByTwitter.nextId());
        //盐
        String credentialsSalt = ByteSource.Util.bytes(loginNo).toHex();
        //生成密码
        String password = Md5.md5Digest(loginByPhone.getPassword() + credentialsSalt).toLowerCase();
        Member member = new Member();
        //新增超级合伙人 判断0  普通 默认普通用户   1 超级合伙人 >>2.专业超级合伙人
        if(!StringUtils.isEmpty(loginByPhone.getSuperPartner())){
            member.setSuperPartner(loginByPhone.getSuperPartner());
            if (!"0".equals(loginByPhone.getSuperPartner())) {
                //需要后台审核解禁
                member.setStatus(CommonStatus.ILLEGAL);
            }
        }
        member.setMemberLevel(MemberLevelEnum.GENERAL);
        Location location = new Location();
        location.setCountry(loginByPhone.getCountry());

        List<Country> list = countryService.getAllCountry();
        Country country = null;
        for(Country country1 : list) {
            if (country1.getZhName().equals(loginByPhone.getCountry())) {
                country = country1;
                break;
            }
        }
        if(country == null){
            return MessageResult.error("");
        } else {
            member.setCountry(country);
        }
        member.setLocation(location);
        member.setUsername(loginByPhone.getUsername());
        member.setPassword(password);
        member.setMobilePhone(phone);
        member.setSalt(credentialsSalt);
        member.setAvatar(defaultAvatarUrl); // 默认用户头像
        log.info("[注册流程-手机] 准备写入数据库, username={}, phone={}", loginByPhone.getUsername(), phone);
        Member member1 = memberService.save(member);
        if (member1 != null) {
            log.info("[注册流程-手机] 用户保存成功, memberId={}, username={}", member1.getId(), member1.getUsername());
        	// Member为@entity注解类，与数据库直接映射，因此，此处setPromotionCode会直接同步到数据库
            //member1.setPromotionCode(GeneratorUtil.getPromotionCode(member1.getId()));
        	member1.setPromotionCode(getPromotionCode());
            memberEvent.onRegisterSuccess(member1, loginByPhone.getPromotion().trim());
            memberOperationLogService.save(member1.getId(), MemberOperationType.REGISTER, getRemoteIp(request), null, "phone");
            log.info("[注册流程-手机] 注册完成, memberId={}, 已触发Kafka注册事件", member1.getId());
            return success(localeMessageSourceService.getMessage("REGISTRATION_SUCCESS"));
        } else {
            log.warn("[注册流程-手机] 用户保存失败, username={}, phone={}", loginByPhone.getUsername(), phone);
            return error(localeMessageSourceService.getMessage("REGISTRATION_FAILED"));
        }
    }

    /**
     * 手机注册暂时关闭
     * @param loginByPhone
     * @param bindingResult
     * @param request
     * @return
     * @throws Exception
     */
//    @RequestMapping("/register/for_phone")
//    @ResponseBody
//    @Transactional(rollbackFor = Exception.class)
//    public MessageResult loginByPhone4Mobiles(
//            @Valid LoginByPhone loginByPhone,
//            BindingResult bindingResult,HttpServletRequest request) throws Exception {
//        log.info("============请到PC端注册---registerPC");
//        return error(localeMessageSourceService.getMessage("REGISTER_TO_PC"));
//    }
    /**
     * 为了手机注册
     * @param loginByPhone
     * @param bindingResult
     * @param request
     * @return
     * @throws Exception
     */
    //@RequestMapping("/register/for_phone")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public MessageResult loginByPhone4Mobile(
            @Valid LoginByPhone loginByPhone,
            BindingResult bindingResult,HttpServletRequest request) throws Exception {
        MessageResult result = BindingResultUtil.validate(bindingResult);
        if (result != null) {
            return result;
        }

//        if (loginByPhone.getCountry().equals("中国")) {
//            Assert.isTrue(ValidateUtil.isMobilePhone(loginByPhone.getPhone().trim()), localeMessageSourceService.getMessage("PHONE_EMPTY_OR_INCORRECT"));
//        }
        String ip = request.getHeader("X-Real-IP");
        String phone = loginByPhone.getPhone();
        ValueOperations valueOperations = redisTemplate.opsForValue();
        Object code =valueOperations.get(SysConstant.PHONE_REG_CODE_PREFIX + phone);
        isTrue(!memberService.phoneIsExist(phone), localeMessageSourceService.getMessage("PHONE_ALREADY_EXISTS"));
        isTrue(!memberService.usernameIsExist(loginByPhone.getUsername()), localeMessageSourceService.getMessage("USERNAME_ALREADY_EXISTS"));
        if (StringUtils.hasText(loginByPhone.getPromotion().trim())) {
        	isTrue(memberService.userPromotionCodeIsExist(loginByPhone.getPromotion()),localeMessageSourceService.getMessage("USER_PROMOTION_CODE_EXISTS"));
        }
        
//        isTrue(memberService.userPromotionCodeIsExist(loginByPhone.getPromotion()),localeMessageSourceService.getMessage("USER_PROMOTION_CODE_EXISTS"));
        //校验是否通过验证码 短信上行
        //isTrue(verifier.verify(loginByPhone.getValidate(),""),localeMessageSourceService.getMessage("VERIFICATION_CODE_INCORRECT"));

        // 换种校验方式
        notNull(code, localeMessageSourceService.getMessage("VERIFICATION_CODE_NOT_EXISTS"));
        if (!code.toString().equals(loginByPhone.getCode())) {
            return error(localeMessageSourceService.getMessage("VERIFICATION_CODE_INCORRECT"));
        } else {
            valueOperations.getOperations().delete(SysConstant.PHONE_REG_CODE_PREFIX + phone);
        }
        
        //if(!invokeUtil.register(loginByPhone.getUsername(), loginByPhone.getPassword(), 1))
        //	return error(localeMessageSourceService.getMessage("REGISTRATION_FAILED"));
        
        //不可重复随机数
        String loginNo = String.valueOf(idWorkByTwitter.nextId());
        //盐
        String credentialsSalt = ByteSource.Util.bytes(loginNo).toHex();
        //生成密码
        String password = Md5.md5Digest(loginByPhone.getPassword() + credentialsSalt).toLowerCase();
        
        String token = request.getHeader("access-auth-token");
        if (!StringUtils.isEmpty(token)) {
        	//memberService.updateExistingToken(token);
        }
        
        Member member = new Member();
        //新增超级合伙人 判断0  普通 默认普通用户   1 超级合伙人 >>2.专业超级合伙人
        if(!StringUtils.isEmpty(loginByPhone.getSuperPartner())){
            member.setSuperPartner(loginByPhone.getSuperPartner());
            if (!"0".equals(loginByPhone.getSuperPartner())) {
                //需要后台审核解禁
                member.setStatus(CommonStatus.ILLEGAL);
            }
        }
        member.setMemberLevel(MemberLevelEnum.GENERAL);
        Location location = new Location();
        location.setCountry(loginByPhone.getCountry());
        Country country = new Country();
        country.setZhName(loginByPhone.getCountry());
        member.setCountry(country);
        member.setLocation(location);
        member.setUsername(loginByPhone.getUsername());
        member.setPassword(password);
        member.setMobilePhone(phone);
        member.setSalt(credentialsSalt);
        Member member1 = memberService.save(member);
        if (member1 != null) {
            //member1.setPromotionCode(GeneratorUtil.getPromotionCode(member1.getId()));
        	member1.setPromotionCode(getPromotionCode());
            // 增加钱包记录
            memberEvent.onRegisterSuccess(member1, loginByPhone.getPromotion());
            return success(localeMessageSourceService.getMessage("REGISTRATION_SUCCESS"));
        } else {
            return error(localeMessageSourceService.getMessage("REGISTRATION_FAILED"));
        }
    }
    
    public String getPromotionCode() {
    	String promotionCode = "";
    	for(int i=0; i<100; i++) {
        	promotionCode = GeneratorUtil.getNewPromotionCode();
        	if(memberService.findMemberByPromotionCode(promotionCode)==null) {
        		return promotionCode;
        		//member1.setPromotionCode(promotionCode);
        		//break;
        	}
        }
    	return promotionCode;
    }
    /**
     * 发送绑定邮箱验证码
     *
     * @param email
     * @param user
     * @return
     */
    @ApiOperation(value="发送绑定邮箱验证码", notes="发送绑定邮箱验证码")
    @PostMapping("/bind/email/code")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public MessageResult sendBindEmail(String email, HttpServletRequest request, @SessionAttribute(SESSION_MEMBER) AuthMember user) {
        Assert.isTrue(ValidateUtil.isEmail(email), localeMessageSourceService.getMessage("WRONG_EMAIL"));
        Member member = memberService.findOne(user.getId());
        Assert.isNull(member.getEmail(), localeMessageSourceService.getMessage("BIND_EMAIL_REPEAT"));
        Assert.isTrue(!memberService.emailIsExist(email), localeMessageSourceService.getMessage("EMAIL_ALREADY_BOUND"));
        String code = String.valueOf(GeneratorUtil.getRandomNumber(100000, 999999));
        ValueOperations valueOperations = redisTemplate.opsForValue();
        if (valueOperations.get(EMAIL_BIND_CODE_PREFIX + email) != null) {
            return error(localeMessageSourceService.getMessage("EMAIL_ALREADY_SEND"));
        }
        if (devModeEnabled) {
            valueOperations.set(EMAIL_BIND_CODE_PREFIX + email, code, 10, TimeUnit.MINUTES);
            log.info("[dev模式] 绑定邮箱验证码已写入Redis, email={}, code={}", email, code);
            return success(localeMessageSourceService.getMessage("SENT_SUCCESS_TEN"));
        }
        try {
        	String ip = getRemoteIp(request);
        	sentCommonEmailCode(valueOperations, EMAIL_BIND_CODE_PREFIX, email, code, ip);
        } catch (Exception e) {
            e.printStackTrace();
            return error(localeMessageSourceService.getMessage("SEND_FAILED"));
        }
        return success(localeMessageSourceService.getMessage("SENT_SUCCESS_TEN"));
    }

    /**
     * 增加提币地址验证码
     *
     * @param user
     * @return
     */
    @ApiOperation(value="发送提币地址验证码", notes="发送提币地址验证码")
    @PostMapping("/add/address/code")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public MessageResult sendAddAddress(@SessionAttribute(SESSION_MEMBER) AuthMember user, HttpServletRequest request) {
        String code = String.valueOf(GeneratorUtil.getRandomNumber(100000, 999999));
        ValueOperations valueOperations = redisTemplate.opsForValue();
        Member member = memberService.findOne(user.getId());
        String email = member.getEmail();
        if (email == null) {
            return error(localeMessageSourceService.getMessage("NOT_BIND_EMAIL"));
        }
        if (valueOperations.get(ADD_ADDRESS_CODE_PREFIX + email) != null) {
            return error(localeMessageSourceService.getMessage("EMAIL_ALREADY_SEND"));
        }
        if (devModeEnabled) {
            valueOperations.set(ADD_ADDRESS_CODE_PREFIX + email, code, 10, TimeUnit.MINUTES);
            log.info("[dev模式] 添加提币地址邮箱验证码已写入Redis, email={}, code={}", email, code);
            return success(localeMessageSourceService.getMessage("SENT_SUCCESS_TEN"));
        }
        try {
            //sentEmailAddCode(valueOperations, email, code);
        	String ip = getRemoteIp(request);
        	sentCommonEmailCode(valueOperations, ADD_ADDRESS_CODE_PREFIX, email, code, ip);
        } catch (Exception e) {
            e.printStackTrace();
            return error(localeMessageSourceService.getMessage("SEND_FAILED"));
        }
        return success(localeMessageSourceService.getMessage("SENT_SUCCESS_TEN"));
    }

    @Async
    public void sentEmailAddCode(ValueOperations valueOperations, String email, String code) throws MessagingException, IOException, TemplateException {
        Map<String, Object> model = new HashMap<>(16);
        model.put("code", code);
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_26);
        cfg.setClassForTemplateLoading(this.getClass(), "/templates");
        Template template = cfg.getTemplate("addAddressCodeEmail.ftl");
        String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
        aliyunEmailAPI.sendSingleSendMail(email, "验证码", html);
        valueOperations.set(ADD_ADDRESS_CODE_PREFIX + email, code, 10, TimeUnit.MINUTES);
    }

    @ApiOperation(value="发送重置密码验证码", notes="发送重置密码验证码")
    @PostMapping("/reset/email/code")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public MessageResult sendResetPasswordCode(HttpServletRequest request, String account) {
    	account = account.trim();
        Member member = memberService.findByEmail(account);
        if(null == member) {
            return error(localeMessageSourceService.getMessage("MEMBER_NOT_EXISTS"));
        }
//        Assert.notNull(member, localeMessageSourceService.getMessage("MEMBER_NOT_EXISTS"));
        ValueOperations valueOperations = redisTemplate.opsForValue();
        if (valueOperations.get(RESET_PASSWORD_CODE_PREFIX + account) != null) {
            return error(localeMessageSourceService.getMessage("EMAIL_ALREADY_SEND"));
        }
        try {
            String code = String.valueOf(GeneratorUtil.getRandomNumber(100000, 999999));
            if (devModeEnabled) {
                valueOperations.set(RESET_PASSWORD_CODE_PREFIX + account, code, 10, TimeUnit.MINUTES);
                log.info("[dev模式] 重置密码邮箱验证码已写入Redis, account={}, code={}", account, code);
            } else {
                sentResetPassword(valueOperations, account, code);
                //String ip = getRemoteIp(request);
                //sentCommonEmailCode(valueOperations, RESET_PASSWORD_CODE_PREFIX, account, code, ip);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return error(localeMessageSourceService.getMessage("SEND_FAILED"));
        }
        return success(localeMessageSourceService.getMessage("SENT_SUCCESS_TEN"));
    }

    @Async
    public void sentResetPassword(ValueOperations valueOperations, String email, String code) throws MessagingException, IOException, TemplateException {
        Map<String, Object> model = new HashMap<>(16);
        model.put("code", code);
        SysConfig sysConfig = sysConfigService.get();
    	model.put("projectName", sysConfig.getProjectName());
    	model.put("projectLogo", sysConfig.getProjectLogo());
    	
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_26);
        cfg.setClassForTemplateLoading(this.getClass(), "/templates");
        
        model.put("android", sysConfig.getPromotionPrefix());
        model.put("ios", sysConfig.getPromotionPrefix());
        
        Template bottomTemplate = cfg.getTemplate("bottom.ftl");
        String bottomHtml = FreeMarkerTemplateUtils.processTemplateIntoString(bottomTemplate, model);
        model.put("bottomHtml", bottomHtml);
        
        Template template = cfg.getTemplate("resetPasswordCodeEmail.ftl");
        String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
        aliyunEmailAPI.sendSingleSendMail(email, "验证码", html);
        valueOperations.set(RESET_PASSWORD_CODE_PREFIX + email, code, 10, TimeUnit.MINUTES);
    }

    /**
     * 忘记密码后重置密码
     *
     * @param mode     0为手机验证，1为邮箱验证
     * @param account  手机或邮箱
     * @param code     验证码
     * @param password 新密码
     * @return
     */
    @ApiOperation(value="忘记密码后重置密码", notes="忘记密码后重置密码")
    @ApiImplicitParams(value = {
    		@ApiImplicitParam(name = "mode",value = "0为手机验证，1为邮箱验证",required = true,dataType = "int"),
    		@ApiImplicitParam(name = "account",value = "手机或邮箱",required = true,dataType = "string"),
    		@ApiImplicitParam(name = "code",value = "验证码",required = true,dataType = "string"),
    		@ApiImplicitParam(name = "password",value = "新密码",required = true,dataType = "string")
    })
    @RequestMapping(value = "/reset/login/password", method = RequestMethod.POST)
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public MessageResult forgetPassword(int mode, String account, String code, String password) throws Exception {
        account = account.trim();
    	Member member = null;
        ValueOperations valueOperations = redisTemplate.opsForValue();
        boolean isDevBypass = !devModeVcode.isEmpty() && devModeVcode.equals(code);
        if (!isDevBypass) {
	        Object redisCode = valueOperations.get(SysConstant.RESET_PASSWORD_CODE_PREFIX + account);
//	        notNull(redisCode, localeMessageSourceService.getMessage("VERIFICATION_CODE_NOT_EXISTS"));
            if(null == redisCode){
                return error(localeMessageSourceService.getMessage("VERIFICATION_CODE_NOT_EXISTS"));
            }
	        if (mode == 0) {
	            member = memberService.findByPhone(account);
	        } else if (mode == 1) {
	            member = memberService.findByEmail(account);
	        }
//	        notNull(member, localeMessageSourceService.getMessage("MEMBER_NOT_EXISTS"));
            if(null == member){
                return error(localeMessageSourceService.getMessage("MEMBER_NOT_EXISTS"));
            }
	        if(Md5.md5Digest(password + member.getSalt()).toLowerCase().equals(member.getPassword())) {
                return error(localeMessageSourceService.getMessage("DO_NOT_OLD_PASSWORD"));
            }
//	        isTrue(password.length() >= 6 && password.length() <= 20, localeMessageSourceService.getMessage("PASSWORD_LENGTH_ILLEGAL"));
            if(password.length() < 6 || password.length() > 20){
                return error(localeMessageSourceService.getMessage("PASSWORD_LENGTH_ILLEGAL"));
            }
	        if (!code.equals(redisCode.toString())) {
	            return error(localeMessageSourceService.getMessage("VERIFICATION_CODE_INCORRECT"));
	        } else {
	            valueOperations.getOperations().delete(SysConstant.RESET_PASSWORD_CODE_PREFIX + account);
	        }
        } else {
	        if (mode == 0) {
	            member = memberService.findByPhone(account);
	        } else if (mode == 1) {
	            member = memberService.findByEmail(account);
	        }
//	        notNull(member, localeMessageSourceService.getMessage("MEMBER_NOT_EXISTS"));
            if(null == member){
                return error(localeMessageSourceService.getMessage("MEMBER_NOT_EXISTS"));
            }
	        if(Md5.md5Digest(password + member.getSalt()).toLowerCase().equals(member.getPassword()))
	        	return error(localeMessageSourceService.getMessage("DO_NOT_OLD_PASSWORD"));
//	        isTrue(password.length() >= 6 && password.length() <= 20, localeMessageSourceService.getMessage("PASSWORD_LENGTH_ILLEGAL"));
            if(password.length() < 6 || password.length() > 20){
                return error(localeMessageSourceService.getMessage("PASSWORD_LENGTH_ILLEGAL"));
            }
        }
        //if(!invokeUtil.resetPassword(account, password, mode))
        //	return MessageResult.error(localeMessageSourceService.getMessage("MEMBER_NOT_EXISTS"));
        //生成密码
        String newPassword = Md5.md5Digest(password + member.getSalt()).toLowerCase();
        member.setPassword(newPassword);
        return success();
    }
    /**
     * 发送解绑旧邮箱验证码
     *
     * @param user
     * @return
     */
    @ApiOperation(value="发送解绑旧邮箱验证码", notes="发送解绑旧邮箱验证码")
    @RequestMapping(value = "/untie/email/code", method = RequestMethod.POST)
    @ResponseBody
    public MessageResult untieEmailCode(@SessionAttribute(SESSION_MEMBER) AuthMember user,HttpServletRequest request){
        Member member = memberService.findOne(user.getId());
//        isTrue(member.getEmail()!=null, localeMessageSourceService.getMessage("NOT_BIND_EMAIL"));
        if(null == member.getEmail()){
            return error(localeMessageSourceService.getMessage("NOT_BIND_EMAIL"));
        }
        ValueOperations valueOperations = redisTemplate.opsForValue();
        Object cache = valueOperations.get(SysConstant.EMAIL_UNTIE_CODE_PREFIX + member.getEmail());
        if(cache!=null){
            return error(localeMessageSourceService.getMessage("EMAIL_ALREADY_SEND"));
        }
        String code = String.valueOf(GeneratorUtil.getRandomNumber(100000, 999999));
        if (devModeEnabled) {
            valueOperations.set(SysConstant.EMAIL_UNTIE_CODE_PREFIX + member.getEmail(), code, 10, TimeUnit.MINUTES);
            log.info("[dev模式] 解绑旧邮箱验证码已写入Redis, email={}, code={}", member.getEmail(), code);
            return success();
        }
        try {
            //sentResetPassword(valueOperations, member.getEmail(), code);
        	String ip = getRemoteIp(request);
        	sentCommonEmailCode(valueOperations, EMAIL_UNTIE_CODE_PREFIX, member.getEmail(), code, ip);
        } catch (MessagingException | IOException | TemplateException e) {
            e.printStackTrace();
        }
        return success();
    }

    /**
     * 发送新邮箱验证码
     *
     * @param user
     * @return
     */
    @ApiOperation(value="发送新邮箱验证码", notes="发送新邮箱验证码")
    @RequestMapping(value = "/update/email/code", method = RequestMethod.POST)
    @ResponseBody
    public MessageResult updateEmailCode(@SessionAttribute(SESSION_MEMBER) AuthMember user,HttpServletRequest request, String email){
        if(memberService.emailIsExist(email)){
            return MessageResult.error(localeMessageSourceService.getMessage("REPEAT_EMAIL_REQUEST"));
        }
        Member member = memberService.findOne(user.getId());
        ValueOperations valueOperations = redisTemplate.opsForValue();
        isTrue(member.getEmail()!=null, localeMessageSourceService.getMessage("NOT_BIND_EMAIL"));
        Object cache = valueOperations.get(SysConstant.EMAIL_UPDATE_CODE_PREFIX + email);
        if(cache!=null){
            return error(localeMessageSourceService.getMessage("EMAIL_ALREADY_SEND"));
        }
        String code = String.valueOf(GeneratorUtil.getRandomNumber(100000, 999999));
        if (devModeEnabled) {
            valueOperations.set(SysConstant.EMAIL_UPDATE_CODE_PREFIX + email, code, 10, TimeUnit.MINUTES);
            log.info("[dev模式] 新邮箱验证码已写入Redis, email={}, code={}", email, code);
            return success();
        }
        try {
            //sentResetPassword(valueOperations, email, code);
        	String ip = getRemoteIp(request);
        	sentCommonEmailCode(valueOperations, EMAIL_UPDATE_CODE_PREFIX, email, code, ip);
        } catch (MessagingException | IOException | TemplateException e) {
            e.printStackTrace();
        }
        return success();
    }

    @ApiOperation(value="发送注册邮箱验证码", notes="发送注册邮箱验证码")
    @PostMapping("/reg/email/code")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public MessageResult sendRegEmail(HttpServletRequest request, @RequestHeader(value = "lang") String lang, String email) {
    	log.info("[注册流程-发验证码] 开始, email={}, lang={}, ip={}", email, lang, getRemoteIp(request));
        if(!ValidateUtil.isEmail(email)){
            return error(localeMessageSourceService.getMessage("WRONG_EMAIL"));
        }
//        Assert.isTrue(ValidateUtil.isEmail(email), localeMessageSourceService.getMessage("WRONG_EMAIL"));
        if(memberService.emailIsExist(email)){
            return error(localeMessageSourceService.getMessage("EMAIL_ALREADY_BOUND"));
        }
//        Assert.isTrue(!memberService.emailIsExist(email), localeMessageSourceService.getMessage("EMAIL_ALREADY_BOUND"));
        String code = String.valueOf(GeneratorUtil.getRandomNumber(100000, 999999));

    	String ip = getRemoteIp(request);
    	if (ip.equals("a")) {
    		return error(localeMessageSourceService.getMessage("EMAIL_ALREADY_SEND"));
    	}

    	if (devModeEnabled) {
    	    ValueOperations devOps = redisTemplate.opsForValue();
    	    devOps.set(EMAIL_REG_CODE_PREFIX + email, code, 10, TimeUnit.MINUTES);
    	    log.info("[dev模式] 注册邮箱验证码已写入Redis, email={}, code={}", email, code);
    	    return success(localeMessageSourceService.getMessage("SENT_SUCCESS_TEN"));
    	}

        String ipKey = "RGE_";
        if (ip.contains(".")) {
        	ipKey += ip.substring(0, ip.lastIndexOf("."));
        } else {
        	ipKey += ip;
        }
        ValueOperations valueOperations = redisTemplate.opsForValue();
        if (valueOperations.get(EMAIL_REG_CODE_PREFIX + email) != null) {
            log.info("[注册流程-发验证码] 验证码已存在，直接返回成功, email={}", email);
            return success(localeMessageSourceService.getMessage("SENT_SUCCESS_TEN"));
        }
        if (valueOperations.get(ipKey) != null) {
        	int count = Integer.parseInt(valueOperations.get(ipKey).toString());
        	if (count > 2) {
        		return error(localeMessageSourceService.getMessage("EMAIL_ALREADY_SEND"));
        	} else {
        		valueOperations.set(ipKey, count+1, 24, TimeUnit.HOURS);
        	}
        } else {
        	valueOperations.set(ipKey, 1, 24, TimeUnit.HOURS);
        }
        try {
            sentEmailRegCode(valueOperations, email, code, ip);
            log.info("[注册流程-发验证码] 验证码已发送, email={}, RedisKey={}, 10分钟有效", email, SysConstant.EMAIL_REG_CODE_PREFIX + email);
        } catch (Exception e) {
            log.error("[注册流程-发验证码] 发送失败, email={}", email, e);
            e.printStackTrace();
            return error(localeMessageSourceService.getMessage("SEND_FAILED"));
        }
        return success(localeMessageSourceService.getMessage("SENT_SUCCESS_TEN"));
    }

    @Async
    public void sentEmailRegCode(ValueOperations valueOperations, String email, String code, String ip) throws IOException, TemplateException {
    	Map<String, Object> model = this.getModelForEmail(ip, email);
    	//Map<String, Object> model = new HashMap<>(16);
    	
    	SysConfig sysConfig = sysConfigService.get();
    	model.put("projectName", sysConfig.getProjectName());
    	model.put("projectLogo", sysConfig.getProjectLogo());
    	
        model.put("code", code);
        model.put("account_pre", "注册");
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_26);
        cfg.setClassForTemplateLoading(this.getClass(), "/templates");
        
        model.put("android", sysConfig.getPromotionPrefix());
        model.put("ios", sysConfig.getPromotionPrefix());
        
        Template bottomTemplate = cfg.getTemplate("bottom.ftl");
        String bottomHtml = FreeMarkerTemplateUtils.processTemplateIntoString(bottomTemplate, model);
        model.put("bottomHtml", bottomHtml);
        
        Template template = cfg.getTemplate("register.ftl");
        String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
        aliyunEmailAPI.sendSingleSendMail(email, "注册", html);
        valueOperations.set(EMAIL_REG_CODE_PREFIX + email, code, 10, TimeUnit.MINUTES);
    }
    
    @ApiOperation(value="发送重置资金密码验证码", notes="发送重置资金密码验证码")
    @PostMapping("/transaction/email/code")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public MessageResult sendTransactionPasswordEmail(@SessionAttribute(SESSION_MEMBER) AuthMember user, HttpServletRequest request) {
        //Assert.isTrue(ValidateUtil.isEmail(email), localeMessageSourceService.getMessage("WRONG_EMAIL"));
        Member member = memberService.findOne(user.getId());
        Assert.isTrue(ValidateUtil.isEmail(member.getEmail()), localeMessageSourceService.getMessage("WRONG_EMAIL"));
        //Assert.isNull(member.getEmail(), localeMessageSourceService.getMessage("WRONG_EMAIL"));
        //Assert.isTrue(!memberService.emailIsExist(member.getEmail()), localeMessageSourceService.getMessage("EMAIL_ALREADY_BOUND"));
        String code = String.valueOf(GeneratorUtil.getRandomNumber(100000, 999999));
        ValueOperations valueOperations = redisTemplate.opsForValue();
        if (valueOperations.get(SysConstant.EMAIL_RESET_MONEY_CODE_PREFIX + member.getEmail()) != null) {
            return error(localeMessageSourceService.getMessage("EMAIL_ALREADY_SEND"));
        }
        if (devModeEnabled) {
            valueOperations.set(SysConstant.EMAIL_RESET_MONEY_CODE_PREFIX + member.getEmail(), code, 10, TimeUnit.MINUTES);
            log.info("[dev模式] 重置资金密码邮箱验证码已写入Redis, email={}, code={}", member.getEmail(), code);
            return success(localeMessageSourceService.getMessage("SENT_SUCCESS_TEN"));
        }
        try {
        	String ip = getRemoteIp(request);
        	sentCommonEmailCode(valueOperations, EMAIL_RESET_MONEY_CODE_PREFIX, member.getEmail(), code, ip);
        } catch (Exception e) {
            e.printStackTrace();
            return error(localeMessageSourceService.getMessage("SEND_FAILED"));
        }
        return success(localeMessageSourceService.getMessage("SENT_SUCCESS_TEN"));
    }
    
    @ApiOperation(value="发送谷歌验证码", notes="发送谷歌验证码")
    @PostMapping("/googleAuth/email/code")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public MessageResult sendGoogleAuthEmail(@SessionAttribute(SESSION_MEMBER) AuthMember user, HttpServletRequest request) {
        //Assert.isTrue(ValidateUtil.isEmail(email), localeMessageSourceService.getMessage("WRONG_EMAIL"));
        Member member = memberService.findOne(user.getId());
        Assert.isTrue(ValidateUtil.isEmail(member.getEmail()), localeMessageSourceService.getMessage("WRONG_EMAIL"));
        //Assert.isNull(member.getEmail(), localeMessageSourceService.getMessage("WRONG_EMAIL"));
        //Assert.isTrue(!memberService.emailIsExist(member.getEmail()), localeMessageSourceService.getMessage("EMAIL_ALREADY_BOUND"));
        String code = String.valueOf(GeneratorUtil.getRandomNumber(100000, 999999));
        ValueOperations valueOperations = redisTemplate.opsForValue();
        if (valueOperations.get(SysConstant.EMAIL_AUTH_GOOGLE_PREFIX + member.getEmail()) != null) {
            return error(localeMessageSourceService.getMessage("EMAIL_ALREADY_SEND"));
        }
        if (devModeEnabled) {
            String devCode = devModeVcode.isEmpty() ? code : devModeVcode;
            valueOperations.set(SysConstant.EMAIL_AUTH_GOOGLE_PREFIX + member.getEmail(), devCode, 10, TimeUnit.MINUTES);
            log.info("[dev模式] 谷歌认证邮箱验证码已写入Redis, email={}, code={}", member.getEmail(), devCode);
            return success(localeMessageSourceService.getMessage("SENT_SUCCESS_TEN"));
        }
        try {
        	String ip = getRemoteIp(request);
        	sentCommonEmailCode(valueOperations, EMAIL_AUTH_GOOGLE_PREFIX, member.getEmail(), code, ip);
        } catch (Exception e) {
            e.printStackTrace();
            return error(localeMessageSourceService.getMessage("SEND_FAILED"));
        }
        return success(localeMessageSourceService.getMessage("SENT_SUCCESS_TEN"));
    }
    @ApiOperation(value="发送修改密码验证码", notes="发送修改密码验证码")
    @PostMapping("/updatePassword/email/code")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public MessageResult updatePasswordEmail(@SessionAttribute(SESSION_MEMBER) AuthMember user, HttpServletRequest request) {
        //Assert.isTrue(ValidateUtil.isEmail(email), localeMessageSourceService.getMessage("WRONG_EMAIL"));
        Member member = memberService.findOne(user.getId());
        Assert.isTrue(ValidateUtil.isEmail(member.getEmail()), localeMessageSourceService.getMessage("WRONG_EMAIL"));
        //Assert.isNull(member.getEmail(), localeMessageSourceService.getMessage("WRONG_EMAIL"));
        //Assert.isTrue(!memberService.emailIsExist(member.getEmail()), localeMessageSourceService.getMessage("EMAIL_ALREADY_BOUND"));
        String code = String.valueOf(GeneratorUtil.getRandomNumber(100000, 999999));
        ValueOperations valueOperations = redisTemplate.opsForValue();
        if (valueOperations.get(SysConstant.EMAIL_UPDATE_PASSWORD_PREFIX + member.getEmail()) != null) {
            return error(localeMessageSourceService.getMessage("EMAIL_ALREADY_SEND"));
        }
        try {
        	valueOperations.set(SysConstant.EMAIL_UPDATE_PASSWORD_PREFIX + member.getEmail(), code, 10, TimeUnit.MINUTES);
        	if (devModeEnabled) {
        	    log.info("[dev模式] 修改密码邮箱验证码已写入Redis, email={}, code={}", member.getEmail(), code);
        	} else {
	        	//String ip = getRemoteIp(request);
	        	//sentCommonEmailCode(valueOperations, EMAIL_UPDATE_PASSWORD_PREFIX, member.getEmail(), code, ip);
	        	net.sf.json.JSONObject jsonObject = new net.sf.json.JSONObject();
	        	jsonObject.put("code", code);
	        	memberService.notifyByEmail(member.getId(), NotifyType.UPDATE_PASSWORD_VALIDATE, jsonObject);
        	}
        } catch (Exception e) {
            e.printStackTrace();
            return error(localeMessageSourceService.getMessage("SEND_FAILED"));
        }
        return success(localeMessageSourceService.getMessage("SENT_SUCCESS_TEN"));
    }
    
    @ApiOperation(value="发送代理商验证码", notes="发送代理商验证码")
    @PostMapping("/agent/email/code")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public MessageResult agentEmail(@SessionAttribute(SESSION_MEMBER) AuthMember user, HttpServletRequest request) {
        //Assert.isTrue(ValidateUtil.isEmail(email), localeMessageSourceService.getMessage("WRONG_EMAIL"));
        Member member = memberService.findOne(user.getId());
        Assert.isTrue(ValidateUtil.isEmail(member.getEmail()), localeMessageSourceService.getMessage("WRONG_EMAIL"));
        //Assert.isNull(member.getEmail(), localeMessageSourceService.getMessage("WRONG_EMAIL"));
        //Assert.isTrue(!memberService.emailIsExist(member.getEmail()), localeMessageSourceService.getMessage("EMAIL_ALREADY_BOUND"));
        String code = String.valueOf(GeneratorUtil.getRandomNumber(100000, 999999));
        ValueOperations valueOperations = redisTemplate.opsForValue();
        if (valueOperations.get(SysConstant.EMAIL_AGENT_PREFIX + member.getEmail()) != null) {
            return error(localeMessageSourceService.getMessage("EMAIL_ALREADY_SEND"));
        }
        try {
        	valueOperations.set(SysConstant.EMAIL_AGENT_PREFIX + member.getEmail(), code, 10, TimeUnit.MINUTES);
        	if (devModeEnabled) {
        	    log.info("[dev模式] 代理商邮箱验证码已写入Redis, email={}, code={}", member.getEmail(), code);
        	} else {
	        	net.sf.json.JSONObject jsonObject = new net.sf.json.JSONObject();
	        	jsonObject.put("code", code);
	        	memberService.notifyByEmail(member.getId(), NotifyType.AGENT_VALIDATE, jsonObject);
	        	//String ip = getRemoteIp(request);
	        	//sentCommonEmailCode(valueOperations, EMAIL_AGENT_PREFIX, member.getEmail(), code, ip);
        	}
        } catch (Exception e) {
            e.printStackTrace();
            return error(localeMessageSourceService.getMessage("SEND_FAILED"));
        }
        return success(localeMessageSourceService.getMessage("SENT_SUCCESS_TEN"));
    }
    
    @ApiOperation(value="发送邮箱变更验证码", notes="发送邮箱变更验证码")
    @PostMapping("/email/change/code")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public MessageResult changeEmail(@SessionAttribute(SESSION_MEMBER) AuthMember user, HttpServletRequest request) {
        //Assert.isTrue(ValidateUtil.isEmail(email), localeMessageSourceService.getMessage("WRONG_EMAIL"));
        Member member = memberService.findOne(user.getId());
        Assert.isTrue(ValidateUtil.isEmail(member.getEmail()), localeMessageSourceService.getMessage("WRONG_EMAIL"));
        //Assert.isNull(member.getEmail(), localeMessageSourceService.getMessage("WRONG_EMAIL"));
        //Assert.isTrue(!memberService.emailIsExist(member.getEmail()), localeMessageSourceService.getMessage("EMAIL_ALREADY_BOUND"));
        String code = String.valueOf(GeneratorUtil.getRandomNumber(100000, 999999));
        ValueOperations valueOperations = redisTemplate.opsForValue();
        if (valueOperations.get(SysConstant.EMAIL_CHANGE_CODE_PREFIX + member.getEmail()) != null) {
            return error(localeMessageSourceService.getMessage("EMAIL_ALREADY_SEND"));
        }
        if (devModeEnabled) {
            valueOperations.set(SysConstant.EMAIL_CHANGE_CODE_PREFIX + member.getEmail(), code, 10, TimeUnit.MINUTES);
            log.info("[dev模式] 邮箱变更验证码已写入Redis, email={}, code={}", member.getEmail(), code);
            return success(localeMessageSourceService.getMessage("SENT_SUCCESS_TEN"));
        }
        try {
        	String ip = getRemoteIp(request);
        	sentCommonEmailCode(valueOperations, EMAIL_CHANGE_CODE_PREFIX, member.getEmail(), code, ip);
        } catch (Exception e) {
            e.printStackTrace();
            return error(localeMessageSourceService.getMessage("SEND_FAILED"));
        }
        return success(localeMessageSourceService.getMessage("SENT_SUCCESS_TEN"));
    }
    
    @Async
    private void sentCommonEmailCode(ValueOperations valueOperations, String prefix, String email, String code, String ip) throws MessagingException, IOException, TemplateException {
    	Map<String, Object> model = this.getModelForEmail(ip, email);
    	
    	SysConfig sysConfig = sysConfigService.get();
    	model.put("projectName", sysConfig.getProjectName());
    	model.put("projectLogo", sysConfig.getProjectLogo());
    	
        model.put("code", code);
        model.put("account_pre", "登录");
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_26);
        cfg.setClassForTemplateLoading(this.getClass(), "/templates");
        
        model.put("android", sysConfig.getPromotionPrefix());
        model.put("ios", sysConfig.getPromotionPrefix());
        
        Template bottomTemplate = cfg.getTemplate("bottom.ftl");
        String bottomHtml = FreeMarkerTemplateUtils.processTemplateIntoString(bottomTemplate, model);
        model.put("bottomHtml", bottomHtml);
        
        Template template = cfg.getTemplate("common.ftl");
        String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
        aliyunEmailAPI.sendSingleSendMail(email, "验证码", html);
        //log.info("send email for {},content:{}", email, html);
        valueOperations.set(prefix + email, code, 10, TimeUnit.MINUTES);
    }
    
    private Map<String, Object> getModelForEmail(String ip, String email) {
    	Map<String, Object> model = new HashMap<>();
    	IpSearch ipSearch = IpSearch.getInstance();
        if(ipSearch!=null) {
        	String[] cap = ipSearch.getCountryAndProvince(ip);
        	model.put("country", cap[0]);
        	model.put("province", cap[1]);
    	} else {
    		model.put("country", "未知");
    		model.put("province", "未知");
        }
        String dateTime = DateUtil.getDateTime();
		model.put("time", dateTime);
		model.put("timewos", dateTime.substring(0,dateTime.length()-3));
		model.put("email", email);
		return model;
    }

}
