소스 검색

20191014钉钉

suochencheng 6 년 전
부모
커밋
96e249cc58
38개의 변경된 파일2246개의 추가작업 그리고 0개의 파일을 삭제
  1. 1 0
      Permanent_Data/accesstoken.xml
  2. BIN
      lib/client-sdk.api-1.0.2.jar
  3. BIN
      lib/client-sdk.common-1.0.0-SNAPSHOT.jar
  4. BIN
      lib/client-sdk.core-1.0.0-SNAPSHOT.jar
  5. BIN
      lib/client-sdk.spring-1.0.0-SNAPSHOT.jar
  6. BIN
      lib/lippi-oapi-encrpt.jar
  7. BIN
      lib/taobao-sdk-java-auto_1479188381469-20191011-source.jar
  8. BIN
      lib/taobao-sdk-java-auto_1479188381469-20191011.jar
  9. 55 0
      pom.xml
  10. 287 0
      src/main/java/com/izouma/dingtalk/Demo.java
  11. 85 0
      src/main/java/com/izouma/dingtalk/DemoTest.java
  12. 65 0
      src/main/java/com/izouma/dingtalk/Env.java
  13. 14 0
      src/main/java/com/izouma/dingtalk/OApiException.java
  14. 180 0
      src/main/java/com/izouma/dingtalk/auth/AuthHelper.java
  15. 63 0
      src/main/java/com/izouma/dingtalk/department/DepartmentHelper.java
  16. 80 0
      src/main/java/com/izouma/dingtalk/eventchange/eventChangeHelper.java
  17. 53 0
      src/main/java/com/izouma/dingtalk/media/MediaHelper.java
  18. 16 0
      src/main/java/com/izouma/dingtalk/message/ConversationMessageDelivery.java
  19. 17 0
      src/main/java/com/izouma/dingtalk/message/ImageMessage.java
  20. 15 0
      src/main/java/com/izouma/dingtalk/message/LightAppMessageDelivery.java
  21. 22 0
      src/main/java/com/izouma/dingtalk/message/LinkMessage.java
  22. 6 0
      src/main/java/com/izouma/dingtalk/message/Message.java
  23. 15 0
      src/main/java/com/izouma/dingtalk/message/MessageDelivery.java
  24. 43 0
      src/main/java/com/izouma/dingtalk/message/MessageHelper.java
  25. 69 0
      src/main/java/com/izouma/dingtalk/message/OAMessage.java
  26. 17 0
      src/main/java/com/izouma/dingtalk/message/TextMessage.java
  27. 43 0
      src/main/java/com/izouma/dingtalk/user/User.java
  28. 143 0
      src/main/java/com/izouma/dingtalk/user/UserHelper.java
  29. 135 0
      src/main/java/com/izouma/dingtalk/utils/FileUtils.java
  30. 251 0
      src/main/java/com/izouma/dingtalk/utils/HttpHelper.java
  31. 53 0
      src/main/java/com/izouma/dingtalk/utils/aes/DingTalkEncryptException.java
  32. 211 0
      src/main/java/com/izouma/dingtalk/utils/aes/DingTalkEncryptor.java
  33. 52 0
      src/main/java/com/izouma/dingtalk/utils/aes/DingTalkJsApiSingnature.java
  34. 50 0
      src/main/java/com/izouma/dingtalk/utils/aes/PKCS7Padding.java
  35. 53 0
      src/main/java/com/izouma/dingtalk/utils/aes/Utils.java
  36. 90 0
      src/main/java/com/izouma/zhumj/web/DingtalkController.java
  37. 29 0
      src/main/resources/logback-spring.xml
  38. 33 0
      src/test/java/com/izouma/zhumj/DingDingTest.java

+ 1 - 0
Permanent_Data/accesstoken.xml

@@ -0,0 +1 @@
+{"dingmiwmqwbheqscaqn1":{"access_token":"ff53ed905f853339a8958d776b86599c","begin_time":1571043265860}}

BIN
lib/client-sdk.api-1.0.2.jar


BIN
lib/client-sdk.common-1.0.0-SNAPSHOT.jar


BIN
lib/client-sdk.core-1.0.0-SNAPSHOT.jar


BIN
lib/client-sdk.spring-1.0.0-SNAPSHOT.jar


BIN
lib/lippi-oapi-encrpt.jar


BIN
lib/taobao-sdk-java-auto_1479188381469-20191011-source.jar


BIN
lib/taobao-sdk-java-auto_1479188381469-20191011.jar


+ 55 - 0
pom.xml

@@ -214,6 +214,61 @@
             <artifactId>spring-boot-starter-freemarker</artifactId>
         </dependency>
 
+        <!-- 钉钉 -->
+        <dependency>
+            <groupId>com.laiwang.lippi</groupId>
+            <artifactId>lippi.oapi.encryt</artifactId>
+            <version>1.0.3-SNAPSHOT</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/lib/lippi-oapi-encrpt.jar</systemPath>
+        </dependency>
+        <dependency>
+            <groupId>com.dingtalk.open</groupId>
+            <artifactId>client-sdk.api</artifactId>
+            <version>1.0.2</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/lib/client-sdk.api-1.0.2.jar</systemPath>
+        </dependency>
+        <dependency>
+            <groupId>com.dingtalk.open</groupId>
+            <artifactId>client-sdk.common</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/lib/client-sdk.common-1.0.0-SNAPSHOT.jar</systemPath>
+        </dependency>
+        <dependency>
+            <groupId>com.dingtalk.open</groupId>
+            <artifactId>client-sdk.core</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/lib/client-sdk.core-1.0.0-SNAPSHOT.jar</systemPath>
+        </dependency>
+        <dependency>
+            <groupId>com.dingtalk.open</groupId>
+            <artifactId>client-sdk.spring</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/lib/client-sdk.spring-1.0.0-SNAPSHOT.jar</systemPath>
+        </dependency>
+        <dependency>
+            <groupId>com.dingtalk.open</groupId>
+            <artifactId>taobao-sdk-java</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/lib/taobao-sdk-java-auto_1479188381469-20191011.jar</systemPath>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.37</version>
+        </dependency>
+        <dependency>
+            <groupId>com.ning</groupId>
+            <artifactId>async-http-client</artifactId>
+            <version>1.9.32</version>
+        </dependency>
+        <!-- 钉钉 -->
+
     </dependencies>
 
 </project>

+ 287 - 0
src/main/java/com/izouma/dingtalk/Demo.java

@@ -0,0 +1,287 @@
+package com.izouma.dingtalk;
+
+import com.izouma.dingtalk.auth.AuthHelper;
+import com.izouma.dingtalk.department.DepartmentHelper;
+import com.izouma.dingtalk.media.MediaHelper;
+import com.izouma.dingtalk.message.LightAppMessageDelivery;
+import com.izouma.dingtalk.message.MessageHelper;
+import com.izouma.dingtalk.user.UserHelper;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.dingtalk.open.client.api.model.corp.*;
+import com.dingtalk.open.client.api.model.corp.MessageBody.OABody.Body;
+import com.dingtalk.open.client.api.model.corp.MessageBody.OABody.Body.Form;
+import com.dingtalk.open.client.api.model.corp.MessageBody.OABody.Body.Rich;
+import com.dingtalk.open.client.api.model.corp.MessageBody.OABody.Head;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 本地测试方法钉钉API
+ */
+public class Demo {
+
+    public static String TO_USER = "";
+    public static String TO_PARTY = "";
+    public static String AGENT_ID = "";
+
+    public static void main(String[] args) throws Exception {
+
+        try {
+
+            List<Department> departments = new ArrayList<Department>();
+            departments = DepartmentHelper.listDepartments(AuthHelper.getAccessToken(), "1");
+            JSONObject usersJSON = new JSONObject();
+
+            System.out.println("depart num:" + departments.size());
+            for (int i = 0; i < departments.size(); i++) {
+                JSONObject userDepJSON = new JSONObject();
+                System.out.println("dep:" + departments.get(i).toString());
+
+                long offset = 0;
+                int size = 5;
+                CorpUserList corpUserList = new CorpUserList();
+                while (true) {
+                    corpUserList = UserHelper.getDepartmentUser(AuthHelper.getAccessToken(), Long.valueOf(departments.get(i).getId())
+                            , offset, size, null);
+                    System.out.println(JSON.toJSONString(corpUserList));
+                    if (Boolean.TRUE.equals(corpUserList.isHasMore())) {
+                        offset += size;
+                    } else {
+                        break;
+                    }
+                }
+                if (corpUserList.getUserlist().size() == 0) {
+                    continue;
+                }
+                for (int j = 0; j < corpUserList.getUserlist().size(); j++) {
+                    String user = JSON.toJSONString(corpUserList.getUserlist().get(j));
+                    userDepJSON.put(j + "", JSONObject.parseObject(user, CorpUserDetail.class));
+
+                }
+
+
+                usersJSON.put(departments.get(i).getName(), userDepJSON);
+                System.out.println("user:" + usersJSON.toString());
+            }
+
+            System.out.println("depart:" + usersJSON.toJSONString());
+
+
+            // 获取access token
+            String accessToken = AuthHelper.getAccessToken();
+            log("成功获取access token: ", accessToken);
+
+            // 获取jsapi ticket
+            String ticket = AuthHelper.getJsapiTicket(accessToken);
+            log("成功获取jsapi ticket: ", ticket);
+
+            // 获取签名
+            String nonceStr = "nonceStr";
+            long timeStamp = System.currentTimeMillis();
+            String url = "http://www.dingtalk.com";
+            String signature = AuthHelper.sign(ticket, nonceStr, timeStamp, url);
+            log("成功签名: ", signature);
+
+            // 创建部门
+            String name = "TestDept.34";
+            String parentId = "1";
+            String order = "1";
+            boolean createDeptGroup = true;
+            long departmentId = Long.parseLong(DepartmentHelper.createDepartment(accessToken, name, parentId, order, createDeptGroup));
+            log("成功创建部门", name, " 部门id=", departmentId);
+
+            // 获取部门列表
+            List<Department> list = DepartmentHelper.listDepartments(accessToken, parentId);
+            log("成功获取部门列表", list);
+
+            // 更新部门
+            name = "hahahaha";
+            boolean autoAddUser = true;
+            String deptManagerUseridList = null;
+            boolean deptHiding = false;
+            String deptPerimits = "aa|qq";
+            DepartmentHelper.updateDepartment(accessToken, departmentId, name, parentId, order, createDeptGroup,
+                    autoAddUser, deptManagerUseridList, deptHiding, deptPerimits, null,
+                    null, null, null, null);
+
+
+            log("成功更新部门", " 部门id=", departmentId);
+
+            CorpUserDetail userDetail = new CorpUserDetail();
+            userDetail.setUserid("id_yuhuan");
+            userDetail.setName("name_yuhuan");
+            userDetail.setEmail("yuhuan@abc.com");
+            userDetail.setMobile("18612124567");
+            userDetail.setDepartment(new ArrayList());
+            userDetail.getDepartment().add(departmentId);
+
+
+            UserHelper.createUser(accessToken, userDetail);
+            log("成功创建成员", "成员信息=", userDetail);
+
+            // 上传图片
+            File file = new File("/Users/ian/Downloads/lALOAVYgbc0DIM0Bwg_450_800.png");
+            UploadResult uploadResult = MediaHelper.upload(accessToken, MediaHelper.TYPE_IMAGE, file);
+            log("成功上传图片", uploadResult);
+
+            // 下载图片
+            String fileDir = "/Users/ian/Desktop/";
+            MediaHelper.download(accessToken, uploadResult.getMedia_id(), fileDir);
+            log("成功下载图片");
+
+
+            MessageBody.TextBody textBody = new MessageBody.TextBody();
+            textBody.setContent("TextMessage");
+
+            MessageBody.ImageBody imageBody = new MessageBody.ImageBody();
+            imageBody.setMedia_id(uploadResult.getMedia_id());
+
+            MessageBody.LinkBody linkBody = new MessageBody.LinkBody();
+            linkBody.setMessageUrl("http://www.baidu.com");
+            linkBody.setPicUrl("@lALOACZwe2Rk");
+            linkBody.setTitle("Link Message");
+            linkBody.setText("This is a link message");
+
+            // 创建oa消息
+            MessageBody.OABody oaBody = new MessageBody.OABody();
+            oaBody.setMessage_url("message_url");
+            Head head = new Head();
+            head.setText("head.text");
+            head.setBgcolor("FFBBBBBB");
+            oaBody.setHead(head);
+
+            Body body = new Body();
+            body.setAuthor("author");
+            body.setContent("content");
+            body.setFile_count("file_count");
+            body.setImage("@image");
+            body.setTitle("body.title");
+            Rich rich = new Rich();
+            rich.setNum("num");
+            rich.setUnit("unit");
+            body.setRich(rich);
+            List<Form> formList = new ArrayList<Form>();
+            Form form = new Form();
+            form.setKey("key");
+            form.setValue("value");
+            formList.add(form);
+            body.setForm(formList);
+            oaBody.setBody(body);
+
+            // 发送微应用消息
+            String toUsers = TO_USER;
+            String toParties = TO_PARTY;
+            String agentId = AGENT_ID;
+            LightAppMessageDelivery lightAppMessageDelivery = new LightAppMessageDelivery(toUsers, toParties, agentId);
+
+            lightAppMessageDelivery.withMessage(MessageType.TEXT, textBody);
+            MessageHelper.send(accessToken, lightAppMessageDelivery);
+            log("成功发送 微应用文本消息");
+            lightAppMessageDelivery.withMessage(MessageType.IMAGE, imageBody);
+            MessageHelper.send(accessToken, lightAppMessageDelivery);
+            log("成功发送 微应用图片消息");
+            lightAppMessageDelivery.withMessage(MessageType.LINK, linkBody);
+            MessageHelper.send(accessToken, lightAppMessageDelivery);
+            log("成功发送 微应用link消息");
+            lightAppMessageDelivery.withMessage(MessageType.OA, oaBody);
+            MessageHelper.send(accessToken, lightAppMessageDelivery);
+            log("成功发送 微应用oa消息");
+
+            // 发送会话消息
+//			String sender = Vars.SENDER;
+//			String cid = Vars.CID;//cid需要通过jsapi获取,具体详情请查看开放平台文档--->客户端文档--->会话
+
+//			ConversationMessageDelivery conversationMessageDelivery = new ConversationMessageDelivery(sender, cid,
+//					agentId);
+//
+//			conversationMessageDelivery.withMessage(MessageType.TEXT, textBody);
+//			MessageHelper.send(accessToken, conversationMessageDelivery);
+//			log("成功发送 会话文本消息");
+//			conversationMessageDelivery.withMessage(MessageType.IMAGE, imageBody);
+//			MessageHelper.send(accessToken, conversationMessageDelivery);
+//			log("成功发送 会话图片消息");
+//			conversationMessageDelivery.withMessage(MessageType.LINK, linkBody);
+//			MessageHelper.send(accessToken, conversationMessageDelivery);
+//			log("成功发送 会话link消息");
+
+            // 更新成员
+            userDetail.setMobile("11177776666");
+            UserHelper.updateUser(accessToken, userDetail);
+            log("成功更新成员", "成员信息=", userDetail);
+
+            // 获取成员
+            CorpUserDetail userDetail11 = UserHelper.getUser(accessToken, userDetail.getUserid());
+            log("成功获取成员", "成员userid=", userDetail11.getUserid());
+
+            // 获取部门成员
+            CorpUserList userList = UserHelper.getDepartmentUser(accessToken, departmentId, null, null, null);
+            log("成功获取部门成员", "部门成员user=", userList.getUserlist());
+
+            // 获取部门成员(详情)
+            CorpUserDetailList userList2 = UserHelper.getUserDetails(accessToken, departmentId, null, null, null);
+            log("成功获取部门成员详情", "部门成员详情user=", userList2.getUserlist());
+
+            // 批量删除成员
+//			User user2 = new User("id_yuhuan2", "name_yuhuan2");
+//			user2.email = "yuhua2n@abc.com";
+//			user2.mobile = "18611111111";
+//			user2.department = new ArrayList();
+//			user2.department.add(departmentId);
+//			UserHelper.createUser(accessToken, user2);
+
+            CorpUserDetail userDetail2 = new CorpUserDetail();
+            userDetail2.setUserid("id_yuhuan2");
+            userDetail2.setName("name_yuhuan2");
+            userDetail2.setEmail("yuhua2n@abc.com");
+            userDetail2.setMobile("18612124926");
+            userDetail2.setDepartment(new ArrayList());
+            userDetail2.getDepartment().add(departmentId);
+            UserHelper.createUser(accessToken, userDetail2);
+
+
+            List<String> useridlist = new ArrayList();
+            useridlist.add(userDetail.getUserid());
+            useridlist.add(userDetail2.getUserid());
+            UserHelper.batchDeleteUser(accessToken, useridlist);
+            log("成功批量删除成员", "成员列表useridlist=", useridlist);
+
+            // 删除成员
+//			User user3 = new User("id_yuhuan3", "name_yuhuan3");
+//			user3.email = "yuhua2n@abc.com";
+//			user3.mobile = "18611111111";
+//			user3.department = new ArrayList();
+//			user3.department.add(departmentId);
+            CorpUserDetail userDetail3 = new CorpUserDetail();
+            userDetail3.setUserid("id_yuhuan3");
+            userDetail3.setName("name_yuhuan3");
+            userDetail3.setMobile("13146654734");
+            userDetail3.setDepartment(new ArrayList());
+            userDetail3.getDepartment().add(departmentId);
+
+
+            UserHelper.createUser(accessToken, userDetail3);
+            UserHelper.deleteUser(accessToken, userDetail3.getUserid());
+            log("成功删除成员", "成员userid=", userDetail3.getUserid());
+
+            // 删除部门
+            DepartmentHelper.deleteDepartment(accessToken, departmentId);
+            log("成功删除部门", " 部门id=", departmentId);
+
+        } catch (OApiException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private static void log(Object... msgs) {
+        StringBuilder sb = new StringBuilder();
+        for (Object o : msgs) {
+            if (o != null) {
+                sb.append(o.toString());
+            }
+        }
+        System.out.println(sb.toString());
+    }
+}

+ 85 - 0
src/main/java/com/izouma/dingtalk/DemoTest.java

@@ -0,0 +1,85 @@
+package com.izouma.dingtalk;
+
+import com.izouma.dingtalk.auth.AuthHelper;
+import com.izouma.dingtalk.department.DepartmentHelper;
+import com.izouma.dingtalk.user.UserHelper;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.dingtalk.open.client.api.model.corp.CorpUserDetail;
+import com.dingtalk.open.client.api.model.corp.CorpUserDetailList;
+import com.dingtalk.open.client.api.model.corp.CorpUserList;
+import com.dingtalk.open.client.api.model.corp.Department;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 本地测试方法钉钉API
+ */
+public class DemoTest {
+
+    public static void main(String[] args) throws Exception {
+
+        try {
+
+            List<Department> departments = new ArrayList<Department>();
+            departments = DepartmentHelper.listDepartments(AuthHelper.getAccessToken(), "1");
+            JSONObject usersJSON = new JSONObject();
+
+            System.out.println("depart num:" + departments.size());
+            for (int i = 0; i < departments.size(); i++) {
+
+                // 获取部门成员(详情)
+                CorpUserDetailList userList2 = UserHelper.getUserDetails(AuthHelper.getAccessToken(), Long.valueOf(departments.get(i).getId()), null, null, null);
+                log("成功获取部门成员详情", "部门成员详情user=", userList2.getUserlist());
+
+                JSONObject userDepJSON = new JSONObject();
+                System.out.println("dep:" + departments.get(i).toString());
+
+                long offset = 0;
+                int size = 5;
+                CorpUserList corpUserList = new CorpUserList();
+                while (true) {
+                    corpUserList = UserHelper.getDepartmentUser(AuthHelper.getAccessToken(), Long.valueOf(departments.get(i).getId())
+                            , offset, size, null);
+                    System.out.println(JSON.toJSONString(corpUserList));
+                    if (Boolean.TRUE.equals(corpUserList.isHasMore())) {
+                        offset += size;
+                    } else {
+                        break;
+                    }
+                }
+                if (corpUserList.getUserlist().size() == 0) {
+                    continue;
+                }
+                for (int j = 0; j < corpUserList.getUserlist().size(); j++) {
+                    String user = JSON.toJSONString(corpUserList.getUserlist().get(j));
+                    userDepJSON.put(j + "", JSONObject.parseObject(user, CorpUserDetail.class));
+
+                }
+
+
+                usersJSON.put(departments.get(i).getName(), userDepJSON);
+                System.out.println("user:" + usersJSON.toString());
+
+
+
+            }
+
+            System.out.println("depart:" + usersJSON.toJSONString());
+
+        } catch (OApiException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private static void log(Object... msgs) {
+        StringBuilder sb = new StringBuilder();
+        for (Object o : msgs) {
+            if (o != null) {
+                sb.append(o.toString());
+            }
+        }
+        System.out.println(sb.toString());
+    }
+}

+ 65 - 0
src/main/java/com/izouma/dingtalk/Env.java

@@ -0,0 +1,65 @@
+package com.izouma.dingtalk;
+
+
+/**
+ * 企业应用接入时的常量定义
+ */
+public class Env {
+
+    /**
+     * 企业corpid
+     */
+    public static final String CORP_ID = "dingaaf79e5a7a84bbe235c2f4657eb6378f";
+
+    /**
+     * 应用agentId
+     */
+    public static final String AGENT_ID = "302997798";
+
+    /**
+     * 应用的appkey
+     */
+    public static final String APP_KEY = "dingmiwmqwbheqscaqn1";
+
+    /**
+     * 应用的appsecret
+     */
+    public static final String APP_SECRET = "Ple3KQpw0US03IgfQnrHlfC_unPyVaW0JHPlT8kx2mXOAnh40xq2i4Pq_Feskm8h";
+
+    /**
+     * 回调host
+     */
+    public static final String CALLBACK_URL_HOST = "";
+
+    /**
+     * 加解密需要用到的token,企业可以随机填写。如 "123456"
+     */
+	public static final String TOKEN = "123456";
+
+    /**
+     * 数据加密密钥。用于回调数据的加密,长度固定为43个字符,从a-z, A-Z, 0-9共62个字符中选取,您可以随机生成
+     */
+	public static final String ENCODING_AES_KEY = "abcdefghijabcdefghijabcdefghijabcdefghij123";
+
+    /**
+     * DING API地址
+     */
+    public static final String OAPI_HOST = "https://oapi.dingtalk.com";
+
+    /**
+     * 删除企业回调接口url
+     */
+    public static final String DELETE_CALLBACK = "https://oapi.dingtalk.com/call_back/delete_call_back";
+
+    /**
+     * 注册企业回调接口url
+     */
+    public static final String REGISTER_CALLBACK = "https://oapi.dingtalk.com/call_back/register_call_back";
+
+    /**
+     * 企业应用后台地址,用户管理后台免登使用
+     */
+    public static final String OA_BACKGROUND_URL = "";
+
+    public static final String SSO_Secret = "";
+}

+ 14 - 0
src/main/java/com/izouma/dingtalk/OApiException.java

@@ -0,0 +1,14 @@
+package com.izouma.dingtalk;
+
+public class OApiException extends Exception {
+
+    public static final int ERR_RESULT_RESOLUTION = -2;
+
+    public OApiException(String field) {
+        this(ERR_RESULT_RESOLUTION, "Cannot resolve field " + field + " from oapi resonpse");
+    }
+
+	public OApiException(int errCode, String errMsg) {
+		super("error code: " + errCode + ", error message: " + errMsg);
+	}
+}

+ 180 - 0
src/main/java/com/izouma/dingtalk/auth/AuthHelper.java

@@ -0,0 +1,180 @@
+package com.izouma.dingtalk.auth;
+
+import com.izouma.dingtalk.Env;
+import com.izouma.dingtalk.OApiException;
+import com.izouma.dingtalk.utils.FileUtils;
+import com.izouma.dingtalk.utils.HttpHelper;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.dingtalk.oapi.lib.aes.DingTalkJsApiSingnature;
+import com.dingtalk.open.client.ServiceFactory;
+import com.dingtalk.open.client.api.model.corp.JsapiTicket;
+import com.dingtalk.open.client.api.service.corp.CorpConnectionService;
+import com.dingtalk.open.client.api.service.corp.JsapiService;
+
+import javax.servlet.http.HttpServletRequest;
+import java.net.URLDecoder;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * AccessToken和jsticket的获取封装
+ */
+public class AuthHelper {
+
+    /**
+     * 调整到1小时50分钟
+     */
+    public static final long cacheTime = 1000 * 60 * 55 * 2;
+
+    /**
+     * 在此方法中,为了避免频繁获取access_token,
+     * 在距离上一次获取access_token时间在两个小时之内的情况,
+     * 将直接从持久化存储中读取access_token
+     *
+     * 因为access_token和jsapi_ticket的过期时间都是7200秒
+     * 所以在获取access_token的同时也去获取了jsapi_ticket
+     * 注:jsapi_ticket是在前端页面JSAPI做权限验证配置的时候需要使用的
+     * 具体信息请查看开发者文档--权限验证配置
+     */
+    public static String getAccessToken() throws OApiException {
+        long curTime = System.currentTimeMillis();
+        JSONObject accessTokenValue = (JSONObject) FileUtils.getValue("accesstoken", Env.APP_KEY);
+        String accToken = "";
+        JSONObject jsontemp = new JSONObject();
+        if (accessTokenValue == null || curTime - accessTokenValue.getLong("begin_time") >= cacheTime) {
+            try {
+                ServiceFactory serviceFactory = ServiceFactory.getInstance();
+                CorpConnectionService corpConnectionService = serviceFactory.getOpenService(CorpConnectionService.class);
+                accToken = corpConnectionService.getCorpToken(Env.APP_KEY, Env.APP_SECRET);
+                // save accessToken
+                JSONObject jsonAccess = new JSONObject();
+                jsontemp.clear();
+                jsontemp.put("access_token", accToken);
+                jsontemp.put("begin_time", curTime);
+                jsonAccess.put(Env.APP_KEY, jsontemp);
+                //真实项目中最好保存到数据库中
+                FileUtils.write2File(jsonAccess, "accesstoken");
+
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        } else {
+            return accessTokenValue.getString("access_token");
+        }
+        return accToken;
+    }
+
+    /**
+     * 获取JSTicket, 用于js的签名计算
+     * 正常的情况下,jsapi_ticket的有效期为7200秒,所以开发者需要在某个地方设计一个定时器,定期去更新jsapi_ticket
+     */
+    public static String getJsapiTicket(String accessToken) throws OApiException {
+        JSONObject jsTicketValue = (JSONObject) FileUtils.getValue("jsticket", Env.APP_KEY);
+        long curTime = System.currentTimeMillis();
+        String jsTicket = "";
+
+        if (jsTicketValue == null || curTime -
+                jsTicketValue.getLong("begin_time") >= cacheTime) {
+            ServiceFactory serviceFactory;
+            try {
+                serviceFactory = ServiceFactory.getInstance();
+                JsapiService jsapiService = serviceFactory.getOpenService(JsapiService.class);
+
+                JsapiTicket JsapiTicket = jsapiService.getJsapiTicket(accessToken, "jsapi");
+                jsTicket = JsapiTicket.getTicket();
+
+                JSONObject jsonTicket = new JSONObject();
+                JSONObject jsontemp = new JSONObject();
+                jsontemp.clear();
+                jsontemp.put("ticket", jsTicket);
+                jsontemp.put("begin_time", curTime);
+                jsonTicket.put(Env.APP_KEY, jsontemp);
+                FileUtils.write2File(jsonTicket, "jsticket");
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            return jsTicket;
+        } else {
+            return jsTicketValue.getString("ticket");
+        }
+    }
+
+    public static String sign(String ticket, String nonceStr, long timeStamp, String url) throws OApiException {
+        try {
+            return DingTalkJsApiSingnature.getJsApiSingnature(url, nonceStr, timeStamp, ticket);
+        } catch (Exception ex) {
+            throw new OApiException(0, ex.getMessage());
+        }
+    }
+
+    /**
+     * 计算当前请求的jsapi的签名数据<br/>
+     * <p>
+     * 如果签名数据是通过ajax异步请求的话,签名计算中的url必须是给用户展示页面的url
+     *
+     * @param request
+     * @return
+     */
+    public static String getConfig(HttpServletRequest request) {
+        String urlString = request.getRequestURL().toString();
+        String queryString = request.getQueryString();
+
+        String queryStringEncode = null;
+        String url;
+        if (queryString != null) {
+            queryStringEncode = URLDecoder.decode(queryString);
+            url = urlString + "?" + queryStringEncode;
+        } else {
+            url = urlString;
+        }
+        /**
+         * 确认url与配置的应用首页地址一致
+         */
+        System.out.println(url);
+
+        /**
+         * 随机字符串
+         */
+        String nonceStr = "abcdefg";
+        long timeStamp = System.currentTimeMillis() / 1000;
+        String signedUrl = url;
+        String accessToken = null;
+        String ticket = null;
+        String signature = null;
+
+        try {
+            accessToken = AuthHelper.getAccessToken();
+
+            ticket = AuthHelper.getJsapiTicket(accessToken);
+            signature = AuthHelper.sign(ticket, nonceStr, timeStamp, signedUrl);
+
+        } catch (OApiException e) {
+            e.printStackTrace();
+        }
+
+        Map<String, Object> configValue = new HashMap<>();
+        configValue.put("jsticket", ticket);
+        configValue.put("signature", signature);
+        configValue.put("nonceStr", nonceStr);
+        configValue.put("timeStamp", timeStamp);
+        configValue.put("corpId", Env.CORP_ID);
+        configValue.put("agentId", Env.AGENT_ID);
+
+        String config = JSON.toJSONString(configValue);
+        return config;
+    }
+
+    public static String getSsoToken() throws OApiException {
+        String url = "https://oapi.dingtalk.com/sso/gettoken?corpid=" + Env.CORP_ID + "&corpsecret=" + Env.SSO_Secret;
+        JSONObject response = HttpHelper.httpGet(url);
+        String ssoToken;
+        if (response.containsKey("access_token")) {
+            ssoToken = response.getString("access_token");
+        } else {
+            throw new OApiException("Sso_token");
+        }
+        return ssoToken;
+
+    }
+}

+ 63 - 0
src/main/java/com/izouma/dingtalk/department/DepartmentHelper.java

@@ -0,0 +1,63 @@
+package com.izouma.dingtalk.department;
+
+import com.dingtalk.open.client.ServiceFactory;
+import com.dingtalk.open.client.api.model.corp.Department;
+import com.dingtalk.open.client.api.model.corp.DepartmentDetail;
+import com.dingtalk.open.client.api.service.corp.CorpDepartmentService;
+import com.dingtalk.open.client.common.SdkInitException;
+import com.dingtalk.open.client.common.ServiceException;
+import com.dingtalk.open.client.common.ServiceNotExistException;
+
+import java.util.List;
+
+/**
+ * 部门相关API
+ *
+ * https://open-doc.dingtalk.com/docs/doc.htm?treeId=371&articleId=106817&docType=1
+ */
+public class DepartmentHelper {
+
+    /**
+     *  创建部门
+     */
+    public static String createDepartment(String accessToken, String name,
+                                          String parentId, String order, boolean createDeptGroup) throws Exception {
+
+        CorpDepartmentService corpDepartmentService = ServiceFactory.getInstance().getOpenService(CorpDepartmentService.class);
+        return corpDepartmentService.deptCreate(accessToken, name, parentId, order, createDeptGroup);
+    }
+
+    /**
+     * 获取部门列表
+     */
+    public static List<Department> listDepartments(String accessToken, String parentDeptId)
+            throws ServiceNotExistException, SdkInitException, ServiceException {
+        CorpDepartmentService corpDepartmentService = ServiceFactory.getInstance().getOpenService(CorpDepartmentService.class);
+        List<Department> deptList = corpDepartmentService.getDeptList(accessToken, parentDeptId);
+        return deptList;
+    }
+
+
+    /**
+     * 删除部门
+     */
+    public static void deleteDepartment(String accessToken, Long id) throws Exception {
+        CorpDepartmentService corpDepartmentService = ServiceFactory.getInstance().getOpenService(CorpDepartmentService.class);
+        corpDepartmentService.deptDelete(accessToken, id);
+    }
+
+    /**
+     * 更新部门
+     */
+    public static void updateDepartment(String accessToken, long id, String name,
+                                        String parentId, String order, Boolean createDeptGroup,
+                                        boolean autoAddUser, String deptManagerUseridList, boolean deptHiding, String deptPerimits,
+                                        String userPerimits, Boolean outerDept, String outerPermitDepts,
+                                        String outerPermitUsers, String orgDeptOwner) throws Exception {
+        CorpDepartmentService corpDepartmentService = ServiceFactory.getInstance().getOpenService(CorpDepartmentService.class);
+        corpDepartmentService.deptUpdate(accessToken, id, name, parentId, order, createDeptGroup,
+                autoAddUser, deptManagerUseridList, deptHiding, deptPerimits, userPerimits,
+                outerDept, outerPermitDepts, outerPermitUsers, orgDeptOwner);
+
+    }
+}

+ 80 - 0
src/main/java/com/izouma/dingtalk/eventchange/eventChangeHelper.java

@@ -0,0 +1,80 @@
+package com.izouma.dingtalk.eventchange;
+
+import com.izouma.dingtalk.Env;
+import com.izouma.dingtalk.OApiException;
+import com.izouma.dingtalk.utils.HttpHelper;
+import com.alibaba.fastjson.JSONObject;
+
+import java.util.List;
+
+/**
+ * 通讯录回调相关事件
+ * <p>
+ * https://open-doc.dingtalk.com/docs/doc.htm?treeId=371&articleId=104975&docType=1
+ */
+public class eventChangeHelper {
+
+    /**
+     * 注册事件回调接口
+     */
+    public static String registerEventChange(String accessToken, List<String> callBackTag, String token, String aesKey, String url) throws OApiException {
+        String signUpUrl = Env.OAPI_HOST + "/call_back/register_call_back?" +
+                "access_token=" + accessToken;
+        JSONObject args = new JSONObject();
+        args.put("call_back_tag", callBackTag);
+        args.put("token", token);
+        args.put("aes_key", aesKey);
+        args.put("url", url);
+
+        JSONObject response = HttpHelper.httpPost(signUpUrl, args);
+        if (response.containsKey("errcode")) {
+            return response.getString("errcode");
+        } else {
+            return null;
+        }
+    }
+
+    //查询事件回调接口
+    public static String getEventChange(String accessToken) throws OApiException {
+        String url = Env.OAPI_HOST + "/call_back/get_call_back?" +
+                "access_token=" + accessToken;
+        JSONObject response = HttpHelper.httpGet(url);
+        return response.toString();
+    }
+
+    //更新事件回调接口
+    public static String updateEventChange(String accessToken, List<String> callBackTag, String token, String aesKey, String url) throws OApiException {
+        String signUpUrl = Env.OAPI_HOST + "/call_back/update_call_back?" +
+                "access_token=" + accessToken;
+        JSONObject args = new JSONObject();
+        args.put("call_back_tag", callBackTag);
+        args.put("token", token);
+        args.put("aes_key", aesKey);
+        args.put("url", url);
+
+        JSONObject response = HttpHelper.httpPost(signUpUrl, args);
+        if (response.containsKey("errcode")) {
+            return response.getString("errcode");
+        } else {
+            return null;
+        }
+    }
+
+    //删除事件回调接口
+    public static String deleteEventChange(String accessToken) throws OApiException {
+        String url = Env.OAPI_HOST + "/call_back/delete_call_back?" +
+                "access_token=" + accessToken;
+        JSONObject response = HttpHelper.httpGet(url);
+        return response.toString();
+    }
+
+
+    public static String getFailedResult(String accessToken) throws OApiException {
+        String url = Env.OAPI_HOST + "/call_back/get_call_back_failed_result?" +
+                "access_token=" + accessToken;
+        JSONObject response = HttpHelper.httpGet(url);
+        return response.toString();
+    }
+
+
+}

+ 53 - 0
src/main/java/com/izouma/dingtalk/media/MediaHelper.java

@@ -0,0 +1,53 @@
+package com.izouma.dingtalk.media;
+
+import com.alibaba.fastjson.JSONObject;
+import com.dingtalk.open.client.ServiceFactory;
+import com.dingtalk.open.client.api.model.corp.UploadResult;
+import com.dingtalk.open.client.api.service.corp.MediaService;
+import com.izouma.dingtalk.Env;
+import com.izouma.dingtalk.utils.HttpHelper;
+
+import java.io.File;
+
+/**
+ * 管理多媒体文件
+ * https://open-doc.dingtalk.com/docs/doc.htm?source=search&treeId=373&articleId=104971&docType=1
+ */
+public class MediaHelper {
+
+    /**
+     * 资源文件类型
+     */
+    public static final String TYPE_IMAGE = "image";
+    public static final String TYPE_VOICE = "voice";
+    public static final String TYPE_VIDEO = "video";
+    public static final String TYPE_FILE = "file";
+
+
+    public static class MediaUploadResult {
+        public String type;
+        public String media_id;
+        public String created_at;
+    }
+
+    /**
+     * 上传多媒体文件
+     * <p>
+     */
+    public static UploadResult upload(String accessToken, String type, File file) throws Exception {
+
+        MediaService mediaService = ServiceFactory.getInstance().getOpenService(MediaService.class);
+        UploadResult uploadResult = mediaService.uploadMediaFile(accessToken, type, file);
+        return uploadResult;
+    }
+
+    /**
+     * 下载多媒体文件,目前sdk没有封装此接口,需要通过HTTP访问
+     */
+    public static void download(String accessToken, String mediaId, String fileDir) throws Exception {
+        String url = Env.OAPI_HOST + "/media/downloadFile?" +
+                "access_token=" + accessToken + "&media_id=" + mediaId;
+        JSONObject response = HttpHelper.downloadMedia(url, fileDir);
+        System.out.println(response);
+    }
+}

+ 16 - 0
src/main/java/com/izouma/dingtalk/message/ConversationMessageDelivery.java

@@ -0,0 +1,16 @@
+package com.izouma.dingtalk.message;
+
+public class ConversationMessageDelivery extends MessageDelivery {
+
+	public String sender;
+	public String cid;
+	public String agentid;
+	
+	public ConversationMessageDelivery(String sender, String cid, 
+			String agentId) {
+		this.sender = sender;
+		this.cid = cid;
+		this.agentid = agentId;
+	}
+		
+}

+ 17 - 0
src/main/java/com/izouma/dingtalk/message/ImageMessage.java

@@ -0,0 +1,17 @@
+package com.izouma.dingtalk.message;
+
+
+public class ImageMessage extends Message {
+	
+	public String media_id;
+	
+	public ImageMessage(String mediaId) {
+		super();
+		media_id = mediaId;
+	}
+	
+	@Override
+	public String type() {
+		return "image";
+	}
+}

+ 15 - 0
src/main/java/com/izouma/dingtalk/message/LightAppMessageDelivery.java

@@ -0,0 +1,15 @@
+package com.izouma.dingtalk.message;
+
+public class LightAppMessageDelivery extends MessageDelivery {
+
+	public String touser;
+	public String toparty;
+	public String agentid;
+	
+	public LightAppMessageDelivery(String toUsers, String toParties, String agentId) {
+		this.touser = toUsers;
+		this.toparty = toParties;
+		this.agentid = agentId;
+	}
+	
+}

+ 22 - 0
src/main/java/com/izouma/dingtalk/message/LinkMessage.java

@@ -0,0 +1,22 @@
+package com.izouma.dingtalk.message;
+
+public class LinkMessage extends Message {
+	
+	public String messageUrl;
+    public String picUrl;
+    public String title;
+    public String text;
+	
+	public LinkMessage(String messageUrl, String picUrl, String title, String text) {
+		super();
+		this.messageUrl = messageUrl;
+		this.picUrl = picUrl;
+		this.title = title;
+		this.text = text;
+	}
+	
+	@Override
+	public String type() {
+		return "link";
+	}
+}

+ 6 - 0
src/main/java/com/izouma/dingtalk/message/Message.java

@@ -0,0 +1,6 @@
+package com.izouma.dingtalk.message;
+
+
+public abstract class Message {
+	public abstract String type();
+}

+ 15 - 0
src/main/java/com/izouma/dingtalk/message/MessageDelivery.java

@@ -0,0 +1,15 @@
+package com.izouma.dingtalk.message;
+
+import com.dingtalk.open.client.api.model.corp.MessageBody;
+
+public class MessageDelivery {
+	
+	public String msgType;
+	public MessageBody message;
+	
+	public MessageDelivery withMessage(String msgType, MessageBody msg) {
+		this.msgType = msgType;
+		this.message = msg;
+		return this;
+	}
+}

+ 43 - 0
src/main/java/com/izouma/dingtalk/message/MessageHelper.java

@@ -0,0 +1,43 @@
+package com.izouma.dingtalk.message;
+
+import com.dingtalk.open.client.ServiceFactory;
+import com.dingtalk.open.client.api.model.corp.MessageSendResult;
+import com.dingtalk.open.client.api.service.corp.MessageService;
+
+/**
+ * 发送消息
+ */
+public class MessageHelper {
+
+    public static class Receipt {
+        String invaliduser;
+        String invalidparty;
+    }
+
+    /**
+     * 发送普通消息
+     *
+     * @param accessToken
+     * @param delivery
+     * @return
+     * @throws Exception
+     */
+    public static Receipt send(String accessToken, LightAppMessageDelivery delivery)
+            throws Exception {
+        MessageService messageService = ServiceFactory.getInstance().getOpenService(MessageService.class);
+        MessageSendResult reulst = messageService.sendToCorpConversation(accessToken, delivery.touser,
+                delivery.toparty, delivery.agentid, delivery.msgType, delivery.message);
+        Receipt receipt = new Receipt();
+        receipt.invaliduser = reulst.getInvaliduser();
+        receipt.invalidparty = reulst.getInvalidparty();
+        return receipt;
+    }
+
+
+    public static String send(String accessToken, ConversationMessageDelivery delivery)
+            throws Exception {
+        MessageService messageService = ServiceFactory.getInstance().getOpenService(MessageService.class);
+        return messageService.sendToNormalConversation(accessToken, delivery.sender,
+                delivery.cid, delivery.msgType, delivery.message);
+    }
+}

+ 69 - 0
src/main/java/com/izouma/dingtalk/message/OAMessage.java

@@ -0,0 +1,69 @@
+package com.izouma.dingtalk.message;
+
+import java.util.List;
+
+/** 
+  { 
+    "message_url": "http://dingtalk.com", 
+    "head": {
+        "bgcolor": "FFCC0000"
+    }, 
+    "body": {
+        "title": "标题", 
+        "form": [
+            {
+                "key": "姓名", 
+                "value": "张三"
+            }, 
+            {
+                "key": "年龄", 
+                "value": "30"
+            }
+        ], 
+        "rich": {
+            "num": "15.6", 
+            "unit": "元"
+        }, 
+        "content": "大段文本", 
+        "image": "@lADOAAGXIszazQKA", 
+        "file_count": "3", 
+        "author": "李四"
+    }
+ */
+public class OAMessage extends Message {
+	
+	public String message_url;
+	public Head head;
+	public Body body;
+	
+
+	@Override
+	public String type() {
+		return "oa";
+	}
+	
+	//content
+	public static class Head {
+		public String bgcolor;
+	}
+	
+	public static class Body {
+		public String title;
+		public List<Form> form;
+		public Rich rich;
+		public String content;
+		public String image;
+		public String file_found;
+		public String author;
+		
+		public static class Form {
+			public String key;
+			public String value;
+		}
+		
+		public static class Rich {
+			public String num;
+			public String unit;
+		}
+	}
+}

+ 17 - 0
src/main/java/com/izouma/dingtalk/message/TextMessage.java

@@ -0,0 +1,17 @@
+package com.izouma.dingtalk.message;
+
+
+public class TextMessage extends Message {
+	
+	public String content;	
+	
+	public TextMessage(String content) {
+		super();
+		this.content = content;
+	}
+	
+	@Override
+	public String type() {
+		return "text";
+	}
+}

+ 43 - 0
src/main/java/com/izouma/dingtalk/user/User.java

@@ -0,0 +1,43 @@
+package com.izouma.dingtalk.user;
+
+import com.alibaba.fastjson.JSONObject;
+
+import java.util.List;
+
+public class User {
+	public String userid;
+	public String name;
+	public boolean active;
+	public String avatar;
+	public List<Long> department;
+	public String position;
+	public String mobile;
+	public String tel;
+	public String workPlace;
+	public String remark;
+	public String email;
+	public String jobnumber;
+	public JSONObject extattr;
+	public boolean isAdmin;
+	public boolean isBoss;
+	public String dingId;
+
+
+	
+	public User() {
+	}
+	
+	public User(String userid, String name) {
+		this.userid = userid;
+		this.name = name;
+	}
+	
+	@Override
+	public String toString() {
+		List<User> users;
+		return "User[userid:" + userid + ", name:" + name + ", active:" + active + ", "
+				+ "avatar:" + avatar + ", department:" + department +
+				", position:" + position + ", mobile:" + mobile + ", email:" + email + 
+				", extattr:" + extattr;
+	}
+}

+ 143 - 0
src/main/java/com/izouma/dingtalk/user/UserHelper.java

@@ -0,0 +1,143 @@
+package com.izouma.dingtalk.user;
+
+import com.alibaba.fastjson.JSONObject;
+import com.dingtalk.open.client.ServiceFactory;
+import com.dingtalk.open.client.api.model.corp.CorpUserBaseInfo;
+import com.dingtalk.open.client.api.model.corp.CorpUserDetail;
+import com.dingtalk.open.client.api.model.corp.CorpUserDetailList;
+import com.dingtalk.open.client.api.model.corp.CorpUserList;
+import com.dingtalk.open.client.api.service.corp.CorpUserService;
+import com.izouma.dingtalk.Env;
+import com.izouma.dingtalk.OApiException;
+import com.izouma.dingtalk.utils.FileUtils;
+import com.izouma.dingtalk.utils.HttpHelper;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 通讯录成员相关的接口调用
+ */
+public class UserHelper {
+
+
+    /**
+     * 根据免登授权码查询免登用户userId
+     *
+     * @param accessToken
+     * @param code
+     * @return
+     * @throws Exception
+     */
+    public static CorpUserBaseInfo getUserInfo(String accessToken, String code) throws Exception {
+        CorpUserService corpUserService = ServiceFactory.getInstance().getOpenService(CorpUserService.class);
+        return corpUserService.getUserinfo(accessToken, code);
+    }
+
+    /**
+     * 创建企业成员
+     * <p>
+     * https://open-doc.dingtalk.com/docs/doc.htm?treeId=385&articleId=106816&docType=1#s1
+     */
+    public static String createUser(String accessToken, CorpUserDetail userDetail) throws Exception {
+        CorpUserService corpUserService = ServiceFactory.getInstance().getOpenService(CorpUserService.class);
+        JSONObject js = (JSONObject) JSONObject.parse(userDetail.getOrderInDepts());
+        Map<Long, Long> orderInDepts = FileUtils.toHashMap(js);
+
+        String userId = corpUserService.createCorpUser(accessToken, userDetail.getUserid(), userDetail.getName(), orderInDepts,
+                userDetail.getDepartment(), userDetail.getPosition(), userDetail.getMobile(), userDetail.getTel(), userDetail.getWorkPlace(),
+                userDetail.getRemark(), userDetail.getEmail(), userDetail.getJobnumber(),
+                userDetail.getIsHide(), userDetail.getSenior(), userDetail.getExtattr());
+
+        // 员工唯一标识ID
+        return userId;
+    }
+
+
+    /**
+     * 更新成员
+     * <p>
+     * https://open-doc.dingtalk.com/docs/doc.htm?treeId=385&articleId=106816&docType=1#s2
+     */
+    public static void updateUser(String accessToken, CorpUserDetail userDetail) throws Exception {
+        CorpUserService corpUserService = ServiceFactory.getInstance().getOpenService(CorpUserService.class);
+        JSONObject js = (JSONObject) JSONObject.parse(userDetail.getOrderInDepts());
+        Map<Long, Long> orderInDepts = FileUtils.toHashMap(js);
+
+        corpUserService.updateCorpUser(accessToken, userDetail.getUserid(), userDetail.getName(), orderInDepts,
+                userDetail.getDepartment(), userDetail.getPosition(), userDetail.getMobile(), userDetail.getTel(), userDetail.getWorkPlace(),
+                userDetail.getRemark(), userDetail.getEmail(), userDetail.getJobnumber(),
+                userDetail.getIsHide(), userDetail.getSenior(), userDetail.getExtattr());
+    }
+
+
+    /**
+     * 删除成员
+     */
+    public static void deleteUser(String accessToken, String userid) throws Exception {
+        CorpUserService corpUserService = ServiceFactory.getInstance().getOpenService(CorpUserService.class);
+        corpUserService.deleteCorpUser(accessToken, userid);
+    }
+
+
+    //获取成员
+    public static CorpUserDetail getUser(String accessToken, String userid) throws Exception {
+
+        CorpUserService corpUserService = ServiceFactory.getInstance().getOpenService(CorpUserService.class);
+        return corpUserService.getCorpUser(accessToken, userid);
+    }
+
+    //批量删除成员
+    public static void batchDeleteUser(String accessToken, List<String> useridlist)
+            throws Exception {
+        CorpUserService corpUserService = ServiceFactory.getInstance().getOpenService(CorpUserService.class);
+        corpUserService.batchdeleteCorpUserListByUserids(accessToken, useridlist);
+
+    }
+
+
+    //获取部门成员
+    public static CorpUserList getDepartmentUser(
+            String accessToken,
+            long departmentId,
+            Long offset,
+            Integer size,
+            String order)
+            throws Exception {
+
+        CorpUserService corpUserService = ServiceFactory.getInstance().getOpenService(CorpUserService.class);
+        return corpUserService.getCorpUserSimpleList(accessToken, departmentId,
+                offset, size, order);
+    }
+
+
+    //获取部门成员(详情)
+    public static CorpUserDetailList getUserDetails(
+            String accessToken,
+            long departmentId,
+            Long offset,
+            Integer size,
+            String order)
+            throws Exception {
+
+        CorpUserService corpUserService = ServiceFactory.getInstance().getOpenService(CorpUserService.class);
+        return corpUserService.getCorpUserList(accessToken, departmentId,
+                offset, size, order);
+    }
+
+
+    /**
+     * 管理后台免登时通过CODE换取微应用管理员的身份信息
+     *
+     * @param ssoToken
+     * @param code
+     * @return
+     * @throws OApiException
+     */
+    public static JSONObject getAgentUserInfo(String ssoToken, String code) throws OApiException {
+        String url = Env.OAPI_HOST + "/sso/getuserinfo?" + "access_token=" + ssoToken + "&code=" + code;
+        JSONObject response = HttpHelper.httpGet(url);
+        return response;
+    }
+
+}

+ 135 - 0
src/main/java/com/izouma/dingtalk/utils/FileUtils.java

@@ -0,0 +1,135 @@
+package com.izouma.dingtalk.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.http.util.TextUtils;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 模拟accessToken和jsTicket的数据持久化<br/>
+ * <p>
+ * 正式项目最好是写入到Mysql
+ */
+public class FileUtils {
+
+    public static final String FILEPATH = "Permanent_Data";
+
+    // json写入文件
+    public synchronized static void write2File(Object json, String fileName) {
+        BufferedWriter writer = null;
+        File filePath = new File(FILEPATH);
+        JSONObject eJSON = null;
+
+        if (!filePath.exists() && !filePath.isDirectory()) {
+            filePath.mkdirs();
+        }
+
+        File file = new File(FILEPATH + File.separator + fileName + ".xml");
+        System.out.println("path:" + file.getPath() + " abs path:" + file.getAbsolutePath());
+        if (!file.exists()) {
+            try {
+                file.createNewFile();
+            } catch (Exception e) {
+                System.out.println("createNewFile,出现异常:");
+                e.printStackTrace();
+            }
+        } else {
+            eJSON = (JSONObject) read2JSON(fileName);
+        }
+
+        try {
+            writer = new BufferedWriter(new FileWriter(file));
+
+            if (eJSON == null) {
+                writer.write(json.toString());
+            } else {
+                Object[] array = ((JSONObject) json).keySet().toArray();
+                for (int i = 0; i < array.length; i++) {
+                    eJSON.put(array[i].toString(), ((JSONObject) json).get(array[i].toString()));
+                }
+
+                writer.write(eJSON.toString());
+            }
+
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (writer != null) {
+                    writer.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+    }
+
+    // 读文件到json
+    public static JSONObject read2JSON(String fileName) {
+        File file = new File(FILEPATH + File.separator + fileName + ".xml");
+        if (!file.exists()) {
+            return null;
+        }
+
+        BufferedReader reader = null;
+        String laststr = "";
+        try {
+            reader = new BufferedReader(new FileReader(file));
+            String tempString = null;
+            while ((tempString = reader.readLine()) != null) {
+                laststr += tempString;
+            }
+            reader.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        return (JSONObject) JSON.parse(laststr);
+    }
+
+    // 通过key值获取文件中的value
+    public static Object getValue(String fileName, String key) {
+        JSONObject eJSON = null;
+        eJSON = (JSONObject) read2JSON(fileName);
+        if (null != eJSON && eJSON.containsKey(key)) {
+            @SuppressWarnings("unchecked")
+            Map<String, Object> values = JSON.parseObject(eJSON.toString(), Map.class);
+            return values.get(key);
+        } else {
+            return null;
+        }
+    }
+
+    public static HashMap<Long, Long> toHashMap(JSONObject js) {
+        if (js == null) {
+            return null;
+        }
+        HashMap<Long, Long> data = new HashMap<Long, Long>();
+        // 将json字符串转换成jsonObject
+        Set<String> set = js.keySet();
+        // 遍历jsonObject数据,添加到Map对象
+        Iterator<String> it = set.iterator();
+        while (it.hasNext()) {
+            String key = String.valueOf(it.next());
+            Long keyLong = Long.valueOf(key);
+
+            String value = js.getString(key);
+            Long valueLong;
+            if (TextUtils.isEmpty(value)) {
+                valueLong = js.getLong(key);
+            } else {
+                valueLong = Long.valueOf(value);
+            }
+            data.put(keyLong, valueLong);
+        }
+        return data;
+    }
+
+
+}

+ 251 - 0
src/main/java/com/izouma/dingtalk/utils/HttpHelper.java

@@ -0,0 +1,251 @@
+package com.izouma.dingtalk.utils;
+
+import com.izouma.dingtalk.OApiException;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.commons.io.FileUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.entity.mime.content.FileBody;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.client.RedirectLocations;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.EntityUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+
+/**
+ * HTTP请求封装,建议直接使用sdk的API
+ */
+public class HttpHelper {
+
+	public static JSONObject httpGet(String url) throws OApiException{
+        HttpGet httpGet = new HttpGet(url);
+        CloseableHttpResponse response = null;
+        CloseableHttpClient httpClient = HttpClients.createDefault();
+        RequestConfig requestConfig = RequestConfig.custom().
+        		setSocketTimeout(2000).setConnectTimeout(2000).build();
+        httpGet.setConfig(requestConfig);
+
+        try {
+            response = httpClient.execute(httpGet, new BasicHttpContext());
+
+            if (response.getStatusLine().getStatusCode() != 200) {
+
+                System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode()
+                                   + ", url=" + url);
+                return null;
+            }
+            HttpEntity entity = response.getEntity();
+            if (entity != null) {
+                String resultStr = EntityUtils.toString(entity, "utf-8");
+
+                JSONObject result = JSON.parseObject(resultStr);
+                if (result.getInteger("errcode") == 0) {
+                    return result;
+                } else {
+                    System.out.println("request url=" + url + ",return value=");
+                    System.out.println(resultStr);
+                    int errCode = result.getInteger("errcode");
+                    String errMsg = result.getString("errmsg");
+                    throw new OApiException(errCode, errMsg);
+                }
+            }
+        } catch (IOException e) {
+            System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());
+            e.printStackTrace();
+        } finally {
+            if (response != null) try {
+                response.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return null;
+    }
+	
+	
+	public static JSONObject httpPost(String url, Object data) throws OApiException {
+        HttpPost httpPost = new HttpPost(url);
+        CloseableHttpResponse response = null;
+        CloseableHttpClient httpClient = HttpClients.createDefault();
+        RequestConfig requestConfig = RequestConfig.custom().
+        		setSocketTimeout(2000).setConnectTimeout(2000).build();
+        httpPost.setConfig(requestConfig);
+        httpPost.addHeader("Content-Type", "application/json");
+
+        try {
+        	StringEntity requestEntity = new StringEntity(JSON.toJSONString(data), "utf-8");
+            httpPost.setEntity(requestEntity);
+            
+            response = httpClient.execute(httpPost, new BasicHttpContext());
+
+            if (response.getStatusLine().getStatusCode() != 200) {
+
+                System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode()
+                                   + ", url=" + url);
+                return null;
+            }
+            HttpEntity entity = response.getEntity();
+            if (entity != null) {
+                String resultStr = EntityUtils.toString(entity, "utf-8");
+
+                JSONObject result = JSON.parseObject(resultStr);
+                if (result.getInteger("errcode") == 0) {
+                	result.remove("errcode");
+                	result.remove("errmsg");
+                    return result;
+                } else {
+                    System.out.println("request url=" + url + ",return value=");
+                    System.out.println(resultStr);
+                    int errCode = result.getInteger("errcode");
+                    String errMsg = result.getString("errmsg");
+                    throw new OApiException(errCode, errMsg);
+                }
+            }
+        } catch (IOException e) {
+            System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());
+            e.printStackTrace();
+        } finally {
+            if (response != null) try {
+                response.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return null;
+    }
+	
+	
+	public static JSONObject uploadMedia(String url, File file) throws OApiException {
+        HttpPost httpPost = new HttpPost(url);
+        CloseableHttpResponse response = null;
+        CloseableHttpClient httpClient = HttpClients.createDefault();
+        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(2000).setConnectTimeout(2000).build();
+        httpPost.setConfig(requestConfig);
+
+        HttpEntity requestEntity = MultipartEntityBuilder.create().addPart("media",
+        		new FileBody(file, ContentType.APPLICATION_OCTET_STREAM, file.getName())).build();
+        httpPost.setEntity(requestEntity);
+
+        try {
+            response = httpClient.execute(httpPost, new BasicHttpContext());
+
+            if (response.getStatusLine().getStatusCode() != 200) {
+
+                System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode()
+                                   + ", url=" + url);
+                return null;
+            }
+            HttpEntity entity = response.getEntity();
+            if (entity != null) {
+                String resultStr = EntityUtils.toString(entity, "utf-8");
+
+                JSONObject result = JSON.parseObject(resultStr);
+                if (result.getInteger("errcode") == 0) {
+                    // 成功
+                	result.remove("errcode");
+                	result.remove("errmsg");
+                    return result;
+                } else {
+                    System.out.println("request url=" + url + ",return value=");
+                    System.out.println(resultStr);
+                    int errCode = result.getInteger("errcode");
+                    String errMsg = result.getString("errmsg");
+                    throw new OApiException(errCode, errMsg);
+                }
+            }
+        } catch (IOException e) {
+            System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());
+            e.printStackTrace();
+        } finally {
+            if (response != null) try {
+                response.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return null;
+    }
+	
+	
+	public static JSONObject downloadMedia(String url, String fileDir) throws OApiException {
+        HttpGet httpGet = new HttpGet(url);
+        CloseableHttpResponse response = null;
+        CloseableHttpClient httpClient = HttpClients.createDefault();
+        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(2000).setConnectTimeout(2000).build();
+        httpGet.setConfig(requestConfig);
+
+        try {
+            HttpContext localContext = new BasicHttpContext();
+
+            response = httpClient.execute(httpGet, localContext);
+
+            RedirectLocations locations = (RedirectLocations) localContext.getAttribute(HttpClientContext.REDIRECT_LOCATIONS);
+            if (locations != null) {
+                URI downloadUrl = locations.getAll().get(0);
+                String filename = downloadUrl.toURL().getFile();
+                System.out.println("downloadUrl=" + downloadUrl);
+                File downloadFile = new File(fileDir + File.separator + filename);
+                FileUtils.writeByteArrayToFile(downloadFile, EntityUtils.toByteArray(response.getEntity()));
+                JSONObject obj = new JSONObject();
+                obj.put("downloadFilePath", downloadFile.getAbsolutePath());
+                obj.put("httpcode", response.getStatusLine().getStatusCode());
+                
+               
+                
+                return obj;
+            } else {
+                if (response.getStatusLine().getStatusCode() != 200) {
+
+                    System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode()
+                                       + ", url=" + url);
+                    return null;
+                }
+                HttpEntity entity = response.getEntity();
+                if (entity != null) {
+                    String resultStr = EntityUtils.toString(entity, "utf-8");
+
+                    JSONObject result = JSON.parseObject(resultStr);
+                    if (result.getInteger("errcode") == 0) {
+                        // 成功
+                    	result.remove("errcode");
+                    	result.remove("errmsg");
+                        return result;
+                    } else {
+                        System.out.println("request url=" + url + ",return value=");
+                        System.out.println(resultStr);
+                        int errCode = result.getInteger("errcode");
+                        String errMsg = result.getString("errmsg");
+                        throw new OApiException(errCode, errMsg);
+                    }
+                }
+            }
+        } catch (IOException e) {
+            System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());
+            e.printStackTrace();
+        } finally {
+            if (response != null) try {
+                response.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return null;
+    }
+}

+ 53 - 0
src/main/java/com/izouma/dingtalk/utils/aes/DingTalkEncryptException.java

@@ -0,0 +1,53 @@
+package com.izouma.dingtalk.utils.aes;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 钉钉开放平台加解密异常类
+ */
+public class DingTalkEncryptException extends Exception {
+    /**成功**/
+    public static final int SUCCESS = 0;
+    /**加密明文文本非法**/
+    public final static int  ENCRYPTION_PLAINTEXT_ILLEGAL = 900001;
+    /**加密时间戳参数非法**/
+    public final static int  ENCRYPTION_TIMESTAMP_ILLEGAL = 900002;
+    /**加密随机字符串参数非法**/
+    public final static int  ENCRYPTION_NONCE_ILLEGAL = 900003;
+    /**不合法的aeskey**/
+    public final static int AES_KEY_ILLEGAL = 900004;
+    /**签名不匹配**/
+    public final static int SIGNATURE_NOT_MATCH = 900005;
+    /**计算签名错误**/
+    public final static int COMPUTE_SIGNATURE_ERROR = 900006;
+    /**计算加密文字错误**/
+    public final static int COMPUTE_ENCRYPT_TEXT_ERROR  = 900007;
+    /**计算解密文字错误**/
+    public final static int COMPUTE_DECRYPT_TEXT_ERROR  = 900008;
+    /**计算解密文字长度不匹配**/
+    public final static int COMPUTE_DECRYPT_TEXT_LENGTH_ERROR  = 900009;
+    /**计算解密文字corpid不匹配**/
+    public final static int COMPUTE_DECRYPT_TEXT_CORPID_ERROR  = 900010;
+
+    private static Map<Integer,String> msgMap = new HashMap<Integer,String>();
+    static{
+        msgMap.put(SUCCESS,"成功");
+        msgMap.put(ENCRYPTION_PLAINTEXT_ILLEGAL,"加密明文文本非法");
+        msgMap.put(ENCRYPTION_TIMESTAMP_ILLEGAL,"加密时间戳参数非法");
+        msgMap.put(ENCRYPTION_NONCE_ILLEGAL,"加密随机字符串参数非法");
+        msgMap.put(SIGNATURE_NOT_MATCH,"签名不匹配");
+        msgMap.put(COMPUTE_SIGNATURE_ERROR,"签名计算失败");
+        msgMap.put(AES_KEY_ILLEGAL,"不合法的aes key");
+        msgMap.put(COMPUTE_ENCRYPT_TEXT_ERROR,"计算加密文字错误");
+        msgMap.put(COMPUTE_DECRYPT_TEXT_ERROR,"计算解密文字错误");
+        msgMap.put(COMPUTE_DECRYPT_TEXT_LENGTH_ERROR,"计算解密文字长度不匹配");
+        msgMap.put(COMPUTE_DECRYPT_TEXT_CORPID_ERROR,"计算解密文字corpid或者suiteKey不匹配");
+    }
+
+    public Integer  code;
+    public DingTalkEncryptException(Integer exceptionCode){
+        super(msgMap.get(exceptionCode));
+        this.code = exceptionCode;
+    }
+}

+ 211 - 0
src/main/java/com/izouma/dingtalk/utils/aes/DingTalkEncryptor.java

@@ -0,0 +1,211 @@
+package com.izouma.dingtalk.utils.aes;
+
+import org.apache.commons.codec.binary.Base64;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * 钉钉开放平台加解密方法
+ * 在ORACLE官方网站下载JCE无限制权限策略文件
+ *     JDK6的下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html
+ *     JDK7的下载地址: http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
+ */
+public class DingTalkEncryptor {
+
+    private static final Charset CHARSET = Charset.forName("utf-8");
+    private static final Base64 base64  = new Base64();
+    private byte[]         aesKey;
+    private String         token;
+    private String         corpId;
+    /**ask getPaddingBytes key固定长度**/
+    private static final Integer AES_ENCODE_KEY_LENGTH = 43;
+    /**加密随机字符串字节长度**/
+    private static final Integer RANDOM_LENGTH = 16;
+
+    /**
+     * 构造函数
+     * @param token             钉钉开放平台上,开发者设置的token
+     * @param encodingAesKey  钉钉开放台上,开发者设置的EncodingAESKey
+     * @param corpId           ISV进行配置的时候应该传对应套件的SUITE_KEY,普通企业是Corpid
+     * @throws DingTalkEncryptException 执行失败,请查看该异常的错误码和具体的错误信息
+     */
+    public DingTalkEncryptor(String token, String encodingAesKey, String corpId) throws DingTalkEncryptException{
+        if (null==encodingAesKey ||  encodingAesKey.length() != AES_ENCODE_KEY_LENGTH) {
+            throw new DingTalkEncryptException(DingTalkEncryptException.AES_KEY_ILLEGAL);
+        }
+        this.token = token;
+        this.corpId = corpId;
+        aesKey = Base64.decodeBase64(encodingAesKey + "=");
+    }
+
+    /**
+     * 将和钉钉开放平台同步的消息体加密,返回加密Map
+     * @param plaintext     传递的消息体明文
+     * @param timeStamp      时间戳
+     * @param nonce           随机字符串
+     * @return
+     * @throws DingTalkEncryptException
+     */
+    public Map<String,String> getEncryptedMap(String plaintext, Long timeStamp, String nonce) throws DingTalkEncryptException {
+        if(null==plaintext){
+            throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_PLAINTEXT_ILLEGAL);
+        }
+        if(null==timeStamp){
+            throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_TIMESTAMP_ILLEGAL);
+        }
+        if(null==nonce){
+            throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_NONCE_ILLEGAL);
+        }
+        // 加密
+        String encrypt = encrypt(Utils.getRandomStr(RANDOM_LENGTH), plaintext);
+        String signature = getSignature(token, String.valueOf(timeStamp), nonce, encrypt);
+        Map<String,String> resultMap = new HashMap<String, String>();
+        resultMap.put("msg_signature", signature);
+        resultMap.put("encrypt", encrypt);
+        resultMap.put("timeStamp", String.valueOf(timeStamp));
+        resultMap.put("nonce", nonce);
+        return  resultMap;
+    }
+
+    /**
+     * 密文解密
+     * @param msgSignature     签名串
+     * @param timeStamp        时间戳
+     * @param nonce             随机串
+     * @param encryptMsg       密文
+     * @return                  解密后的原文
+     * @throws DingTalkEncryptException
+     */
+    public String getDecryptMsg(String msgSignature, String timeStamp, String nonce, String encryptMsg)throws DingTalkEncryptException {
+        //校验签名
+        String signature = getSignature(token, timeStamp, nonce, encryptMsg);
+        if (!signature.equals(msgSignature)) {
+            throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_SIGNATURE_ERROR);
+        }
+        // 解密
+        String result = decrypt(encryptMsg);
+        return result;
+    }
+
+
+    /*
+     * 对明文加密.
+     * @param text 需要加密的明文
+     * @return 加密后base64编码的字符串
+     */
+    private String encrypt(String random, String plaintext) throws DingTalkEncryptException {
+        try {
+            byte[] randomBytes = random.getBytes(CHARSET);
+            byte[] plainTextBytes = plaintext.getBytes(CHARSET);
+            byte[] lengthByte = Utils.int2Bytes(plainTextBytes.length);
+            byte[] corpidBytes = corpId.getBytes(CHARSET);
+            ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+            byteStream.write(randomBytes);
+            byteStream.write(lengthByte);
+            byteStream.write(plainTextBytes);
+            byteStream.write(corpidBytes);
+            byte[] padBytes = PKCS7Padding.getPaddingBytes(byteStream.size());
+            byteStream.write(padBytes);
+            byte[] unencrypted = byteStream.toByteArray();
+            byteStream.close();
+            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
+            SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
+            IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
+            cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
+            byte[] encrypted = cipher.doFinal(unencrypted);
+            String result = base64.encodeToString(encrypted);
+            return result;
+        } catch (Exception e) {
+            throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_ENCRYPT_TEXT_ERROR);
+        }
+    }
+
+    /*
+     * 对密文进行解密.
+     * @param text 需要解密的密文
+     * @return 解密得到的明文
+     */
+    private String decrypt(String text) throws DingTalkEncryptException {
+        byte[] originalArr;
+        try {
+            // 设置解密模式为AES的CBC模式
+            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
+            SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
+            IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
+            cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
+            // 使用BASE64对密文进行解码
+            byte[] encrypted = Base64.decodeBase64(text);
+            // 解密
+            originalArr = cipher.doFinal(encrypted);
+        } catch (Exception e) {
+            throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_ERROR);
+        }
+
+        String plainText;
+        String fromCorpid;
+        try {
+            // 去除补位字符
+            byte[] bytes = PKCS7Padding.removePaddingBytes(originalArr);
+            // 分离16位随机字符串,网络字节序和corpId
+            byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);
+            int plainTextLegth = Utils.bytes2int(networkOrder);
+            plainText = new String(Arrays.copyOfRange(bytes, 20, 20 + plainTextLegth), CHARSET);
+            fromCorpid = new String(Arrays.copyOfRange(bytes, 20 + plainTextLegth, bytes.length), CHARSET);
+        } catch (Exception e) {
+            throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_LENGTH_ERROR);
+        }
+
+        // corpid不相同的情况
+        if (!fromCorpid.equals(corpId)) {
+            throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_CORPID_ERROR);
+        }
+        return plainText;
+    }
+
+    /**
+     * 数字签名
+     * @param token         isv token
+     * @param timestamp     时间戳
+     * @param nonce          随机串
+     * @param encrypt       加密文本
+     * @return
+     * @throws DingTalkEncryptException
+     */
+    public String getSignature(String token, String timestamp, String nonce, String encrypt) throws DingTalkEncryptException {
+        try {
+            String[] array = new String[] { token, timestamp, nonce, encrypt };
+            Arrays.sort(array);
+            StringBuffer sb = new StringBuffer();
+            for (int i = 0; i < 4; i++) {
+                sb.append(array[i]);
+            }
+            String str = sb.toString();
+            MessageDigest md = MessageDigest.getInstance("SHA-1");
+            md.update(str.getBytes());
+            byte[] digest = md.digest();
+
+            StringBuffer hexstr = new StringBuffer();
+            String shaHex = "";
+            for (int i = 0; i < digest.length; i++) {
+                shaHex = Integer.toHexString(digest[i] & 0xFF);
+                if (shaHex.length() < 2) {
+                    hexstr.append(0);
+                }
+                hexstr.append(shaHex);
+            }
+            return hexstr.toString();
+        } catch (Exception e) {
+            throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_SIGNATURE_ERROR);
+        }
+    }
+
+}

+ 52 - 0
src/main/java/com/izouma/dingtalk/utils/aes/DingTalkJsApiSingnature.java

@@ -0,0 +1,52 @@
+package com.izouma.dingtalk.utils.aes;
+
+import java.security.MessageDigest;
+import java.util.Formatter;
+
+/**
+ * 钉钉jsapi签名工具类
+ */
+public class DingTalkJsApiSingnature {
+    /**
+     * 获取jsapi签名
+     * @param url
+     * @param nonce
+     * @param timeStamp
+     * @param jsTicket
+     * @return
+     * @throws DingTalkEncryptException
+     */
+    public static String getJsApiSingnature(String url,String nonce,Long timeStamp,String jsTicket) throws DingTalkEncryptException{
+        String plainTex = "jsapi_ticket=" + jsTicket +"&noncestr=" + nonce +"&timestamp=" + timeStamp + "&url=" + url;
+        System.out.println(plainTex);
+        String signature = "";
+        try{
+            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
+            crypt.reset();
+            crypt.update(plainTex.getBytes("UTF-8"));
+            signature = byteToHex(crypt.digest());
+            return signature;
+        }catch (Exception e){
+            throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_SIGNATURE_ERROR);
+        }
+    }
+
+    private static String byteToHex(final byte[] hash) {
+        Formatter formatter = new Formatter();
+        for (byte b : hash){
+            formatter.format("%02x", b);
+        }
+        String result = formatter.toString();
+        formatter.close();
+        return result;
+    }
+
+
+    public static void main(String args[]) throws Exception{
+        String url="http://10.62.53.138:3000/jsapi";
+        String nonce="abcdefgh";
+        Long timeStamp = 1437027269927L;
+        String tikcet="zHoQdGJuH0ZDebwo7sLqLzHGUueLmkWCC4RycYgkuvDu3eoROgN5qhwnQLgfzwEXtuR9SDzh6BdhyVngzAjrxV";
+        System.err.println(getJsApiSingnature(url,nonce,timeStamp,tikcet));
+    }
+}

+ 50 - 0
src/main/java/com/izouma/dingtalk/utils/aes/PKCS7Padding.java

@@ -0,0 +1,50 @@
+package com.izouma.dingtalk.utils.aes;
+
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+/*
+ * PKCS7算法的加密填充
+ */
+
+public class PKCS7Padding {
+    private final static Charset CHARSET    = Charset.forName("utf-8");
+    private final static int     BLOCK_SIZE = 32;
+
+    /**
+     * 填充mode字节
+     * @param count
+     * @return
+     */
+    public static byte[] getPaddingBytes(int count) {
+        int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
+        if (amountToPad == 0) {
+            amountToPad = BLOCK_SIZE;
+        }
+        char padChr = chr(amountToPad);
+        String tmp = new String();
+        for (int index = 0; index < amountToPad; index++) {
+            tmp += padChr;
+        }
+        return tmp.getBytes(CHARSET);
+    }
+
+    /**
+     * 移除mode填充字节
+     * @param decrypted
+     * @return
+     */
+    public static byte[] removePaddingBytes(byte[] decrypted) {
+        int pad = (int) decrypted[decrypted.length - 1];
+        if (pad < 1 || pad > BLOCK_SIZE) {
+            pad = 0;
+        }
+        return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
+    }
+
+    private static char chr(int a) {
+        byte target = (byte) (a & 0xFF);
+        return (char) target;
+    }
+
+}

+ 53 - 0
src/main/java/com/izouma/dingtalk/utils/aes/Utils.java

@@ -0,0 +1,53 @@
+package com.izouma.dingtalk.utils.aes;
+
+import java.util.Random;
+
+/**
+ * 加解密工具类
+ */
+public class Utils {
+    
+    /**
+     * 获取随机字符串
+     *
+     * @return
+     */
+    public static String getRandomStr(int count) {
+        String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+        Random random = new Random();
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < count; i++) {
+            int number = random.nextInt(base.length());
+            sb.append(base.charAt(number));
+        }
+        return sb.toString();
+    }
+
+
+    /*
+     * int转byte数组,高位在前
+     */
+    public static byte[] int2Bytes(int count) {
+        byte[] byteArr = new byte[4];
+        byteArr[3] = (byte) (count & 0xFF);
+        byteArr[2] = (byte) (count >> 8 & 0xFF);
+        byteArr[1] = (byte) (count >> 16 & 0xFF);
+        byteArr[0] = (byte) (count >> 24 & 0xFF);
+        return byteArr;
+    }
+
+    /**
+     * 高位在前bytes数组转int
+     *
+     * @param byteArr
+     * @return
+     */
+    public static int bytes2int(byte[] byteArr) {
+        int count = 0;
+        for (int i = 0; i < 4; i++) {
+            count <<= 8;
+            count |= byteArr[i] & 0xff;
+        }
+        return count;
+    }
+}

+ 90 - 0
src/main/java/com/izouma/zhumj/web/DingtalkController.java

@@ -0,0 +1,90 @@
+package com.izouma.zhumj.web;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.dingtalk.open.client.api.model.corp.CorpUserDetail;
+import com.dingtalk.open.client.api.model.corp.CorpUserDetailList;
+import com.dingtalk.open.client.api.model.corp.CorpUserList;
+import com.dingtalk.open.client.api.model.corp.Department;
+import com.izouma.dingtalk.auth.AuthHelper;
+import com.izouma.dingtalk.department.DepartmentHelper;
+import com.izouma.dingtalk.user.UserHelper;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+
+@Slf4j
+@AllArgsConstructor
+@RestController
+@RequestMapping("/dingtalk")
+public class DingtalkController {
+
+    @GetMapping("/synchronous ")
+    public void synchronous() throws Exception {
+
+
+        //获取部门列表
+        List<Department> departments = DepartmentHelper.listDepartments(AuthHelper.getAccessToken(), "1");
+
+        for (int i = 0; i < departments.size(); i++) {
+
+            //TODO 保存更新部门信息
+
+
+            List<CorpUserDetail> userDetailList = getAllUserByDepartmentId(Long.valueOf(departments.get(i).getId()));
+
+            for (CorpUserDetail corpUserDetail : userDetailList) {
+                //TODO 保存更新用户信息
+            }
+
+        }
+
+    }
+
+    private List<CorpUserDetail> getAllUserByDepartmentId(Long departmentId) throws Exception {
+        List<CorpUserDetail> userDetailList = new ArrayList<>();
+
+        long offset = 0;
+        int size = 5;
+        CorpUserDetailList corpUserDetailList;
+        while (true) {
+            corpUserDetailList = UserHelper.getUserDetails(AuthHelper.getAccessToken(), departmentId
+                    , offset, size, null);
+
+            userDetailList.addAll(corpUserDetailList.getUserlist());
+            if (Boolean.TRUE.equals(corpUserDetailList.isHasMore())) {
+                offset += size;
+            } else {
+                break;
+            }
+        }
+
+        return userDetailList;
+    }
+
+
+    @GetMapping("/departments")
+    public List departments() throws Exception {
+
+        List<Department> departments = DepartmentHelper.listDepartments(AuthHelper.getAccessToken(), "1");
+        return departments;
+    }
+
+    @GetMapping("/userDetailList")
+    public List userDetailList(@RequestParam Long departmentId) throws Exception {
+
+        List<CorpUserDetail> userDetailList = getAllUserByDepartmentId(departmentId);
+
+        return userDetailList;
+    }
+
+
+}

+ 29 - 0
src/main/resources/logback-spring.xml

@@ -19,6 +19,35 @@
         <!--swagger 类型转换异常日志去除-->
         <logger name="io.swagger.models.parameters.AbstractSerializableParameter" level="ERROR"/>
         <logger name="org.freemarker" level="DEBUG"/>
+
+        <!-- dingding log -->
+        <appender name="HTTP_INVOKE_LOGGER_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
+            <param name="append" value="true"/>
+            <param name="encoding" value="UTF-8"/>
+            <param name="threshold" value="INFO"/>
+            <encoder>
+                <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+            </encoder>
+        </appender>
+
+        <!-- ding open sdk log -->
+        <appender name="DING_OPEN_CLIENT_SDK_LOGGER_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
+            <param name="append" value="true"/>
+            <param name="encoding" value="UTF-8"/>
+            <param name="threshold" value="INFO"/>
+            <encoder>
+                <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+            </encoder>
+        </appender>
+        <logger name="HTTP_INVOKE_LOGGER" additivity="false">
+            <level value="WARN"/>
+            <appender-ref ref="HTTP_INVOKE_LOGGER_APPENDER"/>
+        </logger>
+        <logger name="DING_OPEN_CLIENT_SDK_LOGGER" additivity="false">
+            <level value="WARN"/>
+            <appender-ref ref="DING_OPEN_CLIENT_SDK_LOGGER_APPENDER"/>
+        </logger>
+        <!-- end of dingding log -->
     </springProfile>
 
     <springProfile name="test">

+ 33 - 0
src/test/java/com/izouma/zhumj/DingDingTest.java

@@ -0,0 +1,33 @@
+package com.izouma.zhumj;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.izouma.zhumj.dto.gen.GenCode;
+import com.izouma.zhumj.utils.FileUtils;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.util.ResourceUtils;
+
+import java.io.*;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class DingDingTest {
+
+    static final String corpId="ding1c1369684efa555b35c2f4657eb6378f";
+    static final String corpsecret="Y5H-jC7Rf4MVZuIWq-O_jvUlJphAKebgNByZrrwuAMwglM0fVvZDFa994-WxHfna";
+
+
+
+}