8279a59ecd0413d2345ab461fd4e1ae624e05b19.svn-base 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. package com.iamberry.app.service;
  2. import static com.iamberry.app.config.ImberryConfig.INTER_SMS_KEY;
  3. import static com.iamberry.app.config.ImberryConfig.INTER_SMS_TEXT;
  4. import static com.iamberry.app.config.ImberryConfig.INTER_SMS_URL;
  5. import static com.iamberry.app.config.ImberryConfig.SMS_PASSWORD;
  6. import static com.iamberry.app.config.ImberryConfig.SMS_TEXT;
  7. import static com.iamberry.app.config.ImberryConfig.SMS_URL;
  8. import static com.iamberry.app.config.ImberryConfig.SMS_USERNAME;
  9. import java.text.MessageFormat;
  10. import java.util.ArrayList;
  11. import java.util.Date;
  12. import java.util.HashMap;
  13. import java.util.List;
  14. import java.util.Map;
  15. import net.sf.json.JSONObject;
  16. import org.apache.commons.lang3.StringUtils;
  17. import org.apache.http.HttpEntity;
  18. import org.apache.http.NameValuePair;
  19. import org.apache.http.client.entity.UrlEncodedFormEntity;
  20. import org.apache.http.client.methods.CloseableHttpResponse;
  21. import org.apache.http.client.methods.HttpPost;
  22. import org.apache.http.impl.client.CloseableHttpClient;
  23. import org.apache.http.impl.client.HttpClients;
  24. import org.apache.http.message.BasicNameValuePair;
  25. import org.apache.http.util.EntityUtils;
  26. import org.slf4j.Logger;
  27. import org.slf4j.LoggerFactory;
  28. import org.springframework.beans.factory.annotation.Autowired;
  29. import org.springframework.stereotype.Service;
  30. import com.iamberry.app.config.Response;
  31. import com.iamberry.app.config.ResponseHeader;
  32. import com.iamberry.app.core.entity.CodeValid;
  33. import com.iamberry.app.face.CodeService;
  34. import com.iamberry.app.mapper.CodeMapper;
  35. import com.iamberry.app.tool.util.Result;
  36. import com.iamberry.app.ulitity.Utility;
  37. import com.iamberry.wechat.tools.ResponseJson;
  38. import com.thoughtworks.xstream.XStream;
  39. import com.thoughtworks.xstream.io.xml.StaxDriver;
  40. /**
  41. * @company 深圳爱贝源科技有限公司
  42. * @website www.iamberry.com
  43. * @author 献
  44. * @tel 18271840547
  45. * @date 2016年11月1日
  46. * @explain 验证码业务实现类
  47. */
  48. @Service
  49. public class CodeServiceImpl implements CodeService {
  50. @Autowired
  51. private CodeMapper codeMapper;
  52. private static Logger log = LoggerFactory.getLogger(CodeServiceImpl.class);
  53. private static String ENCODING = "UTF-8";
  54. @Override
  55. public ResponseJson sendCode(String phone, int codeScenario) {
  56. // TODO Auto-generated method stub
  57. // 第一步,判断使用通道,如果是+86开头,优先使用国内通道,否则默认使用国外通道
  58. ResponseJson json = new ResponseJson();
  59. if (StringUtils.isEmpty(phone)) {
  60. json.setReturnCode(404);
  61. json.addResponseKeyValue("Phone Empty!");
  62. return json;
  63. }
  64. // 第二步,如果是国内,判断是否存在
  65. CodeValid codeValid = codeMapper.getLast(phone);
  66. Date now = new Date();
  67. // 通道是否是中国的
  68. boolean IS_CHANNEL_ZH = true;
  69. if (StringUtils.indexOf(phone, "+") != -1) {
  70. if (StringUtils.startsWith(phone, "+86")) {
  71. IS_CHANNEL_ZH = true;
  72. }
  73. } else {
  74. IS_CHANNEL_ZH = true;
  75. }
  76. // 判断通道
  77. if (IS_CHANNEL_ZH && codeValid != null) {
  78. // ** 切换通道需求:每次请求验证码,如果上一次验证码在一分钟以后,三分钟以内没有使用,那么切换通道 **//
  79. if (now.getTime() <= (codeValid.getCodeValidDate().getTime()) &&
  80. (now.getTime() - 60000) >= codeValid.getCodeSendDate().getTime() && codeValid.getCodeUse() == 2 && codeValid.getCodeScenario() == codeScenario) {
  81. // 如果等待三分钟后,那么切换通道,暂时不切换
  82. // IS_CHANNEL_ZH = false;
  83. }
  84. }
  85. // 获取验证码
  86. String code = Utility.getRandomCode(4);
  87. json = sendCMS(phone, code, IS_CHANNEL_ZH ? 1 : 2);
  88. if (json.getReturnCode() != 200) {
  89. json = sendCMS(phone, code, IS_CHANNEL_ZH ? 1 : 2);
  90. }
  91. // 保存发送记录
  92. codeValid = new CodeValid();
  93. codeValid.setCodeChannel(IS_CHANNEL_ZH ? 1 : 2);
  94. codeValid.setCodeMsg(json.getReturnMsg().get("returnMsg").toString());
  95. codeValid.setCodePhone(phone);
  96. codeValid.setCodeScenario(codeScenario);
  97. codeValid.setCodeSendDate(now);
  98. codeValid.setCodeValidDate(new Date(now.getTime() + 180000));
  99. codeValid.setCodeStatus(json.getReturnCode() == 200 ? 3 : 4);
  100. codeValid.setCodeUse(2);
  101. codeValid.setCodeValue(Integer.parseInt(code));
  102. codeMapper.save(codeValid);
  103. return json;
  104. }
  105. /**
  106. * @param phone 手机号码
  107. * @param code 短信验证码
  108. * @param channel 通道 1:主通道;2:备用通道(国外的电话通通使用此)
  109. * @return
  110. */
  111. private ResponseJson sendCMS(String phone, String code, int channel) {
  112. log.info("【发送短信】 Tel:" + phone + " ------> code(" + code + "), channel:" + channel);
  113. // 国内号码
  114. ResponseJson json = new ResponseJson();
  115. json.setReturnCode(500);
  116. String result = null;
  117. try {
  118. if (channel == 1) {
  119. // 使用主通道
  120. // result = sendZHCMS(phone, code);
  121. result = sendOtherCMS(phone, code);
  122. } else {
  123. // 使用备用通道
  124. result = sendOtherCMS(phone, code);
  125. }
  126. } catch (Exception e) {
  127. result = e.getMessage();
  128. }
  129. if (StringUtils.equals(result, "SUCCESS")) {
  130. json.setReturnCode(200);
  131. }
  132. json.addResponseKeyValue(result);
  133. return json;
  134. }
  135. private String sendOtherCMS(String phone, String code) {
  136. String text = MessageFormat.format(INTER_SMS_TEXT, code);
  137. String results = sendSms(text, phone);
  138. JSONObject json = JSONObject.fromObject(results);
  139. String resultcod = json.get("code").toString();
  140. if(resultcod.equals("0")){
  141. System.out.println("使用备用通道,发送验证码成功!" + code);
  142. return "SUCCESS";
  143. }else{
  144. System.out.println("使用备用通道,发送失败...!" + code);
  145. return results;
  146. }
  147. }
  148. public static String sendSms(String text, String mobile) {
  149. Map<String, String> params = new HashMap<String, String>();
  150. params.put("apikey", INTER_SMS_KEY);
  151. params.put("text", text);
  152. params.put("mobile", mobile);
  153. return post(INTER_SMS_URL, params);
  154. }
  155. /** 基于HttpClient 4.3的通用POST方法
  156. * @param url 提交的URL
  157. * @param paramsMap 提交<参数,值>Map
  158. * @return 提交响应
  159. */
  160. public static String post(String url, Map<String, String> paramsMap) {
  161. CloseableHttpClient client = HttpClients.createDefault();
  162. String responseText = "";
  163. CloseableHttpResponse response = null;
  164. try {
  165. HttpPost method = new HttpPost(url);
  166. if (paramsMap != null) {
  167. List<NameValuePair> paramList = new ArrayList<NameValuePair>();
  168. for (Map.Entry<String, String> param : paramsMap.entrySet()) {
  169. NameValuePair pair = new BasicNameValuePair(param.getKey(), param.getValue());
  170. paramList.add(pair);
  171. }
  172. method.setEntity(new UrlEncodedFormEntity(paramList, ENCODING));
  173. }
  174. response = client.execute(method);
  175. HttpEntity entity = response.getEntity();
  176. if (entity != null) {
  177. responseText = EntityUtils.toString(entity);
  178. }
  179. } catch (Exception e) {
  180. e.printStackTrace();
  181. } finally {
  182. try {
  183. response.close();
  184. } catch (Exception e) {
  185. e.printStackTrace();
  186. }
  187. }
  188. return responseText;
  189. }
  190. private String sendZHCMS(String phone, String code) throws Exception {
  191. CloseableHttpClient client = HttpClients.createDefault();
  192. Map<String, String> params = new HashMap<String, String>();
  193. CloseableHttpResponse response = null;
  194. params.put("username", SMS_USERNAME);
  195. params.put("password", SMS_PASSWORD);
  196. params.put("mobile", phone);
  197. params.put("content", MessageFormat.format(SMS_TEXT, code));
  198. HttpPost method = new HttpPost(SMS_URL);
  199. if (params != null) {
  200. List<NameValuePair> paramList = new ArrayList<NameValuePair>();
  201. for (Map.Entry<String, String> param : params.entrySet()) {
  202. NameValuePair pair = new BasicNameValuePair(param.getKey(), param.getValue());
  203. paramList.add(pair);
  204. }
  205. method.setEntity(new UrlEncodedFormEntity(paramList, "UTF-8"));
  206. }
  207. response = client.execute(method);
  208. HttpEntity entity = response.getEntity();
  209. if (entity != null) {
  210. String result = EntityUtils.toString(entity);
  211. XStream xs = new XStream(new StaxDriver());
  212. xs.alias("result", Result.class);
  213. Result object = (Result)xs.fromXML(result);
  214. response.close();
  215. if (0 == object.getResultcode()) {
  216. return "SUCCESS";
  217. } else {
  218. return object.getResultcode() + ":" + object.getErrordescription();
  219. }
  220. }
  221. return null;
  222. }
  223. @Override
  224. public ResponseJson validCode(String phone, String code, int codeScenario) {
  225. ResponseJson json = new ResponseJson();
  226. // 校验
  227. CodeValid codeValid = codeMapper.getLast(phone);
  228. if (codeValid == null) {
  229. // 操作有误
  230. json.setReturnCode(404);
  231. json.addResponseKeyValue("Wrong operation, No send record");
  232. return json;
  233. }
  234. Date now = new Date();
  235. if (now.getTime() > codeValid.getCodeValidDate().getTime()) {
  236. // 验证码无效
  237. json.setReturnCode(403);
  238. json.addResponseKeyValue("Verification code is invalid");
  239. return json;
  240. }
  241. // 验证码是否使用 1:已经使用;2:未使用
  242. if (codeValid.getCodeUse().intValue() == 1) {
  243. // 验证码无效
  244. json.setReturnCode(402);
  245. json.addResponseKeyValue("Verification code has been used");
  246. return json;
  247. }
  248. // 必须 验证码正确,并且场景正确
  249. if (!(codeValid.getCodeValue() == (Integer.parseInt(code))
  250. && codeScenario == codeValid.getCodeScenario())) {
  251. // 验证码错误
  252. json.setReturnCode(405);
  253. json.addResponseKeyValue("Verification code error");
  254. return json;
  255. }
  256. // 只要校验成功,表示本次验证码已使用
  257. codeMapper.update(codeValid.getCodeId());
  258. json.setReturnCode(200);
  259. json.addResponseKeyValue("SUCCESS");
  260. return json;
  261. }
  262. /**
  263. * 放轰炸原则:
  264. * 1、每个手机号码,每60秒只能发送1次!
  265. * 2、每个手机号码,每小时只能发送三次!
  266. * 3、每个手机号码,每天只能发送10次!
  267. * @param phone
  268. * @return
  269. * @author 献
  270. * @Time 2016年12月5日
  271. */
  272. public Response interval(String phone) {
  273. CodeValid codeValid = codeMapper.getLast(phone);
  274. Date now = new Date();
  275. // 每个手机号,限制每60秒,只能发送一次
  276. if (codeValid != null && (60000 > (now.getTime() - codeValid.getCodeSendDate().getTime()))) {
  277. return new Response(new ResponseHeader(404, "每个手机号码,每60秒只能发送1次!", 0));
  278. }
  279. // 如果上一次的验证码没有使用,那么需要输入验证码
  280. if (codeValid != null && codeValid.getCodeUse() == 2) {
  281. return new Response(new ResponseHeader(403, "请输入验证码", 0));
  282. }
  283. // 生成时间规则
  284. Date startDate = new Date();
  285. startDate.setMinutes(0);
  286. startDate.setSeconds(0);
  287. Date endDate = new Date();
  288. endDate.setMinutes(59);
  289. endDate.setSeconds(60);
  290. // 每个手机号码,每个小时最多3条
  291. if (codeMapper.getInterval(startDate, endDate, phone) >= 3) {
  292. return new Response(new ResponseHeader(404, "每个手机号码,每小时只能发送3次!", 0));
  293. }
  294. // 每个手机号码,每天最多10条
  295. endDate.setHours(23);
  296. startDate.setHours(0);
  297. if (codeMapper.getInterval(startDate, endDate, phone) >= 10) {
  298. return new Response(new ResponseHeader(404, "每个手机号码,每天只能发送10次!", 0));
  299. }
  300. return new Response(new ResponseHeader(200, "SUCCESS", 0));
  301. }
  302. }