Browse Source

feat(server): 对接接口

Go 5 years ago
parent
commit
8a3cf68e3a

+ 2 - 1
front/project/admin/routes/user/detail/page.js

@@ -148,7 +148,8 @@ export default class extends Page {
             </Col>
             <Col span={12}>
               <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='身份证照片'>
-                <Avatar src={data.realPhoto} />
+                <Avatar src={data.realPhotoFront} />
+                <Avatar src={data.realPhotoBack} />
               </Form.Item>
             </Col>
           </Row>

+ 51 - 16
server/data/src/main/java/com/qxgmat/data/dao/entity/User.java

@@ -100,10 +100,16 @@ public class User implements Serializable {
     private String realIdentity;
 
     /**
-     * 实名:身份证照片
+     * 实名:身份证照片-正面
      */
-    @Column(name = "`real_photo`")
-    private String realPhoto;
+    @Column(name = "`real_photo_front`")
+    private String realPhotoFront;
+
+    /**
+     * 实名:身份证照片-背面
+     */
+    @Column(name = "`real_photo_back`")
+    private String realPhotoBack;
 
     /**
      * 实名状态:0未实名,1已实名
@@ -457,21 +463,39 @@ public class User implements Serializable {
     }
 
     /**
-     * 获取实名:身份证照片
+     * 获取实名:身份证照片-正面
+     *
+     * @return real_photo_front - 实名:身份证照片-正面
+     */
+    public String getRealPhotoFront() {
+        return realPhotoFront;
+    }
+
+    /**
+     * 设置实名:身份证照片-正面
      *
-     * @return real_photo - 实名:身份证照片
+     * @param realPhotoFront 实名:身份证照片-正面
      */
-    public String getRealPhoto() {
-        return realPhoto;
+    public void setRealPhotoFront(String realPhotoFront) {
+        this.realPhotoFront = realPhotoFront;
     }
 
     /**
-     * 设置实名:身份证照片
+     * 获取实名:身份证照片-背面
      *
-     * @param realPhoto 实名:身份证照片
+     * @return real_photo_back - 实名:身份证照片-背面
      */
-    public void setRealPhoto(String realPhoto) {
-        this.realPhoto = realPhoto;
+    public String getRealPhotoBack() {
+        return realPhotoBack;
+    }
+
+    /**
+     * 设置实名:身份证照片-背面
+     *
+     * @param realPhotoBack 实名:身份证照片-背面
+     */
+    public void setRealPhotoBack(String realPhotoBack) {
+        this.realPhotoBack = realPhotoBack;
     }
 
     /**
@@ -708,7 +732,8 @@ public class User implements Serializable {
         sb.append(", realName=").append(realName);
         sb.append(", realAddress=").append(realAddress);
         sb.append(", realIdentity=").append(realIdentity);
-        sb.append(", realPhoto=").append(realPhoto);
+        sb.append(", realPhotoFront=").append(realPhotoFront);
+        sb.append(", realPhotoBack=").append(realPhotoBack);
         sb.append(", realStatus=").append(realStatus);
         sb.append(", prepareStatus=").append(prepareStatus);
         sb.append(", prepareGoal=").append(prepareGoal);
@@ -893,12 +918,22 @@ public class User implements Serializable {
         }
 
         /**
-         * 设置实名:身份证照片
+         * 设置实名:身份证照片-正面
+         *
+         * @param realPhotoFront 实名:身份证照片-正面
+         */
+        public Builder realPhotoFront(String realPhotoFront) {
+            obj.setRealPhotoFront(realPhotoFront);
+            return this;
+        }
+
+        /**
+         * 设置实名:身份证照片-背面
          *
-         * @param realPhoto 实名:身份证照片
+         * @param realPhotoBack 实名:身份证照片-背面
          */
-        public Builder realPhoto(String realPhoto) {
-            obj.setRealPhoto(realPhoto);
+        public Builder realPhotoBack(String realPhotoBack) {
+            obj.setRealPhotoBack(realPhotoBack);
             return this;
         }
 

+ 6 - 5
server/data/src/main/java/com/qxgmat/data/dao/mapping/UserMapper.xml

@@ -21,7 +21,8 @@
     <result column="real_name" jdbcType="VARCHAR" property="realName" />
     <result column="real_address" jdbcType="VARCHAR" property="realAddress" />
     <result column="real_identity" jdbcType="VARCHAR" property="realIdentity" />
-    <result column="real_photo" jdbcType="VARCHAR" property="realPhoto" />
+    <result column="real_photo_front" jdbcType="VARCHAR" property="realPhotoFront" />
+    <result column="real_photo_back" jdbcType="VARCHAR" property="realPhotoBack" />
     <result column="real_status" jdbcType="INTEGER" property="realStatus" />
     <result column="prepare_status" jdbcType="VARCHAR" property="prepareStatus" />
     <result column="prepare_goal" jdbcType="INTEGER" property="prepareGoal" />
@@ -41,9 +42,9 @@
     -->
     `id`, `nickname`, `avatar`, `password`, `email`, `area`, `mobile`, `wechat_openid_pc`, 
     `wechat_openid_wechat`, `wechat_unionid`, `wechat_access_token`, `wechat_refresh_token`, 
-    `wechat_expire_time`, `real_name`, `real_address`, `real_identity`, `real_photo`, 
-    `real_status`, `prepare_status`, `prepare_goal`, `prepare_examination_time`, `prepare_score_time`, 
-    `latest_exercise`, `latest_error`, `origin_id`, `invite_code`, `total_money`, `invite_number`, 
-    `create_time`
+    `wechat_expire_time`, `real_name`, `real_address`, `real_identity`, `real_photo_front`, 
+    `real_photo_back`, `real_status`, `prepare_status`, `prepare_goal`, `prepare_examination_time`, 
+    `prepare_score_time`, `latest_exercise`, `latest_error`, `origin_id`, `invite_code`, 
+    `total_money`, `invite_number`, `create_time`
   </sql>
 </mapper>

+ 22 - 12
server/gateway-api/src/main/java/com/qxgmat/controller/api/MyController.java

@@ -19,6 +19,7 @@ import com.qxgmat.dto.extend.*;
 import com.qxgmat.dto.request.*;
 import com.qxgmat.dto.request.UserNoteQuestionDto;
 import com.qxgmat.dto.response.*;
+import com.qxgmat.help.AiHelp;
 import com.qxgmat.help.ShiroHelp;
 import com.qxgmat.service.*;
 import com.qxgmat.service.extend.QuestionFlowService;
@@ -63,6 +64,9 @@ public class MyController {
     private ShiroHelp shiroHelp;
 
     @Autowired
+    private AiHelp aiHelp;
+
+    @Autowired
     private SettingService settingService;
 
     @Autowired
@@ -153,31 +157,37 @@ public class MyController {
 
     @RequestMapping(value = "/real", produces = MediaType.IMAGE_JPEG_VALUE, method = RequestMethod.POST)
     @ApiOperation(value = "实名认证", notes = "保存用户实名信息", httpMethod = "POST")
-    public Response<UserRealDto> real(@RequestParam("file") MultipartFile multipartFile)  {
-        if (multipartFile.isEmpty()) {
+    public Response<UserRealDto> real(@RequestParam("front") MultipartFile front, @RequestParam("back") MultipartFile back) throws IOException {
+        if (front.isEmpty() || back.isEmpty()) {
             throw new ParameterException("上传文件为空");
         }
-        String contentType = multipartFile.getContentType();
-        if (!contentType.contains("")) {
-            throw new ParameterException("文件类型错误");
-        }
         User user = (User) shiroHelp.getLoginUser();
         UserRealDto dto = new UserRealDto();
 
-        // todo 第三方验证
+        aiHelp.orcIdcardBack(back.getBytes());
+        Map<String, String> map = aiHelp.orcIdcardFront(front.getBytes());
+        dto.setName(map.get("name"));
+        dto.setAddress(map.get("address"));
+        dto.setIdentity(map.get("identity"));
 
-        String file = UUID.randomUUID().toString();
+        String frontName = UUID.randomUUID().toString();
+        String backName = UUID.randomUUID().toString();
         try {
-            File dest = new File(localPath + File.separator+file);
-            multipartFile.transferTo(dest);
-            dto.setPhoto(webUrl+file);
+            File frontDest = new File(localPath + File.separator+frontName);
+            front.transferTo(frontDest);
+            dto.setPhotoFront(webUrl+frontName);
+            File backDest = new File(localPath + File.separator+backName);
+            back.transferTo(backDest);
+            dto.setPhotoBack(webUrl+backName);
             usersService.edit(User.builder()
                     .id(user.getId())
                     .realAddress(dto.getAddress())
                     .realName(dto.getName())
                     .realIdentity(dto.getIdentity())
-                    .realPhoto(dto.getPhoto())
+                    .realPhotoFront(dto.getPhotoFront())
+                    .realPhotoBack(dto.getPhotoBack())
                     .build());
+            // todo 180天vip
             return ResponseHelp.success(dto);
         } catch (IOException e) {
             e.printStackTrace();

+ 17 - 6
server/gateway-api/src/main/java/com/qxgmat/dto/response/UserRealDto.java

@@ -18,8 +18,11 @@ public class UserRealDto {
     @ApiModelProperty(value = "地址", required = true)
     private String address = "";
 
-    @ApiModelProperty(value = "身份照片", required = true)
-    private String photo = "";
+    @ApiModelProperty(value = "身份照片-正面", required = true)
+    private String photoFront = "";
+
+    @ApiModelProperty(value = "身份照片-反面", required = true)
+    private String photoBack = "";
 
     public String getIdentity() {
         return identity;
@@ -45,11 +48,19 @@ public class UserRealDto {
         this.address = address;
     }
 
-    public String getPhoto() {
-        return photo;
+    public String getPhotoFront() {
+        return photoFront;
+    }
+
+    public void setPhotoFront(String photoFront) {
+        this.photoFront = photoFront;
+    }
+
+    public String getPhotoBack() {
+        return photoBack;
     }
 
-    public void setPhoto(String photo) {
-        this.photo = photo;
+    public void setPhotoBack(String photoBack) {
+        this.photoBack = photoBack;
     }
 }

+ 80 - 0
server/gateway-api/src/main/java/com/qxgmat/help/AiHelp.java

@@ -0,0 +1,80 @@
+package com.qxgmat.help;
+
+import com.alibaba.fastjson.JSONObject;
+import com.nuliji.tools.exception.ParameterException;
+import com.nuliji.tools.third.baidu.BaiduAi;
+import com.nuliji.tools.third.sendcloud.SendCloudMail;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by GaoJie on 2017/11/3.
+ */
+@Service
+public class AiHelp {
+    private static final Logger logger = LoggerFactory.getLogger(AiHelp.class);
+
+    private BaiduAi ai;
+
+    @Autowired
+    private void getSms(@Value("${third.baiduai.appKey}") String appKey,
+                   @Value("${third.baiduai.appSecret}") String appSecret) {
+        this.ai = new BaiduAi(appKey, appSecret);
+    }
+
+//    normal-识别正常
+//    reversed_side-身份证正反面颠倒
+//    non_idcard-上传的图片中不包含身份证
+//    blurred-身份证模糊
+//    other_type_card-其他类型证照
+//    over_exposure-身份证关键字段反光或过曝
+//    over_dark-身份证欠曝(亮度过低)
+//    unknown-未知状态
+    public Map<String, String> orcIdcardFront(byte[] image){
+        BaiduAi.IdcardResponse response = ai.idcard(BaiduAi.IdcardSide.FRONT, false, false, image);
+        validResponse(response);
+        Map<String, String> map = new HashMap<>();
+        JSONObject result = response.getWords_result();
+        JSONObject name = result.getJSONObject(BaiduAi.IdcardResult.NAME.key);
+        map.put("name", name.getString("words"));
+        JSONObject address = result.getJSONObject(BaiduAi.IdcardResult.ADDRESS.key);
+        map.put("address", address.getString("words"));
+        JSONObject identity = result.getJSONObject(BaiduAi.IdcardResult.ID.key);
+        map.put("identity", identity.getString("words"));
+        return map;
+    }
+
+    public boolean orcIdcardBack(byte[] image){
+        BaiduAi.IdcardResponse response = ai.idcard(BaiduAi.IdcardSide.BACK, false, false, image);
+        validResponse(response);
+        return true;
+    }
+
+    private boolean validResponse(BaiduAi.IdcardResponse response){
+        switch(response.getImage_status()){
+            case "normal":
+                return true;
+            case "reversed_side":
+                throw new ParameterException("身份证正反面颠倒");
+            case "non_idcard":
+                throw new ParameterException("上传的图片中不包含身份证");
+            case "blurred":
+                throw new ParameterException("身份证模糊");
+            case "other_type_card":
+                throw new ParameterException("其他类型证照");
+            case "over_exposure":
+                throw new ParameterException("身份证关键字段反光或过曝");
+            case "over_dark":
+                throw new ParameterException("亮度过低");
+            default:
+                throw new ParameterException("图片识别失败");
+        }
+    }
+}

+ 57 - 0
server/gateway-api/src/main/java/com/qxgmat/help/MailHelp.java

@@ -0,0 +1,57 @@
+package com.qxgmat.help;
+
+import com.alibaba.fastjson.JSONObject;
+import com.nuliji.tools.exception.ParameterException;
+import com.nuliji.tools.third.sendcloud.SendCloudMail;
+import com.nuliji.tools.third.sendcloud.SendCloudSms;
+import com.qxgmat.data.constants.SessionKey;
+import com.qxgmat.dto.SmsSessionDto;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpSession;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * Created by GaoJie on 2017/11/3.
+ */
+@Service
+public class MailHelp {
+    private static final Logger logger = LoggerFactory.getLogger(MailHelp.class);
+
+    private SendCloudMail mail;
+
+    @Value("${third.sendcloud.from}")
+    private String from;
+    @Value("${third.sendcloud.fromName}")
+    private String fromName;
+
+    @Autowired
+    private void getSms(@Value("${third.sendcloud.apiUser}") String apiUser,
+                   @Value("${third.sendcloud.apiKey}") String apiKey) {
+        this.mail = new SendCloudMail(apiUser, apiKey);
+    }
+
+    public boolean sendBaseMail(String email, String subject, String body, Map<String, String> params){
+        // 替换模版
+        body = replaceBody(body, params);
+        SendCloudMail.Response response = mail.sendMail(email, subject, body, from, fromName, null);
+        return response.getResult();
+    }
+
+    public boolean sendAttachMail(String email, String subject, String body, Map<String, String> params, String filePath){
+        // 替换模版
+        body = replaceBody(body, params);
+        SendCloudMail.Response response = mail.sendMail(email, subject, body, from, fromName, new FileSystemResource(filePath));
+        return response.getResult();
+    }
+
+    private String replaceBody(String body, Map<String, String> params){
+        return body;
+    }
+}

+ 10 - 10
server/gateway-api/src/main/java/com/qxgmat/help/SmsHelp.java

@@ -1,11 +1,11 @@
 package com.qxgmat.help;
 
+import com.alibaba.fastjson.JSONObject;
+import com.nuliji.tools.third.sendcloud.SendCloudSms;
 import com.qxgmat.data.constants.SessionKey;
 import com.qxgmat.dto.SmsSessionDto;
 import com.nuliji.tools.Transform;
 import com.nuliji.tools.exception.ParameterException;
-import com.nuliji.tools.third.aliyuncs.Aliyun;
-import com.nuliji.tools.third.aliyuncs.SendSmsResponse;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -28,14 +28,12 @@ public class SmsHelp {
     final public String VALID_TEMPLATE = "";
     final private Integer EXPIRE_MINUTES = 15;
 
-    private Aliyun aliyun;
+    private SendCloudSms sms;
 
     @Autowired
-    private void getAliyun(@Value("${third.aliyuncs.accessKeyId}") String accessKeyId,
-                   @Value("${third.aliyuncs.accessKeySecret}") String accessKeySecret,
-                   @Value("${third.aliyuncs.sign}") String sign,
-                   @Value("${third.aliyuncs.regionId}") String regionId) {
-        this.aliyun = new Aliyun(accessKeyId, accessKeySecret, sign, regionId);
+    private void getSms(@Value("${third.sendcloud.smsUser}") String smsUser,
+                   @Value("${third.sendcloud.smsKey}") String smsKey) {
+        this.sms = new SendCloudSms(smsUser, smsKey);
     }
 
 
@@ -50,8 +48,10 @@ public class SmsHelp {
         date.setTime(date.getTime() + EXPIRE_MINUTES*60*1000);
         dto.setExpireTime(date);
         try {
-            Object response = aliyun.sendSms(mobile, templateId, code);
-            dto.setResponse(Transform.convert(response, SendSmsResponse.class));
+            JSONObject vars = new JSONObject();
+            vars.put("code", code);
+            Object response = sms.sendSms(mobile, templateId, vars);
+            dto.setResponse(response);
         } catch (Exception e) {
             logger.error("发送短信失败", e);
             return false;

+ 2 - 2
server/gateway-api/src/main/java/com/qxgmat/help/UploadHelp.java

@@ -31,10 +31,10 @@ public class UploadHelp {
     final public String UPLOAD_IMAGE = "nuliji-edu-all-files.oss-cn-beijing.aliyuncs.com";
     final public String UPLOAD_FILE = "nuliji-edu-all-image.oss-cn-beijing.aliyuncs.com";
 
-    @Value("${third.aliyuncs.accessKeyId}")
+//    @Value("${third.aliyuncs.accessKeyId}")
     private String accessKeyId;
 
-    @Value("${third.aliyuncs.accessKeySecret}")
+//    @Value("${third.aliyuncs.accessKeySecret}")
     private String accessKeySecret;
 
 

+ 2 - 1
server/gateway-api/src/main/java/com/qxgmat/service/UsersService.java

@@ -141,7 +141,8 @@ public class UsersService extends AbstractService {
                     user.setRealAddress(openUser.getRealAddress());
                     user.setRealIdentity(openUser.getRealIdentity());
                     user.setRealName(openUser.getRealName());
-                    user.setRealPhoto(openUser.getRealPhoto());
+                    user.setRealPhotoFront(openUser.getRealPhotoFront());
+                    user.setRealPhotoBack(openUser.getRealPhotoBack());
                     user.setRealStatus(openUser.getRealStatus());
                 }
 

+ 11 - 8
server/gateway-api/src/main/resources/application.yml

@@ -33,14 +33,17 @@ management:
     enabled: false
 
 third:
-  aliyuncs:
-    accessKeyId: 123123123
-    accessKeySecret: 123123123123
-    sign: "多少教育"
-    regionId: "cn-hangzhou"
-
-  amap:
-    appKey: a63ffb92338c6bc12788d963f3aecc5b
+  sendcloud:
+    smsUser: 123123
+    smsKey: 13123
+    apiUser: DUKB24_test_l4hdGN
+    apiKey: G7piYRcF5Uin6g16
+    from: 123@123,com
+    fromName: 千行GMAT
+
+  baiduai:
+    appKey: LD0Si697zIdSut8iv5GoPpE8
+    appSecret: DhpsxWkTbI379Q2tUsnD5RG8jiGVGggo
 
   wechat:
     pc:

+ 2 - 2
server/tools/build.gradle

@@ -34,8 +34,8 @@ dependencies {
     parentClasspath 'com.github.rozidan:modelmapper-spring-boot-starter:1.0.0'
 
     // https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
-    compileClasspath group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.2.0'
-    parentClasspath group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.2.0'
+//    compileClasspath group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.2.0'
+//    parentClasspath group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.2.0'
 
     compile group: 'com.aliyun', name: 'aliyun-java-sdk-core', version:'3.3.1'
     compile group: 'com.aliyun', name: 'aliyun-java-sdk-dysmsapi', version: '1.0.0'

+ 4 - 10
server/tools/src/main/java/com/nuliji/tools/third/amap/RestApi.java

@@ -1,13 +1,10 @@
 package com.nuliji.tools.third.amap;
 
 import com.alibaba.fastjson.JSONObject;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.Response;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
 
 import java.net.URLEncoder;
 import java.util.*;
@@ -78,13 +75,10 @@ public class RestApi {
                     paramStr = paramStr + key + "=" + value + "&";
                 }
             }
-            OkHttpClient client = new OkHttpClient();
-            Request request = new Request.Builder()
-                    .url(api + "?" + paramStr)
-                    .build();
-            Response response = client.newCall(request).execute();
-            String result = response.body().string();
 
+            RestTemplate restTemplate = new RestTemplate();
+            String result ="";
+            result = restTemplate.getForObject(api+"?"+paramStr, String.class);
             logger.info("高德地图返回结果:" + result);
             JSONObject a = JSONObject.parseObject(result);
 

+ 183 - 0
server/tools/src/main/java/com/nuliji/tools/third/baidu/BaiduAi.java

@@ -0,0 +1,183 @@
+package com.nuliji.tools.third.baidu;
+
+import com.alibaba.fastjson.JSONObject;
+import com.nuliji.tools.Tools;
+import com.nuliji.tools.Transform;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+
+import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.*;
+
+public class BaiduAi {
+    private static final Logger logger = LoggerFactory.getLogger(BaiduAi.class);
+
+    final static String api = "https://aip.baidubce.com/rest/2.0/";
+
+    private String appKey = "";
+    private String appSecret = "";
+
+    private static Map<String, String> tokenMap = new HashMap<>();
+    private static Map<String, Date> tokenExpiresIn = new HashMap<>();
+
+    public BaiduAi(String appKey, String appSecret){
+        this.appKey = appKey;
+        this.appSecret = appSecret;
+    }
+
+    public enum IdcardSide {
+        FRONT("front"), BACK("back");
+        public String key;
+        private IdcardSide(String key){
+            this.key = key;
+        }
+
+        public static IdcardSide ValueOf(String name){
+            if (name == null) return null;
+            return IdcardSide.valueOf(name.toUpperCase());
+        }
+    }
+    public enum IdcardResult {
+        ADDRESS("住址"), ID("公民身份号码"), BIRTH("出生"), NAME("姓名"), GENDER("性别"), NATION("民族");
+        public String key;
+        private IdcardResult(String key){
+            this.key = key;
+        }
+
+        public static IdcardResult ValueOf(String name){
+            if (name == null) return null;
+            return IdcardResult.valueOf(name.toUpperCase());
+        }
+    }
+    public class IdcardResponse{
+        private Integer direction;
+        private String image_status;
+        private String risk_type;
+        private String edit_tool;
+        private JSONObject words_result;
+        private Integer words_result_num;
+
+        public Integer getDirection() {
+            return direction;
+        }
+
+        public void setDirection(Integer direction) {
+            this.direction = direction;
+        }
+
+        public String getImage_status() {
+            return image_status;
+        }
+
+        public void setImage_status(String image_status) {
+            this.image_status = image_status;
+        }
+
+        public String getRisk_type() {
+            return risk_type;
+        }
+
+        public void setRisk_type(String risk_type) {
+            this.risk_type = risk_type;
+        }
+
+        public String getEdit_tool() {
+            return edit_tool;
+        }
+
+        public void setEdit_tool(String edit_tool) {
+            this.edit_tool = edit_tool;
+        }
+
+        public JSONObject getWords_result() {
+            return words_result;
+        }
+
+        public void setWords_result(JSONObject words_result) {
+            this.words_result = words_result;
+        }
+
+        public Integer getWords_result_num() {
+            return words_result_num;
+        }
+
+        public void setWords_result_num(Integer words_result_num) {
+            this.words_result_num = words_result_num;
+        }
+    }
+    /**
+     * 身份证识别
+     * @return
+     */
+    public IdcardResponse idcard(IdcardSide idCardSide, boolean detectDirection, boolean detectRisk, byte[] image) {
+        MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
+        params.add("image", Tools.encodeBase64(image));
+        params.add("id_card_side", idCardSide.key);
+        params.add("detect_direction", detectDirection ? "true" : "false");
+        params.add("detect_risk", detectRisk ? "true" : "false");
+
+        return Transform.convert(execute(api+"ocr/v1/idcard?access_token"+getAccessToken(appKey, appSecret), params), IdcardResponse.class);
+    }
+
+    /**
+     * 取微信公众平台accesstoken
+     *
+     * @return
+     */
+    private synchronized String getAccessToken(String appKey, String appSecret) {
+        Date date = new Date();
+        // 系统刚启动时,或者离上次取accessToken有7180秒以上时,重新取它
+        if (!tokenMap.containsKey(appKey) || date.getTime() / 1000 - tokenExpiresIn.get(appKey).getTime() / 1000 >= 2592000) {
+            try {
+                Map<String, String> params = new HashMap<>();
+                params.put("grant_type", "client_credential");
+                params.put("client_id", appKey);
+                params.put("client_secret", appSecret);
+                String paramStr = "";
+                for(String key : params.keySet()){
+                    String value = params.get(key);
+                    value = URLEncoder.encode(value, "UTF-8");
+                    paramStr = paramStr + key + "=" + value + "&";
+                }
+                JSONObject object = execute("https://aip.baidubce.com/oauth/2.0/token?"+paramStr, null);
+                tokenMap.put(appKey, object.getString("accessToken"));
+                tokenExpiresIn.put(appKey, date);
+                logger.info("getAccessToken:" + tokenMap.get(appKey));
+            } catch (UnsupportedEncodingException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return tokenMap.get(appKey);
+    }
+
+    private JSONObject execute(String api, MultiValueMap<String, Object> params) throws RuntimeException {
+        try {
+            HttpHeaders headers = new HttpHeaders();
+            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+            RestTemplate restTemplate = new RestTemplate();
+
+            HttpEntity<MultiValueMap<String, Object>> formEntity = new HttpEntity<>(params, headers);
+            String result = restTemplate.postForObject(api, formEntity, String.class);
+            logger.info("BaiduAi返回结果:" + result);
+            JSONObject a = JSONObject.parseObject(result);
+
+            if (a.getInteger("status") == 0) {
+                throw new Exception("BaiduAi接口错误: " + result);
+            }
+            return a;
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+}

+ 102 - 0
server/tools/src/main/java/com/nuliji/tools/third/sendcloud/SendCloudMail.java

@@ -0,0 +1,102 @@
+package com.nuliji.tools.third.sendcloud;
+
+import com.alibaba.fastjson.JSONObject;
+import com.nuliji.tools.Tools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+
+import java.io.File;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class SendCloudMail {
+    private static final Logger logger = LoggerFactory.getLogger(SendCloudMail.class);
+
+    final static String api = "http://api.sendcloud.net/apiv2/";
+
+    private String apiUser = "";
+    private String apiKey = "";
+
+    public SendCloudMail(String apiUser, String apiKey){
+        this.apiUser = apiUser;
+        this.apiKey = apiKey;
+    }
+
+    public class Response {
+        private String message;
+        private JSONObject info;
+        private boolean result;
+        private int statusCode;
+
+        public String getMessage() {
+            return message;
+        }
+
+        public JSONObject getInfo(){
+            return info;
+        }
+
+        public boolean getResult(){
+            return result;
+        }
+
+        public int getStatusCode(){
+            return statusCode;
+        }
+    }
+
+    public Response sendMail(String to, String subject, String body, String from, String fromName, FileSystemResource attachments) {
+        MultiValueMap<String, Object> params = new LinkedMultiValueMap<String, Object>();
+        params.add("apiUser", apiUser);
+        params.add("apiKey", apiKey);
+        params.add("to", to);
+        params.add("subject", subject);
+        params.add("html", body);
+        params.add("from", from);
+        if (fromName != null) {
+            params.add("fromName", fromName);
+        }
+        if (attachments != null){
+            params.add("attachments", attachments);
+        }
+
+        return execute(api+"mail/send", params);
+    }
+
+    private Response execute(String api, MultiValueMap<String, Object> params) throws RuntimeException {
+        try {
+
+            HttpHeaders headers = new HttpHeaders();
+            headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+            RestTemplate restTemplate = new RestTemplate();
+
+            HttpEntity<MultiValueMap<String, Object>> formEntity = new HttpEntity<>(params, headers);
+            String result = restTemplate.postForObject(api, formEntity, String.class);
+            logger.info("SendCloud返回结果:" + result);
+            JSONObject a = JSONObject.parseObject(result);
+
+            if (a.getInteger("status") == 0) {
+                throw new Exception("SendCloud接口错误: " + result);
+            }
+            Response response = new Response();
+            response.info = a.getJSONObject("info");
+            response.message = a.getString("message");
+            response.result = a.getBooleanValue("result");
+            response.statusCode = a.getIntValue("statusCode");
+            return response;
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+}

+ 111 - 0
server/tools/src/main/java/com/nuliji/tools/third/sendcloud/SendCloudSms.java

@@ -0,0 +1,111 @@
+package com.nuliji.tools.third.sendcloud;
+
+import com.alibaba.fastjson.JSONObject;
+import com.nuliji.tools.Tools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.*;
+
+public class SendCloudSms {
+    private static final Logger logger = LoggerFactory.getLogger(SendCloudSms.class);
+
+    final static String api = "http://www.sendcloud.net/smsapi/";
+
+    private String smsUser = "";
+    private String smsKey = "";
+
+    public SendCloudSms(String smsUser, String smsKey) {
+        this.smsUser = smsUser;
+        this.smsKey = smsKey;
+    }
+
+    public class Response {
+        private String message;
+        private JSONObject info;
+        private boolean result;
+        private int statusCode;
+
+        public String getMessage() {
+            return message;
+        }
+
+        public JSONObject getInfo(){
+            return info;
+        }
+
+        public boolean getResult(){
+            return result;
+        }
+
+        public int getStatusCode(){
+            return statusCode;
+        }
+    }
+
+    public Response sendSms(String mobile, String templateId, JSONObject vars) {
+        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
+        params.add("smsUser", smsUser);
+        params.add("templateId", templateId);
+        params.add("msgType", "0");
+        params.add("phone", mobile);
+        params.add("vars", JSONObject.toJSONString(vars));
+
+        return execute(api+"send", params);
+    }
+
+    private String signature(MultiValueMap<String, String> params) {
+        // 对参数进行排序
+        Map<String, String> sortedMap = new TreeMap<String, String>(new Comparator<String>() {
+
+            public int compare(String arg0, String arg1) {
+                // 忽略大小写
+                return arg0.compareToIgnoreCase(arg1);
+            }
+        });
+        sortedMap.putAll(params.toSingleValueMap());
+
+        // 计算签名
+        StringBuilder sb = new StringBuilder();
+        sb.append(smsKey).append("&");
+        for (String s : sortedMap.keySet()) {
+            sb.append(String.format("%s=%s&", s, sortedMap.get(s)));
+        }
+        sb.append(smsKey);
+        return Tools.stringMD5(sb.toString());
+    }
+
+    private Response execute(String api, MultiValueMap<String, String> params) throws RuntimeException {
+        try {
+            params.add("signature", signature(params));
+
+            HttpHeaders headers = new HttpHeaders();
+            RestTemplate restTemplate = new RestTemplate();
+
+            HttpEntity<MultiValueMap<String, String>> formEntity = new HttpEntity<>(params, headers);
+            String result = restTemplate.postForObject(api, formEntity, String.class);
+            logger.info("SendCloud返回结果:" + result);
+            JSONObject a = JSONObject.parseObject(result);
+
+            if (a.getInteger("status") != 200) {
+                throw new Exception("SendCloud接口错误: " + result);
+            }
+            Response response = new Response();
+            response.info = a.getJSONObject("info");
+            response.message = a.getString("message");
+            response.result = a.getBooleanValue("result");
+            response.statusCode = a.getIntValue("statusCode");
+            return response;
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+}