package com.iamberry.app.service; import static com.iamberry.app.config.ImberryConfig.INTER_SMS_KEY; import static com.iamberry.app.config.ImberryConfig.INTER_SMS_TEXT; import static com.iamberry.app.config.ImberryConfig.INTER_SMS_URL; import static com.iamberry.app.config.ImberryConfig.SMS_PASSWORD; import static com.iamberry.app.config.ImberryConfig.SMS_TEXT; import static com.iamberry.app.config.ImberryConfig.SMS_URL; import static com.iamberry.app.config.ImberryConfig.SMS_USERNAME; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import net.sf.json.JSONObject; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.iamberry.app.config.Response; import com.iamberry.app.config.ResponseHeader; import com.iamberry.app.core.entity.CodeValid; import com.iamberry.app.face.CodeService; import com.iamberry.app.mapper.CodeMapper; import com.iamberry.app.tool.util.Result; import com.iamberry.app.ulitity.Utility; import com.iamberry.wechat.tools.ResponseJson; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.xml.StaxDriver; /** * @company 深圳爱贝源科技有限公司 * @website www.iamberry.com * @author 献 * @tel 18271840547 * @date 2016年11月1日 * @explain 验证码业务实现类 */ @Service public class CodeServiceImpl implements CodeService { @Autowired private CodeMapper codeMapper; private static String ENCODING = "UTF-8"; @Override public ResponseJson sendCode(String phone, int codeScenario) { // TODO Auto-generated method stub // 第一步,判断使用通道,如果是+86开头,优先使用国内通道,否则默认使用国外通道 ResponseJson json = new ResponseJson(); if (StringUtils.isEmpty(phone)) { json.setReturnCode(404); json.addResponseKeyValue("Phone Empty!"); return json; } // 第二步,如果是国内,判断是否存在 CodeValid codeValid = codeMapper.getLast(phone); Date now = new Date(); // 通道是否是中国的 boolean IS_CHANNEL_ZH = false; if (StringUtils.indexOf(phone, "+") != -1) { if (StringUtils.startsWith(phone, "+86")) { IS_CHANNEL_ZH = true; } } else { IS_CHANNEL_ZH = true; } // 判断通道 if (IS_CHANNEL_ZH && codeValid != null) { // ** 切换通道需求:每次请求验证码,如果上一次验证码在一分钟以后,三分钟以内没有使用,那么切换通道 **// if (now.getTime() <= (codeValid.getCodeValidDate().getTime()) && (now.getTime() - 60000) >= codeValid.getCodeSendDate().getTime() && codeValid.getCodeUse() == 2 && codeValid.getCodeScenario() == codeScenario) { // 如果等待三分钟后,那么切换通道,暂时不切换 // IS_CHANNEL_ZH = false; } } // 获取验证码 String code = Utility.getRandomCode(4); json = sendCMS(phone, code, IS_CHANNEL_ZH ? 1 : 2); if (json.getReturnCode() != 200) { json = sendCMS(phone, code, IS_CHANNEL_ZH ? 1 : 2); } // 保存发送记录 codeValid = new CodeValid(); codeValid.setCodeChannel(IS_CHANNEL_ZH ? 1 : 2); codeValid.setCodeMsg(json.getReturnMsg().get("returnMsg").toString()); codeValid.setCodePhone(phone); codeValid.setCodeScenario(codeScenario); codeValid.setCodeSendDate(now); codeValid.setCodeValidDate(new Date(now.getTime() + 180000)); codeValid.setCodeStatus(json.getReturnCode() == 200 ? 3 : 4); codeValid.setCodeUse(2); codeValid.setCodeValue(Integer.parseInt(code)); codeMapper.save(codeValid); return json; } /** * @param phone 手机号码 * @param code 短信验证码 * @param channel 通道 1:主通道;2:备用通道(国外的电话通通使用此) * @return */ private ResponseJson sendCMS(String phone, String code, int channel) { // 国内号码 ResponseJson json = new ResponseJson(); json.setReturnCode(500); String result = null; try { if (channel == 1) { // 使用主通道 // result = sendZHCMS(phone, code); result = sendOtherCMS(phone, code); } else { // 使用备用通道 result = sendOtherCMS(phone, code); } } catch (Exception e) { result = e.getMessage(); } if (StringUtils.equals(result, "SUCCESS")) { json.setReturnCode(200); } json.addResponseKeyValue(result); return json; } private String sendOtherCMS(String phone, String code) { String text = MessageFormat.format(INTER_SMS_TEXT, code); String results = sendSms(text, phone); JSONObject json = JSONObject.fromObject(results); String resultcod = json.get("code").toString(); if(resultcod.equals("0")){ System.out.println("使用备用通道,发送验证码成功!" + code); return "SUCCESS"; }else{ System.out.println("使用备用通道,发送失败...!" + code); return results; } } public static String sendSms(String text, String mobile) { Map params = new HashMap(); params.put("apikey", INTER_SMS_KEY); params.put("text", text); params.put("mobile", mobile); return post(INTER_SMS_URL, params); } /** 基于HttpClient 4.3的通用POST方法 * @param url 提交的URL * @param paramsMap 提交<参数,值>Map * @return 提交响应 */ public static String post(String url, Map paramsMap) { CloseableHttpClient client = HttpClients.createDefault(); String responseText = ""; CloseableHttpResponse response = null; try { HttpPost method = new HttpPost(url); if (paramsMap != null) { List paramList = new ArrayList(); for (Map.Entry param : paramsMap.entrySet()) { NameValuePair pair = new BasicNameValuePair(param.getKey(), param.getValue()); paramList.add(pair); } method.setEntity(new UrlEncodedFormEntity(paramList, ENCODING)); } response = client.execute(method); HttpEntity entity = response.getEntity(); if (entity != null) { responseText = EntityUtils.toString(entity); } } catch (Exception e) { e.printStackTrace(); } finally { try { response.close(); } catch (Exception e) { e.printStackTrace(); } } return responseText; } private String sendZHCMS(String phone, String code) throws Exception { CloseableHttpClient client = HttpClients.createDefault(); Map params = new HashMap(); CloseableHttpResponse response = null; params.put("username", SMS_USERNAME); params.put("password", SMS_PASSWORD); params.put("mobile", phone); params.put("content", MessageFormat.format(SMS_TEXT, code)); HttpPost method = new HttpPost(SMS_URL); if (params != null) { List paramList = new ArrayList(); for (Map.Entry param : params.entrySet()) { NameValuePair pair = new BasicNameValuePair(param.getKey(), param.getValue()); paramList.add(pair); } method.setEntity(new UrlEncodedFormEntity(paramList, "UTF-8")); } response = client.execute(method); HttpEntity entity = response.getEntity(); if (entity != null) { String result = EntityUtils.toString(entity); XStream xs = new XStream(new StaxDriver()); xs.alias("result", Result.class); Result object = (Result)xs.fromXML(result); response.close(); if (0 == object.getResultcode()) { return "SUCCESS"; } else { return object.getResultcode() + ":" + object.getErrordescription(); } } return null; } @Override public ResponseJson validCode(String phone, String code, int codeScenario) { ResponseJson json = new ResponseJson(); // 校验 CodeValid codeValid = codeMapper.getLast(phone); if (codeValid == null) { // 操作有误 json.setReturnCode(404); json.addResponseKeyValue("Wrong operation, No send record"); return json; } Date now = new Date(); if (now.getTime() >= codeValid.getCodeValidDate().getTime()) { // 验证码无效 json.setReturnCode(403); json.addResponseKeyValue("Verification code is invalid"); return json; } // 验证码是否使用 1:已经使用;2:未使用 if (codeValid.getCodeUse().intValue() == 1) { // 验证码无效 json.setReturnCode(402); json.addResponseKeyValue("Verification code has been used"); return json; } // 必须 验证码正确,并且场景正确 if (!(codeValid.getCodeValue() == (Integer.parseInt(code)) && codeScenario == codeValid.getCodeScenario())) { // 验证码错误 json.setReturnCode(405); json.addResponseKeyValue("Verification code error"); return json; } // 只要校验成功,表示本次验证码已使用 codeMapper.update(codeValid.getCodeId()); json.setReturnCode(200); json.addResponseKeyValue("SUCCESS"); return json; } /** * 放轰炸原则: * 1、每个手机号码,每60秒只能发送1次! * 2、每个手机号码,每小时只能发送三次! * 3、每个手机号码,每天只能发送10次! * @param phone * @return * @author 献 * @Time 2016年12月5日 */ public Response interval(String phone) { CodeValid codeValid = codeMapper.getLast(phone); Date now = new Date(); // 每个手机号,限制每60秒,只能发送一次 if (codeValid != null && (60000 > (now.getTime() - codeValid.getCodeSendDate().getTime()))) { return new Response(new ResponseHeader(404, "每个手机号码,每60秒只能发送1次!", 0)); } // 生成时间规则 Date startDate = new Date(); startDate.setMinutes(0); startDate.setSeconds(0); Date endDate = new Date(); endDate.setMinutes(59); endDate.setSeconds(60); // 每个手机号码,每个小时最多3条 if (codeMapper.getInterval(startDate, endDate, phone) >= 3) { return new Response(new ResponseHeader(404, "每个手机号码,每小时只能发送3次!", 0)); } // 每个手机号码,每天最多10条 endDate.setHours(23); startDate.setHours(0); if (codeMapper.getInterval(startDate, endDate, phone) >= 10) { return new Response(new ResponseHeader(404, "每个手机号码,每天只能发送10次!", 0)); } return new Response(new ResponseHeader(200, "SUCCESS", 0)); } }