├── .gitignore ├── README.md ├── lib ├── client-sdk.api-1.0.2.jar ├── client-sdk.common-1.0.0-SNAPSHOT.jar ├── client-sdk.core-1.0.0-SNAPSHOT.jar ├── client-sdk.example-1.0.0-SNAPSHOT.jar ├── client-sdk.spring-1.0.0-SNAPSHOT.jar ├── lippi-oapi-encrpt.jar ├── taobao-sdk-java-auto_1479188381469-20190325-source.jar └── taobao-sdk-java-auto_1479188381469-20190325.jar ├── pom.xml └── src └── main ├── java └── com │ └── alibaba │ └── dingtalk │ └── openapi │ ├── demo │ ├── Demo.java │ ├── Env.java │ ├── OApiException.java │ ├── auth │ │ └── AuthHelper.java │ ├── department │ │ └── DepartmentHelper.java │ ├── eventchange │ │ └── eventChangeHelper.java │ ├── media │ │ └── MediaHelper.java │ ├── message │ │ ├── ConversationMessageDelivery.java │ │ ├── ImageMessage.java │ │ ├── LightAppMessageDelivery.java │ │ ├── LinkMessage.java │ │ ├── Message.java │ │ ├── MessageDelivery.java │ │ ├── MessageHelper.java │ │ ├── OAMessage.java │ │ └── TextMessage.java │ ├── user │ │ ├── User.java │ │ └── UserHelper.java │ └── utils │ │ ├── FileUtils.java │ │ ├── HttpHelper.java │ │ └── aes │ │ ├── DingTalkEncryptException.java │ │ ├── DingTalkEncryptor.java │ │ ├── DingTalkJsApiSingnature.java │ │ ├── PKCS7Padding.java │ │ └── Utils.java │ └── servlet │ ├── CallbackServlet.java │ ├── ContactsServlet.java │ ├── EventChangeReceiveServlet.java │ ├── OAoauth.java │ ├── UserInfoServlet.java │ └── WelcomeServlet.java └── webapp ├── META-INF └── MANIFEST.MF ├── README.md ├── WEB-INF └── web.xml ├── contacts.jsp ├── drawer ├── css │ └── base.css ├── drawer.html ├── index.html └── js │ ├── base.js │ ├── index.js │ └── support.js ├── index.jsp ├── indexPC.jsp ├── javascripts ├── contacts.js ├── demo.js ├── demoPC.js ├── logger.js └── zepto.min.js ├── list ├── 1.jpg ├── 2.jpg ├── 3.jpg ├── detail.html ├── detail2.html ├── list.html ├── num1.png ├── num11.png ├── num2.png ├── num3.png ├── num33.png ├── num4.png ├── num5.png ├── num6.png ├── num7.png ├── num8.png └── num9.png ├── nav ├── 1.html ├── 2.html ├── 3.html ├── 4.html ├── 5.html ├── 6.html ├── default.png ├── javascripts │ └── base.js ├── log.html └── stylesheets │ └── base.css ├── pic ├── comp.png └── isv.png ├── preIndex.jsp ├── stylesheets └── style.css └── tab ├── css └── base.css ├── index.html ├── js ├── base.js ├── dingtalk.js └── index.js ├── tab1.html ├── tab2.html └── tab3.html /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .classpath 3 | .project 4 | .idea/ 5 | .git/ 6 | target/ 7 | .settings/ 8 | */*.iml 9 | */*.iml 10 | */*.log 11 | 12 | api/*.iml 13 | api/.classpath 14 | api/.project 15 | api/.idea/ 16 | api/.git/ 17 | api/target/ 18 | api/.settings/ 19 | 20 | Permanent_Data/* 21 | 22 | 23 | biz/*.iml 24 | biz/.classpath 25 | biz/.project 26 | biz/.idea/ 27 | biz/.git/ 28 | biz/target/ 29 | biz/.settings/ 30 | 31 | 32 | web/*.iml 33 | web/.classpath 34 | web/.project 35 | web/.idea/ 36 | web/.git/ 37 | web/target/ 38 | web/.settings/ 39 | 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 软件依赖 2 | * java version "1.7" 3 | * maven3 4 | 5 | ## Getting Started 6 | 7 | 1. 将工程clone到本地:`git clone https://github.com/open-dingtalk/openapi-demo-java.git` 8 | 2. 使用IDE导入工程,比如eclipse点击`File->import`(推荐使用maven导入), IDEA点击`File->New->Project from Existing Sources...`, 文件编码都是UTF-8 9 | 3. 打开工程的Env.java文件,填入企业的CORP_ID,APP_KEY,APP_SECRET。参数获取请参考[名词解释](https://ding-doc.dingtalk.com/doc#/faquestions/dcubhu) 10 | ``` 11 | public static final String CORP_ID = "your CORP_ID"; 12 | public static final String APP_KEY = "your APP_KEY"; 13 | public static final String APP_SECRET = "your SECRET"; 14 | ``` 15 | 4. 部署工程,建议使用mvn -DskipTests=true jetty:run运行或者IDE中的maven插件运行 16 | 5. OA后台创建微应用,并把工程的首页地址/index.jsp填到微应用**首页地址**中。 17 | [如何创建微应用?](https://ding-doc.dingtalk.com/doc#/bgb96b/aw3h75) 18 | 19 | ## DEMO具体实现 20 | 21 | #### 1. jsapi权限验证配置流程 22 | 23 | 请查看[文档](https://ding-doc.dingtalk.com/doc#/dev/uwa7vs) 24 | - 前端文件:WebContent/index.jsp,WebContent/javascripts/demo.js 25 | - 后端文件 26 | 27 | #### 2.免登流程 28 | 请查看[文档](https://ding-doc.dingtalk.com/doc#/dev/ep7bpq) 29 | - 前端文件:WebContent/javascripts/demo.js 30 | - 后端文件:[链接](https://github.com/open-dingtalk/openapi-demo-java/blob/master/src/main/java/com/alibaba/dingtalk/openapi/servlet/UserInfoServlet.java) 31 | 32 | #### 3.企业内部应用-服务端API-通讯录管理-部门管理 33 | 请查看[文档](https://ding-doc.dingtalk.com/doc#/serverapi2/dubakq) 34 | - 后端文件:[链接](https://github.com/open-dingtalk/openapi-demo-java/tree/master/src/main/java/com/alibaba/dingtalk/openapi/demo/department) 35 | 36 | #### 4.企业内部应用-服务端API-通讯录管理-用户管理 37 | 请查看[文档](https://ding-doc.dingtalk.com/doc#/serverapi2/ege851) 38 | - 后端文件:[链接](https://github.com/open-dingtalk/openapi-demo-java/tree/master/src/main/java/com/alibaba/dingtalk/openapi/demo/user) 39 | -------------------------------------------------------------------------------- /lib/client-sdk.api-1.0.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/lib/client-sdk.api-1.0.2.jar -------------------------------------------------------------------------------- /lib/client-sdk.common-1.0.0-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/lib/client-sdk.common-1.0.0-SNAPSHOT.jar -------------------------------------------------------------------------------- /lib/client-sdk.core-1.0.0-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/lib/client-sdk.core-1.0.0-SNAPSHOT.jar -------------------------------------------------------------------------------- /lib/client-sdk.example-1.0.0-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/lib/client-sdk.example-1.0.0-SNAPSHOT.jar -------------------------------------------------------------------------------- /lib/client-sdk.spring-1.0.0-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/lib/client-sdk.spring-1.0.0-SNAPSHOT.jar -------------------------------------------------------------------------------- /lib/lippi-oapi-encrpt.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/lib/lippi-oapi-encrpt.jar -------------------------------------------------------------------------------- /lib/taobao-sdk-java-auto_1479188381469-20190325-source.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/lib/taobao-sdk-java-auto_1479188381469-20190325-source.jar -------------------------------------------------------------------------------- /lib/taobao-sdk-java-auto_1479188381469-20190325.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/lib/taobao-sdk-java-auto_1479188381469-20190325.jar -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | com.dingtalk.open 7 | dingtalk-app-demo 8 | war 9 | 1.0-SNAPSHOT 10 | 11 | UTF-8 12 | 3.2.8.RELEASE 13 | 14 | 15 | 16 | com.laiwang.lippi 17 | lippi.oapi.encryt 18 | 1.0.3-SNAPSHOT 19 | system 20 | ${project.basedir}/lib/lippi-oapi-encrpt.jar 21 | 22 | 23 | com.dingtalk.open 24 | client-sdk.api 25 | 1.0.2 26 | system 27 | ${project.basedir}/lib/client-sdk.api-1.0.2.jar 28 | 29 | 30 | com.dingtalk.open 31 | client-sdk.common 32 | 1.0.0-SNAPSHOT 33 | system 34 | ${project.basedir}/lib/client-sdk.common-1.0.0-SNAPSHOT.jar 35 | 36 | 37 | com.dingtalk.open 38 | client-sdk.core 39 | 1.0.0-SNAPSHOT 40 | system 41 | ${project.basedir}/lib/client-sdk.core-1.0.0-SNAPSHOT.jar 42 | 43 | 44 | com.dingtalk.open 45 | client-sdk.spring 46 | 1.0.0-SNAPSHOT 47 | system 48 | ${project.basedir}/lib/client-sdk.spring-1.0.0-SNAPSHOT.jar 49 | 50 | 51 | com.dingtalk.open 52 | taobao-sdk-java 53 | 1.0.0-SNAPSHOT 54 | system 55 | ${project.basedir}/lib/taobao-sdk-java-auto_1479188381469-20190325.jar 56 | 57 | 58 | commons-io 59 | commons-io 60 | 2.4 61 | 62 | 63 | org.apache.httpcomponents 64 | 4.3 65 | httpclient 66 | 67 | 68 | org.apache.httpcomponents 69 | 4.3 70 | httpmime 71 | 72 | 73 | org.apache.httpcomponents 74 | 4.3 75 | httpcore 76 | 77 | 78 | 79 | 80 | org.springframework 81 | spring-core 82 | ${spring.version} 83 | 84 | 85 | org.springframework 86 | spring-web 87 | ${spring.version} 88 | 89 | 90 | org.springframework 91 | spring-orm 92 | ${spring.version} 93 | 94 | 95 | org.springframework 96 | spring-context 97 | ${spring.version} 98 | 99 | 100 | org.springframework 101 | spring-aop 102 | ${spring.version} 103 | 104 | 105 | org.springframework 106 | spring-expression 107 | ${spring.version} 108 | 109 | 110 | org.springframework 111 | spring-tx 112 | ${spring.version} 113 | 114 | 115 | org.springframework 116 | spring-webmvc 117 | ${spring.version} 118 | 119 | 120 | org.springframework 121 | spring-context-support 122 | ${spring.version} 123 | 124 | 125 | org.springframework 126 | spring-jdbc 127 | ${spring.version} 128 | 129 | 130 | org.springframework 131 | spring-test 132 | ${spring.version} 133 | test 134 | 135 | 136 | javax.servlet 137 | servlet-api 138 | 2.5 139 | 140 | 141 | javax.servlet.jsp 142 | javax.servlet.jsp-api 143 | 2.3.1 144 | 145 | 146 | com.ning 147 | async-http-client 148 | 1.9.32 149 | 150 | 151 | com.alibaba 152 | fastjson 153 | 1.2.37 154 | 155 | 156 | org.apache.commons 157 | commons-lang3 158 | 3.0.1 159 | 160 | 161 | org.mybatis 162 | mybatis 163 | 3.3.0 164 | 165 | 166 | org.mybatis 167 | mybatis-spring 168 | 1.2.3 169 | 170 | 171 | org.slf4j 172 | slf4j-api 173 | 1.7.6 174 | 175 | 176 | org.slf4j 177 | slf4j-log4j12 178 | 1.7.6 179 | 180 | 181 | junit 182 | junit 183 | 4.11 184 | 185 | 186 | 187 | 188 | ding-app-demo 189 | 190 | 191 | maven-compiler-plugin 192 | 193 | 1.7 194 | 1.7 195 | UTF-8 196 | 197 | 198 | 199 | maven-assembly-plugin 200 | 201 | 202 | web/src/main/assembly/assembly.xml 203 | 204 | 205 | 206 | 207 | org.apache.maven.plugins 208 | maven-source-plugin 209 | 210 | 211 | attach-sources 212 | 213 | jar 214 | 215 | 216 | 217 | 218 | 219 | org.apache.maven.plugins 220 | maven-deploy-plugin 221 | 222 | true 223 | 224 | 225 | 226 | org.apache.maven.plugins 227 | maven-war-plugin 228 | 2.3 229 | 230 | 231 | 232 | lib 233 | WEB-INF/lib 234 | 235 | **/*.jar 236 | 237 | 238 | 239 | 240 | 241 | 242 | org.eclipse.jetty 243 | jetty-maven-plugin 244 | 9.0.7.v20131107 245 | 246 | 247 | 248 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/Demo.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo; 2 | 3 | import com.alibaba.dingtalk.openapi.demo.auth.AuthHelper; 4 | import com.alibaba.dingtalk.openapi.demo.department.DepartmentHelper; 5 | import com.alibaba.dingtalk.openapi.demo.media.MediaHelper; 6 | import com.alibaba.dingtalk.openapi.demo.message.LightAppMessageDelivery; 7 | import com.alibaba.dingtalk.openapi.demo.message.MessageHelper; 8 | import com.alibaba.dingtalk.openapi.demo.user.UserHelper; 9 | import com.alibaba.fastjson.JSON; 10 | import com.alibaba.fastjson.JSONObject; 11 | import com.dingtalk.open.client.api.model.corp.*; 12 | import com.dingtalk.open.client.api.model.corp.MessageBody.OABody.Body; 13 | import com.dingtalk.open.client.api.model.corp.MessageBody.OABody.Body.Form; 14 | import com.dingtalk.open.client.api.model.corp.MessageBody.OABody.Body.Rich; 15 | import com.dingtalk.open.client.api.model.corp.MessageBody.OABody.Head; 16 | 17 | import java.io.File; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | /** 22 | * 本地测试方法钉钉API 23 | */ 24 | public class Demo { 25 | 26 | public static String TO_USER = ""; 27 | public static String TO_PARTY = ""; 28 | public static String AGENT_ID = ""; 29 | 30 | public static void main(String[] args) throws Exception { 31 | 32 | try { 33 | 34 | List departments = new ArrayList(); 35 | departments = DepartmentHelper.listDepartments(AuthHelper.getAccessToken(), "1"); 36 | JSONObject usersJSON = new JSONObject(); 37 | 38 | System.out.println("depart num:" + departments.size()); 39 | for (int i = 0; i < departments.size(); i++) { 40 | JSONObject userDepJSON = new JSONObject(); 41 | System.out.println("dep:" + departments.get(i).toString()); 42 | 43 | long offset = 0; 44 | int size = 5; 45 | CorpUserList corpUserList = new CorpUserList(); 46 | while (true) { 47 | corpUserList = UserHelper.getDepartmentUser(AuthHelper.getAccessToken(), Long.valueOf(departments.get(i).getId()) 48 | , offset, size, null); 49 | System.out.println(JSON.toJSONString(corpUserList)); 50 | if (Boolean.TRUE.equals(corpUserList.isHasMore())) { 51 | offset += size; 52 | } else { 53 | break; 54 | } 55 | } 56 | if (corpUserList.getUserlist().size() == 0) { 57 | continue; 58 | } 59 | for (int j = 0; j < corpUserList.getUserlist().size(); j++) { 60 | String user = JSON.toJSONString(corpUserList.getUserlist().get(j)); 61 | userDepJSON.put(j + "", JSONObject.parseObject(user, CorpUserDetail.class)); 62 | 63 | } 64 | 65 | 66 | usersJSON.put(departments.get(i).getName(), userDepJSON); 67 | System.out.println("user:" + usersJSON.toString()); 68 | } 69 | 70 | System.out.println("depart:" + usersJSON.toJSONString()); 71 | 72 | 73 | // 获取access token 74 | String accessToken = AuthHelper.getAccessToken(); 75 | log("成功获取access token: ", accessToken); 76 | 77 | // 获取jsapi ticket 78 | String ticket = AuthHelper.getJsapiTicket(accessToken); 79 | log("成功获取jsapi ticket: ", ticket); 80 | 81 | // 获取签名 82 | String nonceStr = "nonceStr"; 83 | long timeStamp = System.currentTimeMillis(); 84 | String url = "http://www.dingtalk.com"; 85 | String signature = AuthHelper.sign(ticket, nonceStr, timeStamp, url); 86 | log("成功签名: ", signature); 87 | 88 | // 创建部门 89 | String name = "TestDept.34"; 90 | String parentId = "1"; 91 | String order = "1"; 92 | boolean createDeptGroup = true; 93 | long departmentId = Long.parseLong(DepartmentHelper.createDepartment(accessToken, name, parentId, order, createDeptGroup)); 94 | log("成功创建部门", name, " 部门id=", departmentId); 95 | 96 | // 获取部门列表 97 | List list = DepartmentHelper.listDepartments(accessToken, parentId); 98 | log("成功获取部门列表", list); 99 | 100 | // 更新部门 101 | name = "hahahaha"; 102 | boolean autoAddUser = true; 103 | String deptManagerUseridList = null; 104 | boolean deptHiding = false; 105 | String deptPerimits = "aa|qq"; 106 | DepartmentHelper.updateDepartment(accessToken, departmentId, name, parentId, order, createDeptGroup, 107 | autoAddUser, deptManagerUseridList, deptHiding, deptPerimits, null, 108 | null, null, null, null); 109 | 110 | 111 | log("成功更新部门", " 部门id=", departmentId); 112 | 113 | CorpUserDetail userDetail = new CorpUserDetail(); 114 | userDetail.setUserid("id_yuhuan"); 115 | userDetail.setName("name_yuhuan"); 116 | userDetail.setEmail("yuhuan@abc.com"); 117 | userDetail.setMobile("18612124567"); 118 | userDetail.setDepartment(new ArrayList()); 119 | userDetail.getDepartment().add(departmentId); 120 | 121 | 122 | UserHelper.createUser(accessToken, userDetail); 123 | log("成功创建成员", "成员信息=", userDetail); 124 | 125 | // 上传图片 126 | File file = new File("/Users/ian/Downloads/lALOAVYgbc0DIM0Bwg_450_800.png"); 127 | UploadResult uploadResult = MediaHelper.upload(accessToken, MediaHelper.TYPE_IMAGE, file); 128 | log("成功上传图片", uploadResult); 129 | 130 | // 下载图片 131 | String fileDir = "/Users/ian/Desktop/"; 132 | MediaHelper.download(accessToken, uploadResult.getMedia_id(), fileDir); 133 | log("成功下载图片"); 134 | 135 | 136 | MessageBody.TextBody textBody = new MessageBody.TextBody(); 137 | textBody.setContent("TextMessage"); 138 | 139 | MessageBody.ImageBody imageBody = new MessageBody.ImageBody(); 140 | imageBody.setMedia_id(uploadResult.getMedia_id()); 141 | 142 | MessageBody.LinkBody linkBody = new MessageBody.LinkBody(); 143 | linkBody.setMessageUrl("http://www.baidu.com"); 144 | linkBody.setPicUrl("@lALOACZwe2Rk"); 145 | linkBody.setTitle("Link Message"); 146 | linkBody.setText("This is a link message"); 147 | 148 | // 创建oa消息 149 | MessageBody.OABody oaBody = new MessageBody.OABody(); 150 | oaBody.setMessage_url("message_url"); 151 | Head head = new Head(); 152 | head.setText("head.text"); 153 | head.setBgcolor("FFBBBBBB"); 154 | oaBody.setHead(head); 155 | 156 | Body body = new Body(); 157 | body.setAuthor("author"); 158 | body.setContent("content"); 159 | body.setFile_count("file_count"); 160 | body.setImage("@image"); 161 | body.setTitle("body.title"); 162 | Rich rich = new Rich(); 163 | rich.setNum("num"); 164 | rich.setUnit("unit"); 165 | body.setRich(rich); 166 | List
formList = new ArrayList(); 167 | Form form = new Form(); 168 | form.setKey("key"); 169 | form.setValue("value"); 170 | formList.add(form); 171 | body.setForm(formList); 172 | oaBody.setBody(body); 173 | 174 | // 发送微应用消息 175 | String toUsers = TO_USER; 176 | String toParties = TO_PARTY; 177 | String agentId = AGENT_ID; 178 | LightAppMessageDelivery lightAppMessageDelivery = new LightAppMessageDelivery(toUsers, toParties, agentId); 179 | 180 | lightAppMessageDelivery.withMessage(MessageType.TEXT, textBody); 181 | MessageHelper.send(accessToken, lightAppMessageDelivery); 182 | log("成功发送 微应用文本消息"); 183 | lightAppMessageDelivery.withMessage(MessageType.IMAGE, imageBody); 184 | MessageHelper.send(accessToken, lightAppMessageDelivery); 185 | log("成功发送 微应用图片消息"); 186 | lightAppMessageDelivery.withMessage(MessageType.LINK, linkBody); 187 | MessageHelper.send(accessToken, lightAppMessageDelivery); 188 | log("成功发送 微应用link消息"); 189 | lightAppMessageDelivery.withMessage(MessageType.OA, oaBody); 190 | MessageHelper.send(accessToken, lightAppMessageDelivery); 191 | log("成功发送 微应用oa消息"); 192 | 193 | // 发送会话消息 194 | // String sender = Vars.SENDER; 195 | // String cid = Vars.CID;//cid需要通过jsapi获取,具体详情请查看开放平台文档--->客户端文档--->会话 196 | 197 | // ConversationMessageDelivery conversationMessageDelivery = new ConversationMessageDelivery(sender, cid, 198 | // agentId); 199 | // 200 | // conversationMessageDelivery.withMessage(MessageType.TEXT, textBody); 201 | // MessageHelper.send(accessToken, conversationMessageDelivery); 202 | // log("成功发送 会话文本消息"); 203 | // conversationMessageDelivery.withMessage(MessageType.IMAGE, imageBody); 204 | // MessageHelper.send(accessToken, conversationMessageDelivery); 205 | // log("成功发送 会话图片消息"); 206 | // conversationMessageDelivery.withMessage(MessageType.LINK, linkBody); 207 | // MessageHelper.send(accessToken, conversationMessageDelivery); 208 | // log("成功发送 会话link消息"); 209 | 210 | // 更新成员 211 | userDetail.setMobile("11177776666"); 212 | UserHelper.updateUser(accessToken, userDetail); 213 | log("成功更新成员", "成员信息=", userDetail); 214 | 215 | // 获取成员 216 | CorpUserDetail userDetail11 = UserHelper.getUser(accessToken, userDetail.getUserid()); 217 | log("成功获取成员", "成员userid=", userDetail11.getUserid()); 218 | 219 | // 获取部门成员 220 | CorpUserList userList = UserHelper.getDepartmentUser(accessToken, departmentId, null, null, null); 221 | log("成功获取部门成员", "部门成员user=", userList.getUserlist()); 222 | 223 | // 获取部门成员(详情) 224 | CorpUserDetailList userList2 = UserHelper.getUserDetails(accessToken, departmentId, null, null, null); 225 | log("成功获取部门成员详情", "部门成员详情user=", userList2.getUserlist()); 226 | 227 | // 批量删除成员 228 | // User user2 = new User("id_yuhuan2", "name_yuhuan2"); 229 | // user2.email = "yuhua2n@abc.com"; 230 | // user2.mobile = "18611111111"; 231 | // user2.department = new ArrayList(); 232 | // user2.department.add(departmentId); 233 | // UserHelper.createUser(accessToken, user2); 234 | 235 | CorpUserDetail userDetail2 = new CorpUserDetail(); 236 | userDetail2.setUserid("id_yuhuan2"); 237 | userDetail2.setName("name_yuhuan2"); 238 | userDetail2.setEmail("yuhua2n@abc.com"); 239 | userDetail2.setMobile("18612124926"); 240 | userDetail2.setDepartment(new ArrayList()); 241 | userDetail2.getDepartment().add(departmentId); 242 | UserHelper.createUser(accessToken, userDetail2); 243 | 244 | 245 | List useridlist = new ArrayList(); 246 | useridlist.add(userDetail.getUserid()); 247 | useridlist.add(userDetail2.getUserid()); 248 | UserHelper.batchDeleteUser(accessToken, useridlist); 249 | log("成功批量删除成员", "成员列表useridlist=", useridlist); 250 | 251 | // 删除成员 252 | // User user3 = new User("id_yuhuan3", "name_yuhuan3"); 253 | // user3.email = "yuhua2n@abc.com"; 254 | // user3.mobile = "18611111111"; 255 | // user3.department = new ArrayList(); 256 | // user3.department.add(departmentId); 257 | CorpUserDetail userDetail3 = new CorpUserDetail(); 258 | userDetail3.setUserid("id_yuhuan3"); 259 | userDetail3.setName("name_yuhuan3"); 260 | userDetail3.setMobile("13146654734"); 261 | userDetail3.setDepartment(new ArrayList()); 262 | userDetail3.getDepartment().add(departmentId); 263 | 264 | 265 | UserHelper.createUser(accessToken, userDetail3); 266 | UserHelper.deleteUser(accessToken, userDetail3.getUserid()); 267 | log("成功删除成员", "成员userid=", userDetail3.getUserid()); 268 | 269 | // 删除部门 270 | DepartmentHelper.deleteDepartment(accessToken, departmentId); 271 | log("成功删除部门", " 部门id=", departmentId); 272 | 273 | } catch (OApiException e) { 274 | e.printStackTrace(); 275 | } 276 | } 277 | 278 | private static void log(Object... msgs) { 279 | StringBuilder sb = new StringBuilder(); 280 | for (Object o : msgs) { 281 | if (o != null) { 282 | sb.append(o.toString()); 283 | } 284 | } 285 | System.out.println(sb.toString()); 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/Env.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo; 2 | 3 | 4 | /** 5 | * 企业应用接入时的常量定义 6 | */ 7 | public class Env { 8 | 9 | /** 10 | * 企业corpid 11 | */ 12 | public static final String CORP_ID = ""; 13 | 14 | /** 15 | * 应用agentId 16 | */ 17 | public static final String AGENT_ID = ""; 18 | 19 | /** 20 | * 应用的appkey 21 | */ 22 | public static final String APP_KEY = ""; 23 | 24 | /** 25 | * 应用的appsecret 26 | */ 27 | public static final String APP_SECRET = ""; 28 | 29 | /** 30 | * 回调host 31 | */ 32 | public static final String CALLBACK_URL_HOST = ""; 33 | 34 | /** 35 | * 加解密需要用到的token,企业可以随机填写。如 "123456" 36 | */ 37 | public static final String TOKEN = "123456"; 38 | 39 | /** 40 | * 数据加密密钥。用于回调数据的加密,长度固定为43个字符,从a-z, A-Z, 0-9共62个字符中选取,您可以随机生成 41 | */ 42 | public static final String ENCODING_AES_KEY = "abcdefghijabcdefghijabcdefghijabcdefghij123"; 43 | 44 | /** 45 | * DING API地址 46 | */ 47 | public static final String OAPI_HOST = "https://oapi.dingtalk.com"; 48 | 49 | /** 50 | * 删除企业回调接口url 51 | */ 52 | public static final String DELETE_CALLBACK = "https://oapi.dingtalk.com/call_back/delete_call_back"; 53 | 54 | /** 55 | * 注册企业回调接口url 56 | */ 57 | public static final String REGISTER_CALLBACK = "https://oapi.dingtalk.com/call_back/register_call_back"; 58 | 59 | /** 60 | * 企业应用后台地址,用户管理后台免登使用 61 | */ 62 | public static final String OA_BACKGROUND_URL = ""; 63 | 64 | public static final String SSO_Secret = ""; 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/OApiException.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo; 2 | 3 | public class OApiException extends Exception { 4 | 5 | public static final int ERR_RESULT_RESOLUTION = -2; 6 | 7 | public OApiException(String field) { 8 | this(ERR_RESULT_RESOLUTION, "Cannot resolve field " + field + " from oapi resonpse"); 9 | } 10 | 11 | public OApiException(int errCode, String errMsg) { 12 | super("error code: " + errCode + ", error message: " + errMsg); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/auth/AuthHelper.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.auth; 2 | 3 | import com.alibaba.dingtalk.openapi.demo.Env; 4 | import com.alibaba.dingtalk.openapi.demo.OApiException; 5 | import com.alibaba.dingtalk.openapi.demo.utils.FileUtils; 6 | import com.alibaba.dingtalk.openapi.demo.utils.HttpHelper; 7 | import com.alibaba.fastjson.JSON; 8 | import com.alibaba.fastjson.JSONObject; 9 | import com.dingtalk.oapi.lib.aes.DingTalkJsApiSingnature; 10 | import com.dingtalk.open.client.ServiceFactory; 11 | import com.dingtalk.open.client.api.model.corp.JsapiTicket; 12 | import com.dingtalk.open.client.api.service.corp.CorpConnectionService; 13 | import com.dingtalk.open.client.api.service.corp.JsapiService; 14 | 15 | import javax.servlet.http.HttpServletRequest; 16 | import java.net.URLDecoder; 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | 20 | /** 21 | * AccessToken和jsticket的获取封装 22 | */ 23 | public class AuthHelper { 24 | 25 | /** 26 | * 调整到1小时50分钟 27 | */ 28 | public static final long cacheTime = 1000 * 60 * 55 * 2; 29 | 30 | /** 31 | * 在此方法中,为了避免频繁获取access_token, 32 | * 在距离上一次获取access_token时间在两个小时之内的情况, 33 | * 将直接从持久化存储中读取access_token 34 | * 35 | * 因为access_token和jsapi_ticket的过期时间都是7200秒 36 | * 所以在获取access_token的同时也去获取了jsapi_ticket 37 | * 注:jsapi_ticket是在前端页面JSAPI做权限验证配置的时候需要使用的 38 | * 具体信息请查看开发者文档--权限验证配置 39 | */ 40 | public static String getAccessToken() throws OApiException { 41 | long curTime = System.currentTimeMillis(); 42 | JSONObject accessTokenValue = (JSONObject) FileUtils.getValue("accesstoken", Env.APP_KEY); 43 | String accToken = ""; 44 | JSONObject jsontemp = new JSONObject(); 45 | if (accessTokenValue == null || curTime - accessTokenValue.getLong("begin_time") >= cacheTime) { 46 | try { 47 | ServiceFactory serviceFactory = ServiceFactory.getInstance(); 48 | CorpConnectionService corpConnectionService = serviceFactory.getOpenService(CorpConnectionService.class); 49 | accToken = corpConnectionService.getCorpToken(Env.APP_KEY, Env.APP_SECRET); 50 | // save accessToken 51 | JSONObject jsonAccess = new JSONObject(); 52 | jsontemp.clear(); 53 | jsontemp.put("access_token", accToken); 54 | jsontemp.put("begin_time", curTime); 55 | jsonAccess.put(Env.APP_KEY, jsontemp); 56 | //真实项目中最好保存到数据库中 57 | FileUtils.write2File(jsonAccess, "accesstoken"); 58 | 59 | } catch (Exception e) { 60 | e.printStackTrace(); 61 | } 62 | } else { 63 | return accessTokenValue.getString("access_token"); 64 | } 65 | return accToken; 66 | } 67 | 68 | /** 69 | * 获取JSTicket, 用于js的签名计算 70 | * 正常的情况下,jsapi_ticket的有效期为7200秒,所以开发者需要在某个地方设计一个定时器,定期去更新jsapi_ticket 71 | */ 72 | public static String getJsapiTicket(String accessToken) throws OApiException { 73 | JSONObject jsTicketValue = (JSONObject) FileUtils.getValue("jsticket", Env.APP_KEY); 74 | long curTime = System.currentTimeMillis(); 75 | String jsTicket = ""; 76 | 77 | if (jsTicketValue == null || curTime - 78 | jsTicketValue.getLong("begin_time") >= cacheTime) { 79 | ServiceFactory serviceFactory; 80 | try { 81 | serviceFactory = ServiceFactory.getInstance(); 82 | JsapiService jsapiService = serviceFactory.getOpenService(JsapiService.class); 83 | 84 | JsapiTicket JsapiTicket = jsapiService.getJsapiTicket(accessToken, "jsapi"); 85 | jsTicket = JsapiTicket.getTicket(); 86 | 87 | JSONObject jsonTicket = new JSONObject(); 88 | JSONObject jsontemp = new JSONObject(); 89 | jsontemp.clear(); 90 | jsontemp.put("ticket", jsTicket); 91 | jsontemp.put("begin_time", curTime); 92 | jsonTicket.put(Env.APP_KEY, jsontemp); 93 | FileUtils.write2File(jsonTicket, "jsticket"); 94 | } catch (Exception e) { 95 | e.printStackTrace(); 96 | } 97 | return jsTicket; 98 | } else { 99 | return jsTicketValue.getString("ticket"); 100 | } 101 | } 102 | 103 | public static String sign(String ticket, String nonceStr, long timeStamp, String url) throws OApiException { 104 | try { 105 | return DingTalkJsApiSingnature.getJsApiSingnature(url, nonceStr, timeStamp, ticket); 106 | } catch (Exception ex) { 107 | throw new OApiException(0, ex.getMessage()); 108 | } 109 | } 110 | 111 | /** 112 | * 计算当前请求的jsapi的签名数据
113 | *

114 | * 如果签名数据是通过ajax异步请求的话,签名计算中的url必须是给用户展示页面的url 115 | * 116 | * @param request 117 | * @return 118 | */ 119 | public static String getConfig(HttpServletRequest request) { 120 | String urlString = request.getRequestURL().toString(); 121 | String queryString = request.getQueryString(); 122 | 123 | String queryStringEncode = null; 124 | String url; 125 | if (queryString != null) { 126 | queryStringEncode = URLDecoder.decode(queryString); 127 | url = urlString + "?" + queryStringEncode; 128 | } else { 129 | url = urlString; 130 | } 131 | /** 132 | * 确认url与配置的应用首页地址一致 133 | */ 134 | System.out.println(url); 135 | 136 | /** 137 | * 随机字符串 138 | */ 139 | String nonceStr = "abcdefg"; 140 | long timeStamp = System.currentTimeMillis() / 1000; 141 | String signedUrl = url; 142 | String accessToken = null; 143 | String ticket = null; 144 | String signature = null; 145 | 146 | try { 147 | accessToken = AuthHelper.getAccessToken(); 148 | 149 | ticket = AuthHelper.getJsapiTicket(accessToken); 150 | signature = AuthHelper.sign(ticket, nonceStr, timeStamp, signedUrl); 151 | 152 | } catch (OApiException e) { 153 | e.printStackTrace(); 154 | } 155 | 156 | Map configValue = new HashMap<>(); 157 | configValue.put("jsticket", ticket); 158 | configValue.put("signature", signature); 159 | configValue.put("nonceStr", nonceStr); 160 | configValue.put("timeStamp", timeStamp); 161 | configValue.put("corpId", Env.CORP_ID); 162 | configValue.put("agentId", Env.AGENT_ID); 163 | 164 | String config = JSON.toJSONString(configValue); 165 | return config; 166 | } 167 | 168 | public static String getSsoToken() throws OApiException { 169 | String url = "https://oapi.dingtalk.com/sso/gettoken?corpid=" + Env.CORP_ID + "&corpsecret=" + Env.SSO_Secret; 170 | JSONObject response = HttpHelper.httpGet(url); 171 | String ssoToken; 172 | if (response.containsKey("access_token")) { 173 | ssoToken = response.getString("access_token"); 174 | } else { 175 | throw new OApiException("Sso_token"); 176 | } 177 | return ssoToken; 178 | 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/department/DepartmentHelper.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.department; 2 | 3 | import com.dingtalk.open.client.ServiceFactory; 4 | import com.dingtalk.open.client.api.model.corp.Department; 5 | import com.dingtalk.open.client.api.service.corp.CorpDepartmentService; 6 | import com.dingtalk.open.client.common.SdkInitException; 7 | import com.dingtalk.open.client.common.ServiceException; 8 | import com.dingtalk.open.client.common.ServiceNotExistException; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * 部门相关API 14 | * 15 | * https://open-doc.dingtalk.com/docs/doc.htm?treeId=371&articleId=106817&docType=1 16 | */ 17 | public class DepartmentHelper { 18 | 19 | /** 20 | * 创建部门 21 | */ 22 | public static String createDepartment(String accessToken, String name, 23 | String parentId, String order, boolean createDeptGroup) throws Exception { 24 | 25 | CorpDepartmentService corpDepartmentService = ServiceFactory.getInstance().getOpenService(CorpDepartmentService.class); 26 | return corpDepartmentService.deptCreate(accessToken, name, parentId, order, createDeptGroup); 27 | } 28 | 29 | /** 30 | * 获取部门列表 31 | */ 32 | public static List listDepartments(String accessToken, String parentDeptId) 33 | throws ServiceNotExistException, SdkInitException, ServiceException { 34 | CorpDepartmentService corpDepartmentService = ServiceFactory.getInstance().getOpenService(CorpDepartmentService.class); 35 | List deptList = corpDepartmentService.getDeptList(accessToken, parentDeptId); 36 | return deptList; 37 | } 38 | 39 | 40 | /** 41 | * 删除部门 42 | */ 43 | public static void deleteDepartment(String accessToken, Long id) throws Exception { 44 | CorpDepartmentService corpDepartmentService = ServiceFactory.getInstance().getOpenService(CorpDepartmentService.class); 45 | corpDepartmentService.deptDelete(accessToken, id); 46 | } 47 | 48 | /** 49 | * 更新部门 50 | */ 51 | public static void updateDepartment(String accessToken, long id, String name, 52 | String parentId, String order, Boolean createDeptGroup, 53 | boolean autoAddUser, String deptManagerUseridList, boolean deptHiding, String deptPerimits, 54 | String userPerimits, Boolean outerDept, String outerPermitDepts, 55 | String outerPermitUsers, String orgDeptOwner) throws Exception { 56 | CorpDepartmentService corpDepartmentService = ServiceFactory.getInstance().getOpenService(CorpDepartmentService.class); 57 | corpDepartmentService.deptUpdate(accessToken, id, name, parentId, order, createDeptGroup, 58 | autoAddUser, deptManagerUseridList, deptHiding, deptPerimits, userPerimits, 59 | outerDept, outerPermitDepts, outerPermitUsers, orgDeptOwner); 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/eventchange/eventChangeHelper.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.eventchange; 2 | 3 | import com.alibaba.dingtalk.openapi.demo.Env; 4 | import com.alibaba.dingtalk.openapi.demo.OApiException; 5 | import com.alibaba.dingtalk.openapi.demo.utils.HttpHelper; 6 | import com.alibaba.fastjson.JSONObject; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * 通讯录回调相关事件 12 | *

13 | * https://open-doc.dingtalk.com/docs/doc.htm?treeId=371&articleId=104975&docType=1 14 | */ 15 | public class eventChangeHelper { 16 | 17 | /** 18 | * 注册事件回调接口 19 | */ 20 | public static String registerEventChange(String accessToken, List callBackTag, String token, String aesKey, String url) throws OApiException { 21 | String signUpUrl = Env.OAPI_HOST + "/call_back/register_call_back?" + 22 | "access_token=" + accessToken; 23 | JSONObject args = new JSONObject(); 24 | args.put("call_back_tag", callBackTag); 25 | args.put("token", token); 26 | args.put("aes_key", aesKey); 27 | args.put("url", url); 28 | 29 | JSONObject response = HttpHelper.httpPost(signUpUrl, args); 30 | if (response.containsKey("errcode")) { 31 | return response.getString("errcode"); 32 | } else { 33 | return null; 34 | } 35 | } 36 | 37 | //查询事件回调接口 38 | public static String getEventChange(String accessToken) throws OApiException { 39 | String url = Env.OAPI_HOST + "/call_back/get_call_back?" + 40 | "access_token=" + accessToken; 41 | JSONObject response = HttpHelper.httpGet(url); 42 | return response.toString(); 43 | } 44 | 45 | //更新事件回调接口 46 | public static String updateEventChange(String accessToken, List callBackTag, String token, String aesKey, String url) throws OApiException { 47 | String signUpUrl = Env.OAPI_HOST + "/call_back/update_call_back?" + 48 | "access_token=" + accessToken; 49 | JSONObject args = new JSONObject(); 50 | args.put("call_back_tag", callBackTag); 51 | args.put("token", token); 52 | args.put("aes_key", aesKey); 53 | args.put("url", url); 54 | 55 | JSONObject response = HttpHelper.httpPost(signUpUrl, args); 56 | if (response.containsKey("errcode")) { 57 | return response.getString("errcode"); 58 | } else { 59 | return null; 60 | } 61 | } 62 | 63 | //删除事件回调接口 64 | public static String deleteEventChange(String accessToken) throws OApiException { 65 | String url = Env.OAPI_HOST + "/call_back/delete_call_back?" + 66 | "access_token=" + accessToken; 67 | JSONObject response = HttpHelper.httpGet(url); 68 | return response.toString(); 69 | } 70 | 71 | 72 | public static String getFailedResult(String accessToken) throws OApiException { 73 | String url = Env.OAPI_HOST + "/call_back/get_call_back_failed_result?" + 74 | "access_token=" + accessToken; 75 | JSONObject response = HttpHelper.httpGet(url); 76 | return response.toString(); 77 | } 78 | 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/media/MediaHelper.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.media; 2 | 3 | import com.alibaba.dingtalk.openapi.demo.Env; 4 | import com.alibaba.dingtalk.openapi.demo.utils.HttpHelper; 5 | import com.alibaba.fastjson.JSONObject; 6 | import com.dingtalk.open.client.ServiceFactory; 7 | import com.dingtalk.open.client.api.model.corp.UploadResult; 8 | import com.dingtalk.open.client.api.service.corp.MediaService; 9 | 10 | import java.io.File; 11 | 12 | /** 13 | * 管理多媒体文件 14 | * https://open-doc.dingtalk.com/docs/doc.htm?source=search&treeId=373&articleId=104971&docType=1 15 | */ 16 | public class MediaHelper { 17 | 18 | /** 19 | * 资源文件类型 20 | */ 21 | public static final String TYPE_IMAGE = "image"; 22 | public static final String TYPE_VOICE = "voice"; 23 | public static final String TYPE_VIDEO = "video"; 24 | public static final String TYPE_FILE = "file"; 25 | 26 | 27 | public static class MediaUploadResult { 28 | public String type; 29 | public String media_id; 30 | public String created_at; 31 | } 32 | 33 | /** 34 | * 上传多媒体文件 35 | *

36 | */ 37 | public static UploadResult upload(String accessToken, String type, File file) throws Exception { 38 | 39 | MediaService mediaService = ServiceFactory.getInstance().getOpenService(MediaService.class); 40 | UploadResult uploadResult = mediaService.uploadMediaFile(accessToken, type, file); 41 | return uploadResult; 42 | } 43 | 44 | /** 45 | * 下载多媒体文件,目前sdk没有封装此接口,需要通过HTTP访问 46 | */ 47 | public static void download(String accessToken, String mediaId, String fileDir) throws Exception { 48 | String url = Env.OAPI_HOST + "/media/downloadFile?" + 49 | "access_token=" + accessToken + "&media_id=" + mediaId; 50 | JSONObject response = HttpHelper.downloadMedia(url, fileDir); 51 | System.out.println(response); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/message/ConversationMessageDelivery.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.message; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | 5 | public class ConversationMessageDelivery extends MessageDelivery { 6 | 7 | public String sender; 8 | public String cid; 9 | public String agentid; 10 | 11 | public ConversationMessageDelivery(String sender, String cid, 12 | String agentId) { 13 | this.sender = sender; 14 | this.cid = cid; 15 | this.agentid = agentId; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/message/ImageMessage.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.message; 2 | 3 | 4 | public class ImageMessage extends Message { 5 | 6 | public String media_id; 7 | 8 | public ImageMessage(String mediaId) { 9 | super(); 10 | media_id = mediaId; 11 | } 12 | 13 | @Override 14 | public String type() { 15 | return "image"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/message/LightAppMessageDelivery.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.message; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | 5 | public class LightAppMessageDelivery extends MessageDelivery { 6 | 7 | public String touser; 8 | public String toparty; 9 | public String agentid; 10 | 11 | public LightAppMessageDelivery(String toUsers, String toParties, String agentId) { 12 | this.touser = toUsers; 13 | this.toparty = toParties; 14 | this.agentid = agentId; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/message/LinkMessage.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.message; 2 | 3 | public class LinkMessage extends Message { 4 | 5 | public String messageUrl; 6 | public String picUrl; 7 | public String title; 8 | public String text; 9 | 10 | public LinkMessage(String messageUrl, String picUrl, String title, String text) { 11 | super(); 12 | this.messageUrl = messageUrl; 13 | this.picUrl = picUrl; 14 | this.title = title; 15 | this.text = text; 16 | } 17 | 18 | @Override 19 | public String type() { 20 | return "link"; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/message/Message.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.message; 2 | 3 | 4 | public abstract class Message { 5 | public abstract String type(); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/message/MessageDelivery.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.message; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONObject; 5 | import com.dingtalk.open.client.api.model.corp.MessageBody; 6 | 7 | public class MessageDelivery { 8 | 9 | public String msgType; 10 | public MessageBody message; 11 | 12 | public MessageDelivery withMessage(String msgType, MessageBody msg) { 13 | this.msgType = msgType; 14 | this.message = msg; 15 | return this; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/message/MessageHelper.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.message; 2 | 3 | import com.dingtalk.open.client.ServiceFactory; 4 | import com.dingtalk.open.client.api.model.corp.MessageSendResult; 5 | import com.dingtalk.open.client.api.service.corp.MessageService; 6 | 7 | /** 8 | * 发送消息 9 | */ 10 | public class MessageHelper { 11 | 12 | public static class Receipt { 13 | String invaliduser; 14 | String invalidparty; 15 | } 16 | 17 | /** 18 | * 发送普通消息 19 | * 20 | * @param accessToken 21 | * @param delivery 22 | * @return 23 | * @throws Exception 24 | */ 25 | public static Receipt send(String accessToken, LightAppMessageDelivery delivery) 26 | throws Exception { 27 | MessageService messageService = ServiceFactory.getInstance().getOpenService(MessageService.class); 28 | MessageSendResult reulst = messageService.sendToCorpConversation(accessToken, delivery.touser, 29 | delivery.toparty, delivery.agentid, delivery.msgType, delivery.message); 30 | Receipt receipt = new Receipt(); 31 | receipt.invaliduser = reulst.getInvaliduser(); 32 | receipt.invalidparty = reulst.getInvalidparty(); 33 | return receipt; 34 | } 35 | 36 | 37 | public static String send(String accessToken, ConversationMessageDelivery delivery) 38 | throws Exception { 39 | MessageService messageService = ServiceFactory.getInstance().getOpenService(MessageService.class); 40 | return messageService.sendToNormalConversation(accessToken, delivery.sender, 41 | delivery.cid, delivery.msgType, delivery.message); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/message/OAMessage.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.message; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | { 7 | "message_url": "http://dingtalk.com", 8 | "head": { 9 | "bgcolor": "FFCC0000" 10 | }, 11 | "body": { 12 | "title": "标题", 13 | "form": [ 14 | { 15 | "key": "姓名", 16 | "value": "张三" 17 | }, 18 | { 19 | "key": "年龄", 20 | "value": "30" 21 | } 22 | ], 23 | "rich": { 24 | "num": "15.6", 25 | "unit": "元" 26 | }, 27 | "content": "大段文本", 28 | "image": "@lADOAAGXIszazQKA", 29 | "file_count": "3", 30 | "author": "李四" 31 | } 32 | */ 33 | public class OAMessage extends Message { 34 | 35 | public String message_url; 36 | public Head head; 37 | public Body body; 38 | 39 | 40 | @Override 41 | public String type() { 42 | return "oa"; 43 | } 44 | 45 | //content 46 | public static class Head { 47 | public String bgcolor; 48 | } 49 | 50 | public static class Body { 51 | public String title; 52 | public List form; 53 | public Rich rich; 54 | public String content; 55 | public String image; 56 | public String file_found; 57 | public String author; 58 | 59 | public static class Form { 60 | public String key; 61 | public String value; 62 | } 63 | 64 | public static class Rich { 65 | public String num; 66 | public String unit; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/message/TextMessage.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.message; 2 | 3 | 4 | public class TextMessage extends Message { 5 | 6 | public String content; 7 | 8 | public TextMessage(String content) { 9 | super(); 10 | this.content = content; 11 | } 12 | 13 | @Override 14 | public String type() { 15 | return "text"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/user/User.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.user; 2 | 3 | import java.util.List; 4 | 5 | import com.alibaba.fastjson.JSONObject; 6 | 7 | public class User { 8 | public String userid; 9 | public String name; 10 | public boolean active; 11 | public String avatar; 12 | public List department; 13 | public String position; 14 | public String mobile; 15 | public String tel; 16 | public String workPlace; 17 | public String remark; 18 | public String email; 19 | public String jobnumber; 20 | public JSONObject extattr; 21 | public boolean isAdmin; 22 | public boolean isBoss; 23 | public String dingId; 24 | 25 | 26 | 27 | public User() { 28 | } 29 | 30 | public User(String userid, String name) { 31 | this.userid = userid; 32 | this.name = name; 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | List users; 38 | return "User[userid:" + userid + ", name:" + name + ", active:" + active + ", " 39 | + "avatar:" + avatar + ", department:" + department + 40 | ", position:" + position + ", mobile:" + mobile + ", email:" + email + 41 | ", extattr:" + extattr; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/user/UserHelper.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.user; 2 | 3 | import com.alibaba.dingtalk.openapi.demo.Env; 4 | import com.alibaba.dingtalk.openapi.demo.OApiException; 5 | import com.alibaba.dingtalk.openapi.demo.utils.FileUtils; 6 | import com.alibaba.dingtalk.openapi.demo.utils.HttpHelper; 7 | import com.alibaba.fastjson.JSONObject; 8 | import com.dingtalk.open.client.ServiceFactory; 9 | import com.dingtalk.open.client.api.model.corp.CorpUserBaseInfo; 10 | import com.dingtalk.open.client.api.model.corp.CorpUserDetail; 11 | import com.dingtalk.open.client.api.model.corp.CorpUserDetailList; 12 | import com.dingtalk.open.client.api.model.corp.CorpUserList; 13 | import com.dingtalk.open.client.api.service.corp.CorpUserService; 14 | 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | /** 19 | * 通讯录成员相关的接口调用 20 | */ 21 | public class UserHelper { 22 | 23 | 24 | /** 25 | * 根据免登授权码查询免登用户userId 26 | * 27 | * @param accessToken 28 | * @param code 29 | * @return 30 | * @throws Exception 31 | */ 32 | public static CorpUserBaseInfo getUserInfo(String accessToken, String code) throws Exception { 33 | CorpUserService corpUserService = ServiceFactory.getInstance().getOpenService(CorpUserService.class); 34 | return corpUserService.getUserinfo(accessToken, code); 35 | } 36 | 37 | /** 38 | * 创建企业成员 39 | *

40 | * https://open-doc.dingtalk.com/docs/doc.htm?treeId=385&articleId=106816&docType=1#s1 41 | */ 42 | public static String createUser(String accessToken, CorpUserDetail userDetail) throws Exception { 43 | CorpUserService corpUserService = ServiceFactory.getInstance().getOpenService(CorpUserService.class); 44 | JSONObject js = (JSONObject) JSONObject.parse(userDetail.getOrderInDepts()); 45 | Map orderInDepts = FileUtils.toHashMap(js); 46 | 47 | String userId = corpUserService.createCorpUser(accessToken, userDetail.getUserid(), userDetail.getName(), orderInDepts, 48 | userDetail.getDepartment(), userDetail.getPosition(), userDetail.getMobile(), userDetail.getTel(), userDetail.getWorkPlace(), 49 | userDetail.getRemark(), userDetail.getEmail(), userDetail.getJobnumber(), 50 | userDetail.getIsHide(), userDetail.getSenior(), userDetail.getExtattr()); 51 | 52 | // 员工唯一标识ID 53 | return userId; 54 | } 55 | 56 | 57 | /** 58 | * 更新成员 59 | *

60 | * https://open-doc.dingtalk.com/docs/doc.htm?treeId=385&articleId=106816&docType=1#s2 61 | */ 62 | public static void updateUser(String accessToken, CorpUserDetail userDetail) throws Exception { 63 | CorpUserService corpUserService = ServiceFactory.getInstance().getOpenService(CorpUserService.class); 64 | JSONObject js = (JSONObject) JSONObject.parse(userDetail.getOrderInDepts()); 65 | Map orderInDepts = FileUtils.toHashMap(js); 66 | 67 | corpUserService.updateCorpUser(accessToken, userDetail.getUserid(), userDetail.getName(), orderInDepts, 68 | userDetail.getDepartment(), userDetail.getPosition(), userDetail.getMobile(), userDetail.getTel(), userDetail.getWorkPlace(), 69 | userDetail.getRemark(), userDetail.getEmail(), userDetail.getJobnumber(), 70 | userDetail.getIsHide(), userDetail.getSenior(), userDetail.getExtattr()); 71 | } 72 | 73 | 74 | /** 75 | * 删除成员 76 | */ 77 | public static void deleteUser(String accessToken, String userid) throws Exception { 78 | CorpUserService corpUserService = ServiceFactory.getInstance().getOpenService(CorpUserService.class); 79 | corpUserService.deleteCorpUser(accessToken, userid); 80 | } 81 | 82 | 83 | //获取成员 84 | public static CorpUserDetail getUser(String accessToken, String userid) throws Exception { 85 | 86 | CorpUserService corpUserService = ServiceFactory.getInstance().getOpenService(CorpUserService.class); 87 | return corpUserService.getCorpUser(accessToken, userid); 88 | } 89 | 90 | //批量删除成员 91 | public static void batchDeleteUser(String accessToken, List useridlist) 92 | throws Exception { 93 | CorpUserService corpUserService = ServiceFactory.getInstance().getOpenService(CorpUserService.class); 94 | corpUserService.batchdeleteCorpUserListByUserids(accessToken, useridlist); 95 | 96 | } 97 | 98 | 99 | //获取部门成员 100 | public static CorpUserList getDepartmentUser( 101 | String accessToken, 102 | long departmentId, 103 | Long offset, 104 | Integer size, 105 | String order) 106 | throws Exception { 107 | 108 | CorpUserService corpUserService = ServiceFactory.getInstance().getOpenService(CorpUserService.class); 109 | return corpUserService.getCorpUserSimpleList(accessToken, departmentId, 110 | offset, size, order); 111 | } 112 | 113 | 114 | //获取部门成员(详情) 115 | public static CorpUserDetailList getUserDetails( 116 | String accessToken, 117 | long departmentId, 118 | Long offset, 119 | Integer size, 120 | String order) 121 | throws Exception { 122 | 123 | CorpUserService corpUserService = ServiceFactory.getInstance().getOpenService(CorpUserService.class); 124 | return corpUserService.getCorpUserList(accessToken, departmentId, 125 | offset, size, order); 126 | } 127 | 128 | 129 | /** 130 | * 管理后台免登时通过CODE换取微应用管理员的身份信息 131 | * 132 | * @param ssoToken 133 | * @param code 134 | * @return 135 | * @throws OApiException 136 | */ 137 | public static JSONObject getAgentUserInfo(String ssoToken, String code) throws OApiException { 138 | String url = Env.OAPI_HOST + "/sso/getuserinfo?" + "access_token=" + ssoToken + "&code=" + code; 139 | JSONObject response = HttpHelper.httpGet(url); 140 | return response; 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/utils/FileUtils.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.utils; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONObject; 5 | import org.apache.http.util.TextUtils; 6 | 7 | import java.io.*; 8 | import java.util.HashMap; 9 | import java.util.Iterator; 10 | import java.util.Map; 11 | import java.util.Set; 12 | 13 | /** 14 | * 模拟accessToken和jsTicket的数据持久化
15 | *

16 | * 正式项目最好是写入到Mysql 17 | */ 18 | public class FileUtils { 19 | 20 | public static final String FILEPATH = "Permanent_Data"; 21 | 22 | // json写入文件 23 | public synchronized static void write2File(Object json, String fileName) { 24 | BufferedWriter writer = null; 25 | File filePath = new File(FILEPATH); 26 | JSONObject eJSON = null; 27 | 28 | if (!filePath.exists() && !filePath.isDirectory()) { 29 | filePath.mkdirs(); 30 | } 31 | 32 | File file = new File(FILEPATH + File.separator + fileName + ".xml"); 33 | System.out.println("path:" + file.getPath() + " abs path:" + file.getAbsolutePath()); 34 | if (!file.exists()) { 35 | try { 36 | file.createNewFile(); 37 | } catch (Exception e) { 38 | System.out.println("createNewFile,出现异常:"); 39 | e.printStackTrace(); 40 | } 41 | } else { 42 | eJSON = (JSONObject) read2JSON(fileName); 43 | } 44 | 45 | try { 46 | writer = new BufferedWriter(new FileWriter(file)); 47 | 48 | if (eJSON == null) { 49 | writer.write(json.toString()); 50 | } else { 51 | Object[] array = ((JSONObject) json).keySet().toArray(); 52 | for (int i = 0; i < array.length; i++) { 53 | eJSON.put(array[i].toString(), ((JSONObject) json).get(array[i].toString())); 54 | } 55 | 56 | writer.write(eJSON.toString()); 57 | } 58 | 59 | } catch (IOException e) { 60 | e.printStackTrace(); 61 | } finally { 62 | try { 63 | if (writer != null) { 64 | writer.close(); 65 | } 66 | } catch (IOException e) { 67 | e.printStackTrace(); 68 | } 69 | } 70 | 71 | } 72 | 73 | // 读文件到json 74 | public static JSONObject read2JSON(String fileName) { 75 | File file = new File(FILEPATH + File.separator + fileName + ".xml"); 76 | if (!file.exists()) { 77 | return null; 78 | } 79 | 80 | BufferedReader reader = null; 81 | String laststr = ""; 82 | try { 83 | reader = new BufferedReader(new FileReader(file)); 84 | String tempString = null; 85 | while ((tempString = reader.readLine()) != null) { 86 | laststr += tempString; 87 | } 88 | reader.close(); 89 | } catch (IOException e) { 90 | e.printStackTrace(); 91 | } 92 | 93 | return (JSONObject) JSON.parse(laststr); 94 | } 95 | 96 | // 通过key值获取文件中的value 97 | public static Object getValue(String fileName, String key) { 98 | JSONObject eJSON = null; 99 | eJSON = (JSONObject) read2JSON(fileName); 100 | if (null != eJSON && eJSON.containsKey(key)) { 101 | @SuppressWarnings("unchecked") 102 | Map values = JSON.parseObject(eJSON.toString(), Map.class); 103 | return values.get(key); 104 | } else { 105 | return null; 106 | } 107 | } 108 | 109 | public static HashMap toHashMap(JSONObject js) { 110 | if (js == null) { 111 | return null; 112 | } 113 | HashMap data = new HashMap(); 114 | // 将json字符串转换成jsonObject 115 | Set set = js.keySet(); 116 | // 遍历jsonObject数据,添加到Map对象 117 | Iterator it = set.iterator(); 118 | while (it.hasNext()) { 119 | String key = String.valueOf(it.next()); 120 | Long keyLong = Long.valueOf(key); 121 | 122 | String value = js.getString(key); 123 | Long valueLong; 124 | if (TextUtils.isEmpty(value)) { 125 | valueLong = js.getLong(key); 126 | } else { 127 | valueLong = Long.valueOf(value); 128 | } 129 | data.put(keyLong, valueLong); 130 | } 131 | return data; 132 | } 133 | 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/utils/HttpHelper.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.utils; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.net.URI; 6 | 7 | import org.apache.commons.io.FileUtils; 8 | import org.apache.http.HttpEntity; 9 | import org.apache.http.client.config.RequestConfig; 10 | import org.apache.http.client.methods.CloseableHttpResponse; 11 | import org.apache.http.client.methods.HttpGet; 12 | import org.apache.http.client.methods.HttpPost; 13 | import org.apache.http.client.protocol.HttpClientContext; 14 | import org.apache.http.entity.StringEntity; 15 | import org.apache.http.entity.mime.MultipartEntityBuilder; 16 | import org.apache.http.entity.mime.content.FileBody; 17 | import org.apache.http.entity.ContentType; 18 | import org.apache.http.impl.client.CloseableHttpClient; 19 | import org.apache.http.impl.client.HttpClients; 20 | import org.apache.http.impl.client.RedirectLocations; 21 | import org.apache.http.protocol.BasicHttpContext; 22 | import org.apache.http.protocol.HttpContext; 23 | import org.apache.http.util.EntityUtils; 24 | 25 | import com.alibaba.dingtalk.openapi.demo.OApiException; 26 | import com.alibaba.fastjson.JSON; 27 | import com.alibaba.fastjson.JSONObject; 28 | 29 | /** 30 | * HTTP请求封装,建议直接使用sdk的API 31 | */ 32 | public class HttpHelper { 33 | 34 | public static JSONObject httpGet(String url) throws OApiException{ 35 | HttpGet httpGet = new HttpGet(url); 36 | CloseableHttpResponse response = null; 37 | CloseableHttpClient httpClient = HttpClients.createDefault(); 38 | RequestConfig requestConfig = RequestConfig.custom(). 39 | setSocketTimeout(2000).setConnectTimeout(2000).build(); 40 | httpGet.setConfig(requestConfig); 41 | 42 | try { 43 | response = httpClient.execute(httpGet, new BasicHttpContext()); 44 | 45 | if (response.getStatusLine().getStatusCode() != 200) { 46 | 47 | System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode() 48 | + ", url=" + url); 49 | return null; 50 | } 51 | HttpEntity entity = response.getEntity(); 52 | if (entity != null) { 53 | String resultStr = EntityUtils.toString(entity, "utf-8"); 54 | 55 | JSONObject result = JSON.parseObject(resultStr); 56 | if (result.getInteger("errcode") == 0) { 57 | return result; 58 | } else { 59 | System.out.println("request url=" + url + ",return value="); 60 | System.out.println(resultStr); 61 | int errCode = result.getInteger("errcode"); 62 | String errMsg = result.getString("errmsg"); 63 | throw new OApiException(errCode, errMsg); 64 | } 65 | } 66 | } catch (IOException e) { 67 | System.out.println("request url=" + url + ", exception, msg=" + e.getMessage()); 68 | e.printStackTrace(); 69 | } finally { 70 | if (response != null) try { 71 | response.close(); 72 | } catch (IOException e) { 73 | e.printStackTrace(); 74 | } 75 | } 76 | 77 | return null; 78 | } 79 | 80 | 81 | public static JSONObject httpPost(String url, Object data) throws OApiException { 82 | HttpPost httpPost = new HttpPost(url); 83 | CloseableHttpResponse response = null; 84 | CloseableHttpClient httpClient = HttpClients.createDefault(); 85 | RequestConfig requestConfig = RequestConfig.custom(). 86 | setSocketTimeout(2000).setConnectTimeout(2000).build(); 87 | httpPost.setConfig(requestConfig); 88 | httpPost.addHeader("Content-Type", "application/json"); 89 | 90 | try { 91 | StringEntity requestEntity = new StringEntity(JSON.toJSONString(data), "utf-8"); 92 | httpPost.setEntity(requestEntity); 93 | 94 | response = httpClient.execute(httpPost, new BasicHttpContext()); 95 | 96 | if (response.getStatusLine().getStatusCode() != 200) { 97 | 98 | System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode() 99 | + ", url=" + url); 100 | return null; 101 | } 102 | HttpEntity entity = response.getEntity(); 103 | if (entity != null) { 104 | String resultStr = EntityUtils.toString(entity, "utf-8"); 105 | 106 | JSONObject result = JSON.parseObject(resultStr); 107 | if (result.getInteger("errcode") == 0) { 108 | result.remove("errcode"); 109 | result.remove("errmsg"); 110 | return result; 111 | } else { 112 | System.out.println("request url=" + url + ",return value="); 113 | System.out.println(resultStr); 114 | int errCode = result.getInteger("errcode"); 115 | String errMsg = result.getString("errmsg"); 116 | throw new OApiException(errCode, errMsg); 117 | } 118 | } 119 | } catch (IOException e) { 120 | System.out.println("request url=" + url + ", exception, msg=" + e.getMessage()); 121 | e.printStackTrace(); 122 | } finally { 123 | if (response != null) try { 124 | response.close(); 125 | } catch (IOException e) { 126 | e.printStackTrace(); 127 | } 128 | } 129 | 130 | return null; 131 | } 132 | 133 | 134 | public static JSONObject uploadMedia(String url, File file) throws OApiException { 135 | HttpPost httpPost = new HttpPost(url); 136 | CloseableHttpResponse response = null; 137 | CloseableHttpClient httpClient = HttpClients.createDefault(); 138 | RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(2000).setConnectTimeout(2000).build(); 139 | httpPost.setConfig(requestConfig); 140 | 141 | HttpEntity requestEntity = MultipartEntityBuilder.create().addPart("media", 142 | new FileBody(file, ContentType.APPLICATION_OCTET_STREAM, file.getName())).build(); 143 | httpPost.setEntity(requestEntity); 144 | 145 | try { 146 | response = httpClient.execute(httpPost, new BasicHttpContext()); 147 | 148 | if (response.getStatusLine().getStatusCode() != 200) { 149 | 150 | System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode() 151 | + ", url=" + url); 152 | return null; 153 | } 154 | HttpEntity entity = response.getEntity(); 155 | if (entity != null) { 156 | String resultStr = EntityUtils.toString(entity, "utf-8"); 157 | 158 | JSONObject result = JSON.parseObject(resultStr); 159 | if (result.getInteger("errcode") == 0) { 160 | // 成功 161 | result.remove("errcode"); 162 | result.remove("errmsg"); 163 | return result; 164 | } else { 165 | System.out.println("request url=" + url + ",return value="); 166 | System.out.println(resultStr); 167 | int errCode = result.getInteger("errcode"); 168 | String errMsg = result.getString("errmsg"); 169 | throw new OApiException(errCode, errMsg); 170 | } 171 | } 172 | } catch (IOException e) { 173 | System.out.println("request url=" + url + ", exception, msg=" + e.getMessage()); 174 | e.printStackTrace(); 175 | } finally { 176 | if (response != null) try { 177 | response.close(); 178 | } catch (IOException e) { 179 | e.printStackTrace(); 180 | } 181 | } 182 | 183 | return null; 184 | } 185 | 186 | 187 | public static JSONObject downloadMedia(String url, String fileDir) throws OApiException { 188 | HttpGet httpGet = new HttpGet(url); 189 | CloseableHttpResponse response = null; 190 | CloseableHttpClient httpClient = HttpClients.createDefault(); 191 | RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(2000).setConnectTimeout(2000).build(); 192 | httpGet.setConfig(requestConfig); 193 | 194 | try { 195 | HttpContext localContext = new BasicHttpContext(); 196 | 197 | response = httpClient.execute(httpGet, localContext); 198 | 199 | RedirectLocations locations = (RedirectLocations) localContext.getAttribute(HttpClientContext.REDIRECT_LOCATIONS); 200 | if (locations != null) { 201 | URI downloadUrl = locations.getAll().get(0); 202 | String filename = downloadUrl.toURL().getFile(); 203 | System.out.println("downloadUrl=" + downloadUrl); 204 | File downloadFile = new File(fileDir + File.separator + filename); 205 | FileUtils.writeByteArrayToFile(downloadFile, EntityUtils.toByteArray(response.getEntity())); 206 | JSONObject obj = new JSONObject(); 207 | obj.put("downloadFilePath", downloadFile.getAbsolutePath()); 208 | obj.put("httpcode", response.getStatusLine().getStatusCode()); 209 | 210 | 211 | 212 | return obj; 213 | } else { 214 | if (response.getStatusLine().getStatusCode() != 200) { 215 | 216 | System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode() 217 | + ", url=" + url); 218 | return null; 219 | } 220 | HttpEntity entity = response.getEntity(); 221 | if (entity != null) { 222 | String resultStr = EntityUtils.toString(entity, "utf-8"); 223 | 224 | JSONObject result = JSON.parseObject(resultStr); 225 | if (result.getInteger("errcode") == 0) { 226 | // 成功 227 | result.remove("errcode"); 228 | result.remove("errmsg"); 229 | return result; 230 | } else { 231 | System.out.println("request url=" + url + ",return value="); 232 | System.out.println(resultStr); 233 | int errCode = result.getInteger("errcode"); 234 | String errMsg = result.getString("errmsg"); 235 | throw new OApiException(errCode, errMsg); 236 | } 237 | } 238 | } 239 | } catch (IOException e) { 240 | System.out.println("request url=" + url + ", exception, msg=" + e.getMessage()); 241 | e.printStackTrace(); 242 | } finally { 243 | if (response != null) try { 244 | response.close(); 245 | } catch (IOException e) { 246 | e.printStackTrace(); 247 | } 248 | } 249 | 250 | return null; 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/utils/aes/DingTalkEncryptException.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.utils.aes; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * 钉钉开放平台加解密异常类 8 | */ 9 | public class DingTalkEncryptException extends Exception { 10 | /**成功**/ 11 | public static final int SUCCESS = 0; 12 | /**加密明文文本非法**/ 13 | public final static int ENCRYPTION_PLAINTEXT_ILLEGAL = 900001; 14 | /**加密时间戳参数非法**/ 15 | public final static int ENCRYPTION_TIMESTAMP_ILLEGAL = 900002; 16 | /**加密随机字符串参数非法**/ 17 | public final static int ENCRYPTION_NONCE_ILLEGAL = 900003; 18 | /**不合法的aeskey**/ 19 | public final static int AES_KEY_ILLEGAL = 900004; 20 | /**签名不匹配**/ 21 | public final static int SIGNATURE_NOT_MATCH = 900005; 22 | /**计算签名错误**/ 23 | public final static int COMPUTE_SIGNATURE_ERROR = 900006; 24 | /**计算加密文字错误**/ 25 | public final static int COMPUTE_ENCRYPT_TEXT_ERROR = 900007; 26 | /**计算解密文字错误**/ 27 | public final static int COMPUTE_DECRYPT_TEXT_ERROR = 900008; 28 | /**计算解密文字长度不匹配**/ 29 | public final static int COMPUTE_DECRYPT_TEXT_LENGTH_ERROR = 900009; 30 | /**计算解密文字corpid不匹配**/ 31 | public final static int COMPUTE_DECRYPT_TEXT_CORPID_ERROR = 900010; 32 | 33 | private static Map msgMap = new HashMap(); 34 | static{ 35 | msgMap.put(SUCCESS,"成功"); 36 | msgMap.put(ENCRYPTION_PLAINTEXT_ILLEGAL,"加密明文文本非法"); 37 | msgMap.put(ENCRYPTION_TIMESTAMP_ILLEGAL,"加密时间戳参数非法"); 38 | msgMap.put(ENCRYPTION_NONCE_ILLEGAL,"加密随机字符串参数非法"); 39 | msgMap.put(SIGNATURE_NOT_MATCH,"签名不匹配"); 40 | msgMap.put(COMPUTE_SIGNATURE_ERROR,"签名计算失败"); 41 | msgMap.put(AES_KEY_ILLEGAL,"不合法的aes key"); 42 | msgMap.put(COMPUTE_ENCRYPT_TEXT_ERROR,"计算加密文字错误"); 43 | msgMap.put(COMPUTE_DECRYPT_TEXT_ERROR,"计算解密文字错误"); 44 | msgMap.put(COMPUTE_DECRYPT_TEXT_LENGTH_ERROR,"计算解密文字长度不匹配"); 45 | msgMap.put(COMPUTE_DECRYPT_TEXT_CORPID_ERROR,"计算解密文字corpid或者suiteKey不匹配"); 46 | } 47 | 48 | public Integer code; 49 | public DingTalkEncryptException(Integer exceptionCode){ 50 | super(msgMap.get(exceptionCode)); 51 | this.code = exceptionCode; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/utils/aes/DingTalkEncryptor.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.utils.aes; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.nio.charset.Charset; 5 | import java.security.MessageDigest; 6 | import java.util.*; 7 | 8 | import javax.crypto.Cipher; 9 | import javax.crypto.spec.IvParameterSpec; 10 | import javax.crypto.spec.SecretKeySpec; 11 | 12 | import org.apache.commons.codec.binary.Base64; 13 | 14 | 15 | /** 16 | * 钉钉开放平台加解密方法 17 | * 在ORACLE官方网站下载JCE无限制权限策略文件 18 | * JDK6的下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html 19 | * JDK7的下载地址: http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html 20 | */ 21 | public class DingTalkEncryptor { 22 | 23 | private static final Charset CHARSET = Charset.forName("utf-8"); 24 | private static final Base64 base64 = new Base64(); 25 | private byte[] aesKey; 26 | private String token; 27 | private String corpId; 28 | /**ask getPaddingBytes key固定长度**/ 29 | private static final Integer AES_ENCODE_KEY_LENGTH = 43; 30 | /**加密随机字符串字节长度**/ 31 | private static final Integer RANDOM_LENGTH = 16; 32 | 33 | /** 34 | * 构造函数 35 | * @param token 钉钉开放平台上,开发者设置的token 36 | * @param encodingAesKey 钉钉开放台上,开发者设置的EncodingAESKey 37 | * @param corpId ISV进行配置的时候应该传对应套件的SUITE_KEY,普通企业是Corpid 38 | * @throws DingTalkEncryptException 执行失败,请查看该异常的错误码和具体的错误信息 39 | */ 40 | public DingTalkEncryptor(String token, String encodingAesKey, String corpId) throws DingTalkEncryptException{ 41 | if (null==encodingAesKey || encodingAesKey.length() != AES_ENCODE_KEY_LENGTH) { 42 | throw new DingTalkEncryptException(DingTalkEncryptException.AES_KEY_ILLEGAL); 43 | } 44 | this.token = token; 45 | this.corpId = corpId; 46 | aesKey = Base64.decodeBase64(encodingAesKey + "="); 47 | } 48 | 49 | /** 50 | * 将和钉钉开放平台同步的消息体加密,返回加密Map 51 | * @param plaintext 传递的消息体明文 52 | * @param timeStamp 时间戳 53 | * @param nonce 随机字符串 54 | * @return 55 | * @throws DingTalkEncryptException 56 | */ 57 | public Map getEncryptedMap(String plaintext, Long timeStamp, String nonce) throws DingTalkEncryptException { 58 | if(null==plaintext){ 59 | throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_PLAINTEXT_ILLEGAL); 60 | } 61 | if(null==timeStamp){ 62 | throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_TIMESTAMP_ILLEGAL); 63 | } 64 | if(null==nonce){ 65 | throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_NONCE_ILLEGAL); 66 | } 67 | // 加密 68 | String encrypt = encrypt(Utils.getRandomStr(RANDOM_LENGTH), plaintext); 69 | String signature = getSignature(token, String.valueOf(timeStamp), nonce, encrypt); 70 | Map resultMap = new HashMap(); 71 | resultMap.put("msg_signature", signature); 72 | resultMap.put("encrypt", encrypt); 73 | resultMap.put("timeStamp", String.valueOf(timeStamp)); 74 | resultMap.put("nonce", nonce); 75 | return resultMap; 76 | } 77 | 78 | /** 79 | * 密文解密 80 | * @param msgSignature 签名串 81 | * @param timeStamp 时间戳 82 | * @param nonce 随机串 83 | * @param encryptMsg 密文 84 | * @return 解密后的原文 85 | * @throws DingTalkEncryptException 86 | */ 87 | public String getDecryptMsg(String msgSignature, String timeStamp, String nonce, String encryptMsg)throws DingTalkEncryptException { 88 | //校验签名 89 | String signature = getSignature(token, timeStamp, nonce, encryptMsg); 90 | if (!signature.equals(msgSignature)) { 91 | throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_SIGNATURE_ERROR); 92 | } 93 | // 解密 94 | String result = decrypt(encryptMsg); 95 | return result; 96 | } 97 | 98 | 99 | /* 100 | * 对明文加密. 101 | * @param text 需要加密的明文 102 | * @return 加密后base64编码的字符串 103 | */ 104 | private String encrypt(String random, String plaintext) throws DingTalkEncryptException { 105 | try { 106 | byte[] randomBytes = random.getBytes(CHARSET); 107 | byte[] plainTextBytes = plaintext.getBytes(CHARSET); 108 | byte[] lengthByte = Utils.int2Bytes(plainTextBytes.length); 109 | byte[] corpidBytes = corpId.getBytes(CHARSET); 110 | ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); 111 | byteStream.write(randomBytes); 112 | byteStream.write(lengthByte); 113 | byteStream.write(plainTextBytes); 114 | byteStream.write(corpidBytes); 115 | byte[] padBytes = PKCS7Padding.getPaddingBytes(byteStream.size()); 116 | byteStream.write(padBytes); 117 | byte[] unencrypted = byteStream.toByteArray(); 118 | byteStream.close(); 119 | Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); 120 | SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); 121 | IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16); 122 | cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); 123 | byte[] encrypted = cipher.doFinal(unencrypted); 124 | String result = base64.encodeToString(encrypted); 125 | return result; 126 | } catch (Exception e) { 127 | throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_ENCRYPT_TEXT_ERROR); 128 | } 129 | } 130 | 131 | /* 132 | * 对密文进行解密. 133 | * @param text 需要解密的密文 134 | * @return 解密得到的明文 135 | */ 136 | private String decrypt(String text) throws DingTalkEncryptException { 137 | byte[] originalArr; 138 | try { 139 | // 设置解密模式为AES的CBC模式 140 | Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); 141 | SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); 142 | IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16)); 143 | cipher.init(Cipher.DECRYPT_MODE, keySpec, iv); 144 | // 使用BASE64对密文进行解码 145 | byte[] encrypted = Base64.decodeBase64(text); 146 | // 解密 147 | originalArr = cipher.doFinal(encrypted); 148 | } catch (Exception e) { 149 | throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_ERROR); 150 | } 151 | 152 | String plainText; 153 | String fromCorpid; 154 | try { 155 | // 去除补位字符 156 | byte[] bytes = PKCS7Padding.removePaddingBytes(originalArr); 157 | // 分离16位随机字符串,网络字节序和corpId 158 | byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20); 159 | int plainTextLegth = Utils.bytes2int(networkOrder); 160 | plainText = new String(Arrays.copyOfRange(bytes, 20, 20 + plainTextLegth), CHARSET); 161 | fromCorpid = new String(Arrays.copyOfRange(bytes, 20 + plainTextLegth, bytes.length), CHARSET); 162 | } catch (Exception e) { 163 | throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_LENGTH_ERROR); 164 | } 165 | 166 | // corpid不相同的情况 167 | if (!fromCorpid.equals(corpId)) { 168 | throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_CORPID_ERROR); 169 | } 170 | return plainText; 171 | } 172 | 173 | /** 174 | * 数字签名 175 | * @param token isv token 176 | * @param timestamp 时间戳 177 | * @param nonce 随机串 178 | * @param encrypt 加密文本 179 | * @return 180 | * @throws DingTalkEncryptException 181 | */ 182 | public String getSignature(String token, String timestamp, String nonce, String encrypt) throws DingTalkEncryptException { 183 | try { 184 | String[] array = new String[] { token, timestamp, nonce, encrypt }; 185 | Arrays.sort(array); 186 | StringBuffer sb = new StringBuffer(); 187 | for (int i = 0; i < 4; i++) { 188 | sb.append(array[i]); 189 | } 190 | String str = sb.toString(); 191 | MessageDigest md = MessageDigest.getInstance("SHA-1"); 192 | md.update(str.getBytes()); 193 | byte[] digest = md.digest(); 194 | 195 | StringBuffer hexstr = new StringBuffer(); 196 | String shaHex = ""; 197 | for (int i = 0; i < digest.length; i++) { 198 | shaHex = Integer.toHexString(digest[i] & 0xFF); 199 | if (shaHex.length() < 2) { 200 | hexstr.append(0); 201 | } 202 | hexstr.append(shaHex); 203 | } 204 | return hexstr.toString(); 205 | } catch (Exception e) { 206 | throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_SIGNATURE_ERROR); 207 | } 208 | } 209 | 210 | } 211 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/utils/aes/DingTalkJsApiSingnature.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.utils.aes; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.security.MessageDigest; 5 | import java.security.NoSuchAlgorithmException; 6 | import java.util.Formatter; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | /** 11 | * 钉钉jsapi签名工具类 12 | */ 13 | public class DingTalkJsApiSingnature { 14 | /** 15 | * 获取jsapi签名 16 | * @param url 17 | * @param nonce 18 | * @param timeStamp 19 | * @param jsTicket 20 | * @return 21 | * @throws DingTalkEncryptException 22 | */ 23 | public static String getJsApiSingnature(String url,String nonce,Long timeStamp,String jsTicket) throws DingTalkEncryptException{ 24 | String plainTex = "jsapi_ticket=" + jsTicket +"&noncestr=" + nonce +"×tamp=" + timeStamp + "&url=" + url; 25 | System.out.println(plainTex); 26 | String signature = ""; 27 | try{ 28 | MessageDigest crypt = MessageDigest.getInstance("SHA-1"); 29 | crypt.reset(); 30 | crypt.update(plainTex.getBytes("UTF-8")); 31 | signature = byteToHex(crypt.digest()); 32 | return signature; 33 | }catch (Exception e){ 34 | throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_SIGNATURE_ERROR); 35 | } 36 | } 37 | 38 | private static String byteToHex(final byte[] hash) { 39 | Formatter formatter = new Formatter(); 40 | for (byte b : hash){ 41 | formatter.format("%02x", b); 42 | } 43 | String result = formatter.toString(); 44 | formatter.close(); 45 | return result; 46 | } 47 | 48 | 49 | public static void main(String args[]) throws Exception{ 50 | String url="http://10.62.53.138:3000/jsapi"; 51 | String nonce="abcdefgh"; 52 | Long timeStamp = 1437027269927L; 53 | String tikcet="zHoQdGJuH0ZDebwo7sLqLzHGUueLmkWCC4RycYgkuvDu3eoROgN5qhwnQLgfzwEXtuR9SDzh6BdhyVngzAjrxV"; 54 | System.err.println(getJsApiSingnature(url,nonce,timeStamp,tikcet)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/utils/aes/PKCS7Padding.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.utils.aes; 2 | 3 | import java.nio.charset.Charset; 4 | import java.util.Arrays; 5 | 6 | /* 7 | * PKCS7算法的加密填充 8 | */ 9 | 10 | public class PKCS7Padding { 11 | private final static Charset CHARSET = Charset.forName("utf-8"); 12 | private final static int BLOCK_SIZE = 32; 13 | 14 | /** 15 | * 填充mode字节 16 | * @param count 17 | * @return 18 | */ 19 | public static byte[] getPaddingBytes(int count) { 20 | int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); 21 | if (amountToPad == 0) { 22 | amountToPad = BLOCK_SIZE; 23 | } 24 | char padChr = chr(amountToPad); 25 | String tmp = new String(); 26 | for (int index = 0; index < amountToPad; index++) { 27 | tmp += padChr; 28 | } 29 | return tmp.getBytes(CHARSET); 30 | } 31 | 32 | /** 33 | * 移除mode填充字节 34 | * @param decrypted 35 | * @return 36 | */ 37 | public static byte[] removePaddingBytes(byte[] decrypted) { 38 | int pad = (int) decrypted[decrypted.length - 1]; 39 | if (pad < 1 || pad > BLOCK_SIZE) { 40 | pad = 0; 41 | } 42 | return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); 43 | } 44 | 45 | private static char chr(int a) { 46 | byte target = (byte) (a & 0xFF); 47 | return (char) target; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/demo/utils/aes/Utils.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.demo.utils.aes; 2 | 3 | import java.util.Random; 4 | 5 | /** 6 | * 加解密工具类 7 | */ 8 | public class Utils { 9 | 10 | /** 11 | * 获取随机字符串 12 | * 13 | * @return 14 | */ 15 | public static String getRandomStr(int count) { 16 | String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 17 | Random random = new Random(); 18 | StringBuffer sb = new StringBuffer(); 19 | for (int i = 0; i < count; i++) { 20 | int number = random.nextInt(base.length()); 21 | sb.append(base.charAt(number)); 22 | } 23 | return sb.toString(); 24 | } 25 | 26 | 27 | /* 28 | * int转byte数组,高位在前 29 | */ 30 | public static byte[] int2Bytes(int count) { 31 | byte[] byteArr = new byte[4]; 32 | byteArr[3] = (byte) (count & 0xFF); 33 | byteArr[2] = (byte) (count >> 8 & 0xFF); 34 | byteArr[1] = (byte) (count >> 16 & 0xFF); 35 | byteArr[0] = (byte) (count >> 24 & 0xFF); 36 | return byteArr; 37 | } 38 | 39 | /** 40 | * 高位在前bytes数组转int 41 | * 42 | * @param byteArr 43 | * @return 44 | */ 45 | public static int bytes2int(byte[] byteArr) { 46 | int count = 0; 47 | for (int i = 0; i < 4; i++) { 48 | count <<= 8; 49 | count |= byteArr[i] & 0xff; 50 | } 51 | return count; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/servlet/CallbackServlet.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.servlet; 2 | 3 | import com.alibaba.dingtalk.openapi.demo.Env; 4 | import com.alibaba.dingtalk.openapi.demo.auth.AuthHelper; 5 | import com.alibaba.fastjson.JSON; 6 | import com.alibaba.fastjson.JSONObject; 7 | import com.dingtalk.api.DefaultDingTalkClient; 8 | import com.dingtalk.api.DingTalkClient; 9 | import com.dingtalk.api.request.OapiCallBackDeleteCallBackRequest; 10 | import com.dingtalk.api.request.OapiCallBackRegisterCallBackRequest; 11 | import com.dingtalk.api.response.OapiCallBackRegisterCallBackResponse; 12 | import com.dingtalk.oapi.lib.aes.DingTalkEncryptor; 13 | import com.dingtalk.oapi.lib.aes.Utils; 14 | 15 | import javax.servlet.http.HttpServlet; 16 | import javax.servlet.http.HttpServletRequest; 17 | import javax.servlet.http.HttpServletResponse; 18 | import java.io.BufferedReader; 19 | import java.io.IOException; 20 | import java.util.Arrays; 21 | import java.util.Map; 22 | 23 | /** 24 | * @Author: xiaoqian 25 | * @Date: 2019/3/25 上午10:34 26 | * @Description: 27 | */ 28 | public class CallbackServlet extends HttpServlet { 29 | 30 | /** 31 | * 创建套件后,验证回调URL创建有效事件 32 | */ 33 | private static final String CHECK_URL = "check_url"; 34 | 35 | /** 36 | * 通讯录用户增加事件 37 | */ 38 | public static final String USER_ADD_ORG = "user_add_org"; 39 | 40 | /** 41 | * 通讯录用户更改事件 42 | */ 43 | public static final String USER_MODIFY_ORG = "user_modify_org"; 44 | 45 | public static final String CALLBACK_RESPONSE_SUCCESS = "success"; 46 | 47 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 48 | response.setContentType("application/json; charset=utf-8"); 49 | String signature = request.getParameter("signature"); 50 | String timestamp = request.getParameter("timestamp"); 51 | String nonce = request.getParameter("nonce"); 52 | BufferedReader br = null; 53 | StringBuilder sb = new StringBuilder(); 54 | try { 55 | DingTalkEncryptor dingTalkEncryptor = new DingTalkEncryptor(Env.TOKEN, Env.ENCODING_AES_KEY, 56 | Env.CORP_ID); 57 | br = request.getReader(); 58 | String str; 59 | while ((str = br.readLine()) != null) { 60 | sb.append(str); 61 | } 62 | br.close(); 63 | /** 64 | * 从post请求的body中获取回调信息的加密数据进行解密处理 65 | */ 66 | JSONObject jsonObject = JSON.parseObject(sb.toString()); 67 | String encryptMsg = jsonObject.getString("encrypt"); 68 | String plainText = dingTalkEncryptor.getDecryptMsg(signature, timestamp, nonce, encryptMsg); 69 | JSONObject obj = JSON.parseObject(plainText); 70 | /** 71 | * 根据回调数据类型做不同的业务处理 72 | */ 73 | String eventType = obj.getString("EventType"); 74 | if (CHECK_URL.equals(eventType)) { 75 | System.out.println("验证回调URL创建有效事件:" + plainText); 76 | } else if (USER_ADD_ORG.equals(eventType)) { 77 | System.out.println("通讯录用户增加事件事件:" + plainText); 78 | //todo 实现相关业务逻辑 79 | } else if (USER_MODIFY_ORG.equals(eventType)) { 80 | System.out.println("通讯录用户更改事件:" + plainText); 81 | //todo 实现相关业务逻辑 82 | } else { 83 | //todo 其他类型事件处理 84 | } 85 | /** 86 | * 返回success的加密信息表示回调处理成功 87 | */ 88 | Map encryptedMap = dingTalkEncryptor.getEncryptedMap(CALLBACK_RESPONSE_SUCCESS, System.currentTimeMillis(), Utils.getRandomStr(8)); 89 | response.getWriter().append(JSON.toJSONString(encryptedMap)); 90 | } catch (Exception e) { 91 | /** 92 | * 失败的情况,应用的开发者应该通过告警感知,并干预修复 93 | */ 94 | System.err.println(e.getMessage()); 95 | System.out.println("process callback failed!"); 96 | } finally { 97 | if (br != null) { 98 | br.close(); 99 | } 100 | } 101 | 102 | } 103 | 104 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { 105 | doGet(request, response); 106 | } 107 | 108 | public static void main(String[] args) throws Exception{ 109 | /** 110 | * 先删除企业已有的回调 111 | */ 112 | DingTalkClient client = new DefaultDingTalkClient(Env.DELETE_CALLBACK); 113 | OapiCallBackDeleteCallBackRequest request = new OapiCallBackDeleteCallBackRequest(); 114 | request.setHttpMethod("GET"); 115 | client.execute(request, AuthHelper.getAccessToken()); 116 | 117 | /** 118 | * 重新为企业注册回调 119 | */ 120 | client = new DefaultDingTalkClient(Env.REGISTER_CALLBACK); 121 | OapiCallBackRegisterCallBackRequest registerRequest = new OapiCallBackRegisterCallBackRequest(); 122 | registerRequest.setUrl(Env.CALLBACK_URL_HOST + "/callback"); 123 | registerRequest.setAesKey(Env.ENCODING_AES_KEY); 124 | registerRequest.setToken(Env.TOKEN); 125 | /** 126 | * 需要注册的回调事件 127 | * 参考 https://open-doc.dingtalk.com/microapp/serverapi2/skn8ld 128 | */ 129 | registerRequest.setCallBackTag(Arrays.asList("user_add_org", "user_modify_org")); 130 | OapiCallBackRegisterCallBackResponse registerResponse = client.execute(registerRequest, AuthHelper.getAccessToken()); 131 | if (registerResponse.isSuccess()) { 132 | System.out.println("回调注册成功了!!!"); 133 | } 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/servlet/ContactsServlet.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.servlet; 2 | 3 | import com.alibaba.dingtalk.openapi.demo.auth.AuthHelper; 4 | import com.alibaba.dingtalk.openapi.demo.department.DepartmentHelper; 5 | import com.alibaba.dingtalk.openapi.demo.user.UserHelper; 6 | import com.alibaba.fastjson.JSON; 7 | import com.alibaba.fastjson.JSONArray; 8 | import com.alibaba.fastjson.JSONObject; 9 | import com.dingtalk.open.client.api.model.corp.CorpUserDetail; 10 | import com.dingtalk.open.client.api.model.corp.CorpUserList; 11 | import com.dingtalk.open.client.api.model.corp.Department; 12 | 13 | import javax.servlet.http.HttpServlet; 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | import java.io.IOException; 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | /** 21 | * 查询企业通讯录下的用户列表 22 | */ 23 | public class ContactsServlet extends HttpServlet { 24 | 25 | /** 26 | * 查看当前accessToken下的可以查询到的员工列表
27 | *

28 | * 先查部门列表,然后再查部门下的所有人 29 | * 30 | * @param request 31 | * @param response 32 | * @throws IOException 33 | */ 34 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 35 | 36 | try { 37 | response.setContentType("text/html; charset=utf-8"); 38 | String accessToken = AuthHelper.getAccessToken(); 39 | 40 | List departments = new ArrayList(); 41 | // 1表示部门根目录,如果获取accessToken的corpSecret设置了部门范围,需要更改成对应部门的id 42 | // 可以通过https://oapi.dingtalk.com/auth/scopes?access_token=ACCESS_TOKEN 查询部门id列表 43 | departments = DepartmentHelper.listDepartments(accessToken, "1"); 44 | JSONObject json = new JSONObject(); 45 | JSONArray usersArray = new JSONArray(); 46 | 47 | System.out.println("depart num:" + departments.size()); 48 | for (int i = 0; i < departments.size(); i++) { 49 | JSONObject usersJSON = new JSONObject(); 50 | JSONArray userArray = new JSONArray(); 51 | 52 | long offset = 0; 53 | int size = 5; 54 | CorpUserList corpUserList = new CorpUserList(); 55 | while (true) { 56 | corpUserList = UserHelper.getDepartmentUser(accessToken, Long.valueOf(departments.get(i).getId()) 57 | , offset, size, null); 58 | System.out.println(JSON.toJSONString(corpUserList)); 59 | if (Boolean.TRUE.equals(corpUserList.isHasMore())) { 60 | offset += size; 61 | } else { 62 | break; 63 | } 64 | } 65 | 66 | if (corpUserList.getUserlist().size() == 0) { 67 | continue; 68 | } 69 | for (int j = 0; j < corpUserList.getUserlist().size(); j++) { 70 | String user = JSON.toJSONString(corpUserList.getUserlist().get(j)); 71 | userArray.add(JSONObject.parseObject(user, CorpUserDetail.class)); 72 | } 73 | System.out.println("user:" + userArray.toString()); 74 | usersJSON.put("name", departments.get(i).getName()); 75 | usersJSON.put("member", userArray); 76 | usersArray.add(usersJSON); 77 | } 78 | json.put("department", usersArray); 79 | System.out.println("depart:" + json.toJSONString()); 80 | response.getWriter().append(json.toJSONString()); 81 | 82 | } catch (Exception e) { 83 | e.printStackTrace(); 84 | response.getWriter().append(e.getMessage()); 85 | } 86 | } 87 | 88 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { 89 | doGet(request, response); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/servlet/EventChangeReceiveServlet.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.servlet; 2 | 3 | import com.alibaba.dingtalk.openapi.demo.Env; 4 | import com.alibaba.dingtalk.openapi.demo.utils.aes.DingTalkEncryptException; 5 | import com.alibaba.dingtalk.openapi.demo.utils.aes.DingTalkEncryptor; 6 | import com.alibaba.fastjson.JSONObject; 7 | 8 | import javax.servlet.ServletException; 9 | import javax.servlet.ServletInputStream; 10 | import javax.servlet.http.HttpServlet; 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | import java.io.BufferedReader; 14 | import java.io.IOException; 15 | import java.io.InputStreamReader; 16 | import java.util.Map; 17 | 18 | /** 19 | * 企业通讯录回调地址实现
20 | * 21 | * 详细文档见: https://open-doc.dingtalk.com/docs/doc.htm?treeId=385&articleId=104975&docType=1 22 | */ 23 | public class EventChangeReceiveServlet extends HttpServlet { 24 | 25 | 26 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 27 | /**url中的签名**/ 28 | String msgSignature = request.getParameter("signature"); 29 | /**url中的时间戳**/ 30 | String timeStamp = request.getParameter("timestamp"); 31 | /**url中的随机字符串**/ 32 | String nonce = request.getParameter("nonce"); 33 | 34 | /**post数据包数据中的加密数据**/ 35 | ServletInputStream sis = request.getInputStream(); 36 | BufferedReader br = new BufferedReader(new InputStreamReader(sis)); 37 | String line = null; 38 | StringBuilder sb = new StringBuilder(); 39 | while ((line = br.readLine()) != null) { 40 | sb.append(line); 41 | } 42 | JSONObject jsonEncrypt = JSONObject.parseObject(sb.toString()); 43 | String encrypt = jsonEncrypt.getString("encrypt"); 44 | 45 | // 对回调的参数进行解密,确保请求合法 46 | /**对encrypt进行解密**/ 47 | DingTalkEncryptor dingTalkEncryptor = null; 48 | String plainText = null; 49 | try { 50 | // 根据用户注册的token和AES_KEY进行解密 51 | dingTalkEncryptor = new DingTalkEncryptor(Env.TOKEN, Env.ENCODING_AES_KEY, Env.CORP_ID); 52 | plainText = dingTalkEncryptor.getDecryptMsg(msgSignature, timeStamp, nonce, encrypt); 53 | } catch (DingTalkEncryptException e) { 54 | e.printStackTrace(); 55 | throw new RuntimeException(e); 56 | } 57 | 58 | /**对从encrypt解密出来的明文进行处理**/ 59 | JSONObject plainTextJson = JSONObject.parseObject(plainText); 60 | String eventType = plainTextJson.getString("EventType"); 61 | switch (eventType) { 62 | case "user_add_org"://通讯录用户增加 do something 63 | break; 64 | case "user_modify_org"://通讯录用户更改 do something 65 | break; 66 | case "user_leave_org"://通讯录用户离职 do something 67 | break; 68 | case "org_admin_add"://通讯录用户被设为管理员 do something 69 | break; 70 | case "org_admin_remove"://通讯录用户被取消设置管理员 do something 71 | break; 72 | case "org_dept_create"://通讯录企业部门创建 do something 73 | break; 74 | case "org_dept_modify"://通讯录企业部门修改 do something 75 | break; 76 | case "org_dept_remove"://通讯录企业部门删除 do something 77 | break; 78 | case "org_remove"://企业被解散 do something 79 | break; 80 | 81 | 82 | case "check_url"://do something 83 | default: //do something 84 | break; 85 | } 86 | 87 | /**对返回信息进行加密**/ 88 | long timeStampLong = Long.parseLong(timeStamp); 89 | Map jsonMap = null; 90 | try { 91 | jsonMap = dingTalkEncryptor.getEncryptedMap("success", timeStampLong, nonce); 92 | } catch (DingTalkEncryptException e) { 93 | System.out.println(e.getMessage()); 94 | e.printStackTrace(); 95 | } 96 | JSONObject json = new JSONObject(); 97 | json.putAll(jsonMap); 98 | response.getWriter().append(json.toString()); 99 | } 100 | 101 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 102 | doGet(request, response); 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/servlet/OAoauth.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.servlet; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.ServletException; 6 | import javax.servlet.http.HttpServlet; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | import com.alibaba.dingtalk.openapi.demo.Env; 11 | import com.alibaba.dingtalk.openapi.demo.OApiException; 12 | import com.alibaba.dingtalk.openapi.demo.auth.AuthHelper; 13 | import com.alibaba.dingtalk.openapi.demo.user.UserHelper; 14 | import com.alibaba.fastjson.JSONObject; 15 | 16 | /** 17 | * 应用管理后台免登流程 18 | */ 19 | public class OAoauth extends HttpServlet{ 20 | 21 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException{ 22 | String code = request.getParameter("code"); 23 | if(code != null){ 24 | try { 25 | String ssoToken = AuthHelper.getSsoToken(); 26 | response.getWriter().append(ssoToken); 27 | JSONObject js = UserHelper.getAgentUserInfo(ssoToken, code); 28 | response.getWriter().append(js.toString()); 29 | } catch (OApiException e) { 30 | e.printStackTrace(); 31 | } 32 | }else{ 33 | // 免登成功后会跳转到redirect_url 34 | String reurl = "https://oa.dingtalk.com/omp/api/micro_app/admin/landing?corpid=" + 35 | Env.CORP_ID + "&redirect_url=" + Env.OA_BACKGROUND_URL;//配置的OA后台地址 36 | response.addHeader("location", reurl); 37 | response.setStatus(302); 38 | } 39 | } 40 | 41 | 42 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 43 | doGet(request, response); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/servlet/UserInfoServlet.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.servlet; 2 | 3 | import com.alibaba.dingtalk.openapi.demo.auth.AuthHelper; 4 | import com.alibaba.dingtalk.openapi.demo.user.UserHelper; 5 | import com.alibaba.fastjson.JSON; 6 | import com.dingtalk.open.client.api.model.corp.CorpUserDetail; 7 | 8 | import javax.servlet.http.HttpServlet; 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | import java.io.IOException; 12 | 13 | /** 14 | * 根据免登授权码查询免登用户信息 15 | */ 16 | public class UserInfoServlet extends HttpServlet { 17 | 18 | 19 | /** 20 | * 根据免登授权码获取免登用户userId 21 | * 22 | * @param request 23 | * @param response 24 | * @throws IOException 25 | */ 26 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 27 | // 获取免登授权码 28 | String code = request.getParameter("code"); 29 | String corpId = request.getParameter("corpid"); 30 | System.out.println("authCode:" + code + " corpid:" + corpId); 31 | try { 32 | response.setContentType("text/html; charset=utf-8"); 33 | 34 | String accessToken = AuthHelper.getAccessToken(); 35 | System.out.println("access token:" + accessToken); 36 | CorpUserDetail user = UserHelper.getUser(accessToken, UserHelper.getUserInfo(accessToken, code).getUserid()); 37 | 38 | String userJson = JSON.toJSONString(user); 39 | response.getWriter().append(userJson); 40 | System.out.println("userjson:" + userJson); 41 | } catch (Exception e) { 42 | e.printStackTrace(); 43 | response.getWriter().append(e.getMessage()); 44 | } 45 | } 46 | 47 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { 48 | doGet(request, response); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dingtalk/openapi/servlet/WelcomeServlet.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dingtalk.openapi.servlet; 2 | 3 | import javax.servlet.http.HttpServlet; 4 | import javax.servlet.http.HttpServletRequest; 5 | import javax.servlet.http.HttpServletResponse; 6 | import java.io.IOException; 7 | 8 | /** 9 | * @Author: xiaoqian 10 | * @Date: 2019/3/22 下午3:35 11 | * @Description: 12 | */ 13 | public class WelcomeServlet extends HttpServlet { 14 | 15 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 16 | try { 17 | response.setContentType("text/html; charset=utf-8"); 18 | response.getWriter().append("welcome!"); 19 | } catch (Exception e) { 20 | e.printStackTrace(); 21 | response.getWriter().append(e.getMessage()); 22 | } 23 | } 24 | 25 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { 26 | doGet(request, response); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/webapp/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Class-Path: 3 | 4 | -------------------------------------------------------------------------------- /src/main/webapp/README.md: -------------------------------------------------------------------------------- 1 | # open api demo (java ver.) 2 | java version "1.7.0_75" 3 | 4 | ## Getting Started 5 | 6 | 1..将工程clone到本地:```git clone https://github.com/ddtalk/HarleyCorp.git```,导入到IDE中,比如eclipse点击```File->import```导入到eclipse中 7 | 8 | 2.打开工程的Env.java文件,填入企业的CORP_ID和SECRET(CORP_ID和SECRET可以在企业OA后台找到) 9 | ``` 10 | public static final String CORP_ID = "your CORP_ID"; 11 | 12 | public static final String CORP_SECRET = "your CORP_SECRET"; 13 | ``` 14 | 15 | 16 | 17 | 3.部署工程 18 | 19 | 4.OA后台创建微应用,并把工程的首页地址填到微应用首页中。 20 | [如何创建微应用?](http://ddtalk.github.io/dingTalkDoc/#step-2-创建微应用) 21 | 22 | 23 | 24 | ###本DEMO具体实现 25 | 26 | 1.jsapi权限验证配置流程 27 | 28 | 请查看[文档](http://ddtalk.github.io/dingTalkDoc/#页面引入js文件) 29 | - 前端文件:WebContent/index.jsp,WebContent/javascripts/demo.js 30 | - 后端文件:[链接](https://github.com/injekt/openapi-demo-java/blob/master/src/com/alibaba/dingtalk/openapi/demo/auth/AuthHelper.java) 31 | 32 | 2.免登流程 33 | 34 | 请查看[文档](http://ddtalk.github.io/dingTalkDoc/#手机客户端微应用中调用免登) 35 | - 前端文件:WebContent/javascripts/demo.js和 36 | - 后端文件:[链接](https://github.com/injekt/openapi-demo-java/blob/master/src/com/alibaba/dingtalk/openapi/servlet/UserInfoServlet.java) 37 | 38 | 39 | 3.部门的操作 40 | 请查看[文档](http://ddtalk.github.io/dingTalkDoc/#管理通讯录) 41 | - 后端文件:[链接](https://github.com/injekt/openapi-demo-java/blob/master/src/com/alibaba/dingtalk/openapi/demo/department) 42 | 43 | 4.员工的操作 44 | 请查看[文档](http://ddtalk.github.io/dingTalkDoc/#管理通讯录) 45 | - 后端文件:[链接](https://github.com/injekt/openapi-demo-java/blob/master/src/com/alibaba/dingtalk/openapi/demo/user) 46 | 47 | 5.通讯录事件(比如用户的离职,部门的删除)回调 48 | 请查看[文档](http://ddtalk.github.io/dingTalkDoc/#通讯录及群会话变更事件回调接口录) 49 | - 后端文件:[链接](https://github.com/injekt/openapi-demo-java/blob/master/src/com/alibaba/dingtalk/openapi/servlet/EventChangeReceiveServlet.java) 50 | 51 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UserInfoServlet 5 | UserInfoServlet 6 | com.alibaba.dingtalk.openapi.servlet.UserInfoServlet 7 | 8 | 9 | ContactsServlet 10 | ContactsServlet 11 | com.alibaba.dingtalk.openapi.servlet.ContactsServlet 12 | 13 | 14 | OAoauth 15 | OAoauth 16 | com.alibaba.dingtalk.openapi.servlet.OAoauth 17 | 18 | 19 | OAoauth 20 | /OAoauth 21 | 22 | 23 | EventChangeReceiveServlet 24 | EventChangeReceiveServlet 25 | com.alibaba.dingtalk.openapi.servlet.EventChangeReceiveServlet 26 | 27 | 28 | WelcomeServlet 29 | WelcomeServlet 30 | com.alibaba.dingtalk.openapi.servlet.WelcomeServlet 31 | 32 | 33 | CallbackServlet 34 | CallbackServlet 35 | com.alibaba.dingtalk.openapi.servlet.CallbackServlet 36 | 37 | 38 | UserInfoServlet 39 | /userinfo 40 | 41 | 42 | ContactsServlet 43 | /contactsinfo 44 | 45 | 46 | EventChangeReceiveServlet 47 | /eventreceive 48 | 49 | 50 | WelcomeServlet 51 | /welcome 52 | 53 | 54 | CallbackServlet 55 | /callback 56 | 57 | -------------------------------------------------------------------------------- /src/main/webapp/contacts.jsp: -------------------------------------------------------------------------------- 1 | 2 | <%@ page language="java" import="java.util.*" contentType="text/html;charset=utf-8"%> 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 50 | 51 | 52 | 53 | Contact 54 | 95 | 96 | 98 | 100 | 103 | 115 | 116 | 117 | 118 | 119 |

企业通讯录

120 |
121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /src/main/webapp/drawer/css/base.css: -------------------------------------------------------------------------------- 1 | body { 2 | } 3 | 4 | a { 5 | color: #00B7FF; 6 | } 7 | 8 | button { 9 | width: 48%; 10 | height: 50px; 11 | font-size: 20px; 12 | margin-top: 5px; 13 | margin-left: 5px; 14 | border-radius: 10px; 15 | display: inline-block; 16 | float: left; 17 | } 18 | 19 | .clear-float { 20 | clear: both; 21 | } 22 | 23 | #log { 24 | padding-top: 10px; 25 | padding-left: 10px; 26 | } 27 | 28 | .log-i { 29 | color: green; 30 | } 31 | 32 | .log-e { 33 | color: red; 34 | } 35 | 36 | .tag { 37 | display: inline-block; 38 | width: 170px; 39 | } 40 | 41 | .api { 42 | display: inline-block; 43 | width: 300px; 44 | } 45 | 46 | .hidden { 47 | display: none; 48 | } 49 | 50 | .log-row { 51 | word-break: break-all;word-wrap: break-word; 52 | } -------------------------------------------------------------------------------- /src/main/webapp/drawer/drawer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | hahahaha 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 | 78 | 79 | 80 |

drawer test

81 |
82 |
83 |
84 |
85 | 86 | -------------------------------------------------------------------------------- /src/main/webapp/drawer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | drawer test 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 | 106 | 107 | 108 |

drawer test

109 |
110 | 111 |
112 |
113 | 114 | -------------------------------------------------------------------------------- /src/main/webapp/drawer/js/base.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by liqiao on 12/28/15. 3 | */ 4 | var log = { 5 | i: function(info) { 6 | add(info, 'i'); 7 | }, 8 | e: function(err) { 9 | add(err, 'e'); 10 | } 11 | }; 12 | 13 | function add(msg, level) { 14 | var row = document.createElement('div'); 15 | row.setAttribute('class', 'log-row log-' + level); 16 | row.innerHTML = msg; 17 | 18 | document.querySelector('#log').appendChild(row); 19 | } -------------------------------------------------------------------------------- /src/main/webapp/drawer/js/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by liqiao on 12/26/15. 3 | */ 4 | -------------------------------------------------------------------------------- /src/main/webapp/drawer/js/support.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by liqiao on 1/6/16. 3 | */ 4 | (function() { 5 | 6 | //constants 7 | var MSG_TYPE_REQ = 1; 8 | var MSG_TYPE_RES = 2; 9 | var MSG_TYPE_BACK = 3; 10 | 11 | var STATUS_LOAD_START = 0; 12 | var STATUS_LOAD_FINISH = 1; 13 | var STATUS_LOAD_ERROR = -1; 14 | 15 | //req callbacks 16 | var ReqIdGen = 0; 17 | var callbacks = {}; 18 | 19 | //global error handler 20 | var preloadingIds = {}; 21 | 22 | var processingGo = false; 23 | 24 | var isFirstRun = true; 25 | 26 | var drawerId; 27 | 28 | var frameId;//ios compat 29 | var preloadUrls = {}; //ios compat 30 | 31 | var fromId; 32 | 33 | var logger = { 34 | LEVEL: { 35 | INFO: 0, 36 | WARNING: 1 37 | }, 38 | level: 1, 39 | i: function(msg) { 40 | console.log(msg); 41 | }, 42 | w: function(msg) { 43 | if (this.level > this.LEVEL.INFO) { 44 | alert(msg); 45 | } 46 | else { 47 | this.i(msg); 48 | } 49 | } 50 | }; 51 | 52 | if (!window.onerror) { 53 | window.onerror = function (msg, url, line, column, errObj) { 54 | logger.w('Oops!\n' + msg + '\nhappened at ' + url + ' [line ' + line + ', column ' + column + ']') 55 | }; 56 | } 57 | 58 | 59 | document.addEventListener('runtimeready', function() { 60 | if (dd) dd.on = document.addEventListener; 61 | }); 62 | 63 | var errorHandler = function(err) { 64 | logger.w('[nav error] ' + JSON.stringify(err)); 65 | }; 66 | 67 | var invokeHandler = function(data, cb) { 68 | logger.i('[nav invoke] ' + JSON.stringify(data)); 69 | } 70 | 71 | var initNav = function(p) { 72 | checkEnv(); 73 | 74 | if (!p) throw 'parameter for init() is missing'; 75 | if (!p.id) throw 'id is missing'; 76 | 77 | if (p.id) frameId = p.id; 78 | if (p.onError && 'function' === p.onError) errorHandler = p.onError; 79 | if (p.onInvoke && 'function' === p.onInvoke) invokeHandler = p.onInvoke; 80 | 81 | var handleMessages = function(data) { 82 | if (data && data.length > 0) { 83 | var once = true; 84 | data.forEach(function (message) { 85 | if (message.from) { 86 | fromId = message.from; 87 | } 88 | if (message.content) { 89 | var msgType = message.content._type; 90 | if (!once && (msgType === MSG_TYPE_REQ || msgType === MSG_TYPE_RES)) { 91 | logger.w('Warning: more than one REQ or RES message are post to this frame: ' + 92 | JSON.stringify(message.content)); 93 | return; 94 | } 95 | if (MSG_TYPE_REQ === msgType) { 96 | once = false; 97 | var reqId = message.content._reqId; 98 | var handle = invokeHandler; 99 | var _h = message.content._handler; 100 | if (_h && 'string' === typeof _h && window[_h]) handle = window[_h]; 101 | 102 | handle(message.content._value, function (response) { 103 | var msgContent = { 104 | _type: MSG_TYPE_RES, 105 | _reqId: reqId, 106 | _value: response 107 | }; 108 | postMessage({ 109 | id: message.from, 110 | content: msgContent 111 | }); 112 | }); 113 | } 114 | else if (MSG_TYPE_RES === msgType) { 115 | once = false; 116 | var reqId = message.content._reqId; 117 | if (reqId !== undefined && callbacks[reqId]) { 118 | var callback = callbacks[reqId]; 119 | delete callbacks[reqId]; 120 | if (typeof callback === 'function') { 121 | callback(message.content._value); 122 | } 123 | } 124 | } 125 | else if (MSG_TYPE_BACK === msgType) { 126 | var targetId = message.content._target; 127 | if (frameId !== targetId) { 128 | dd.support.nav.close(); 129 | } 130 | } 131 | } 132 | }); 133 | } 134 | }; 135 | 136 | var resumeHandler = function(event) { 137 | if (shouldDowngrade()) { 138 | var data = compatMessageFetch(frameId); 139 | handleMessages(data); 140 | } 141 | else { 142 | dd.runtime.message.fetch({ 143 | onSuccess: function (data) { 144 | handleMessages(data); 145 | }, 146 | onFail: function (err) { 147 | console.log('resume handler fetching messages: ' + 148 | JSON.stringify(err)); 149 | } 150 | }); 151 | } 152 | }; 153 | 154 | /*for first run*/ 155 | setTimeout(resumeHandler, 0); 156 | //resumeHandler(); 157 | 158 | dd.on('resume', resumeHandler); 159 | }; 160 | 161 | var preload = function(p) { 162 | checkEnv(); 163 | if (!p) throw 'parameter for preload() is missing'; 164 | if (!p.pages) throw 'pages is missing'; 165 | if (!(p.pages instanceof Array)) throw 'invalid pages: not an array'; 166 | var ids = []; 167 | p.pages.forEach(function(page) { 168 | if (page.id && 'string' === typeof page.id) ids.push(page.id); 169 | }); 170 | 171 | ids.forEach(function(id) { 172 | var cnt = preloadingIds[p.id]; 173 | preloadingIds[id] = cnt !== undefined ? ++cnt : 0; 174 | if (cnt > 1) { 175 | logger.w('Warning: preload id[' + id + '] too frequently'); 176 | } 177 | }); 178 | 179 | var flag = true; 180 | 181 | if (shouldDowngrade()) { //ios compat 182 | 183 | p.pages.forEach(function(page) { 184 | if ((page.id && 'string' === typeof page.id) && 185 | (page.url && 'string' === typeof page.url)) { 186 | preloadUrls[page.id] = page.url; 187 | } 188 | }); 189 | 190 | if (p.onSuccess) { 191 | ids.forEach(function(id) { 192 | p.onSuccess({ 193 | id: id, 194 | status: STATUS_LOAD_START, 195 | extras: {} 196 | }); 197 | p.onSuccess({ 198 | id: id, 199 | status: STATUS_LOAD_FINISH, 200 | extras: {} 201 | }); 202 | }); 203 | } 204 | return; 205 | } 206 | 207 | dd.ui.nav.preload({ 208 | pages: p.pages, 209 | onSuccess: function(data) { 210 | if (flag && data && data.id && data.status === STATUS_LOAD_START) { 211 | flag = false; 212 | var cnt = preloadingIds[p.id]; 213 | if (cnt > 0) preloadingIds[p.id] = cnt - 1; 214 | } 215 | if (p.onSuccess) p.onSuccess(data); 216 | }, 217 | onFail: function(err) { 218 | ids.forEach(function(id) { 219 | var cnt = preloadingIds[p.id]; 220 | if (cnt > 0) preloadingIds[p.id] = cnt - 1; 221 | }); 222 | if (p.onFail) p.onFail(err); 223 | } 224 | }); 225 | }; 226 | 227 | var go = function(p) { 228 | checkEnv(); 229 | 230 | if (processingGo) { 231 | throw 'calling go() for id['+ p.id +'] at ' + location.href + ' too frequently'; 232 | } 233 | 234 | if (!p) throw 'parameter for go() is missing'; 235 | if (!p.id) throw 'id is missing'; 236 | if (p.handler && typeof p.handler !== 'string') throw 'handler is supposed to be a string'; 237 | var id = p.id; 238 | 239 | processingGo = true; 240 | 241 | var params = p.params || {}; 242 | var handler = p.handler; 243 | var win = p.onSuccess; 244 | var fail = function(err) { 245 | processingGo = false; 246 | var f = p.onFail ? p.onFail : errorHandler; 247 | f(err); 248 | }; 249 | 250 | var reqId = ReqIdGen++; 251 | if (callbacks[reqId]) throw 'fatal: reqId[' + reqId + '] is existed'; 252 | callbacks[reqId] = win; 253 | 254 | var args = { 255 | id: id, 256 | onSuccess: function() { 257 | processingGo = false; 258 | }, 259 | onFail: fail 260 | }; 261 | if (p.anim !== undefined) args.anim = p.anim; 262 | 263 | var msgContent = { 264 | _type: MSG_TYPE_REQ, 265 | _reqId: reqId, 266 | _handler: handler, 267 | _value: params 268 | }; 269 | 270 | if (shouldDowngrade()) { //ios compat 271 | var url = preloadUrls[id]; 272 | if (p.createIfNeeded) { 273 | if (!url && (!p.url || typeof p.url !== 'string')) { 274 | processingGo = false; 275 | throw 'url of go() is missing or invalid'; 276 | } 277 | 278 | url = p.url; 279 | } 280 | if (!url) { 281 | processingGo = false; 282 | throw 'missing url for go()'; 283 | } 284 | preloadUrls[id] = url; 285 | 286 | postMessage({ 287 | id: id, 288 | content: msgContent 289 | }); 290 | setTimeout(function() { 291 | processingGo = false; 292 | dd.biz.util.openLink({ 293 | url: url 294 | }); 295 | }, 0); 296 | } 297 | else { 298 | var postMessageAndGo = function () { 299 | postMessage({ 300 | id: id, 301 | content: msgContent, 302 | onSuccess: function () { 303 | dd.ui.nav.go(args); 304 | }, 305 | onFail: fail 306 | }); 307 | }; 308 | 309 | if (p.createIfNeeded) { 310 | if (!p.url || typeof p.url !== 'string') { 311 | processingGo = false; 312 | throw 'url of go() is missing or invalid'; 313 | } 314 | 315 | var flag = true; 316 | var timeout = true; 317 | 318 | preload({ 319 | pages: [{ 320 | id: id, 321 | url: p.url 322 | }], 323 | onSuccess: function (data) { 324 | if (flag && data && data.id === id && data.status === STATUS_LOAD_START) { 325 | flag = false; 326 | // /*black magic*/ 327 | // setTimeout(postMessageAndGo, 0); 328 | timeout = false; 329 | } 330 | }, 331 | onFail: fail 332 | }); 333 | 334 | setTimeout(function () { 335 | if (timeout) { 336 | logger.w('preload too long: ' + id); 337 | } 338 | postMessageAndGo(); 339 | }, 300); 340 | } 341 | else { 342 | postMessageAndGo(); 343 | } 344 | } 345 | }; 346 | 347 | var backTo = function(p) { 348 | 349 | }; 350 | 351 | /** 352 | * @param p 353 | * { 354 | * id: 'id', 355 | * params: {}, 356 | * handler: 'fn', 357 | * onSuccess: function(res) {}, 358 | * onFail: function(err) {} 359 | * } 360 | */ 361 | var call = function(p) { 362 | checkEnv(); 363 | 364 | if (!p) throw 'parameter for call() is missing'; 365 | if (!p.id) throw 'id is missing'; 366 | if (p.handler && typeof p.handler !== 'string') throw 'handler is supposed to be a string'; 367 | 368 | var id = p.id; 369 | var params = p.params || {}; 370 | var handler = p.handler; 371 | var win = p.onSuccess; 372 | var fail = function(err) { 373 | var f = p.onFail ? p.onFail : errorHandler; 374 | f(err); 375 | }; 376 | 377 | var reqId = ReqIdGen++; 378 | if (callbacks[reqId]) throw 'fatal: reqId[' + reqId + '] is existed'; 379 | callbacks[reqId] = win; 380 | 381 | var msgContent = { 382 | _type: MSG_TYPE_REQ, 383 | _reqId: reqId, 384 | _handler: handler, 385 | _value: params 386 | }; 387 | 388 | postMessage({ 389 | id: id, 390 | content: msgContent, 391 | onSuccess: function () { 392 | logger.i('call id[' + id + '] with params [' + JSON.stringify(params) + ']'); 393 | }, 394 | onFail: fail 395 | }); 396 | }; 397 | 398 | 399 | var initDrawer = function(p) { 400 | if (!p) throw 'parameter for init() is missing'; 401 | if (!p.id) throw 'id is missing'; 402 | 403 | drawerId = p.id; 404 | dd.ui.drawer.init(p); 405 | }; 406 | 407 | 408 | /** 409 | * @param p 410 | * { 411 | * params: {}, 412 | * handler: 'fn', 413 | * onSuccess: function(res) {}, 414 | * onFail: function(err) {} 415 | * } 416 | */ 417 | var openDrawer = function(p) { 418 | checkEnv(); 419 | 420 | if (!drawerId) throw 'drawer id is undefined. Has drawer been initialized?'; 421 | if (!p) p = {}; 422 | p.id = drawerId; 423 | 424 | call(p); 425 | 426 | dd.ui.drawer.open({ 427 | onSuccess: function() {}, 428 | onFail: p.onFail ? p.onFail : errorHandler 429 | }); 430 | }; 431 | 432 | 433 | function checkEnv() { 434 | if (!window.nuva) throw 'dd is not ready'; 435 | if (!dd.ios && !dd.android) throw 'env is not dd.android nor dd.ios'; 436 | } 437 | 438 | 439 | function postMessage(p) { 440 | if (!p) throw 'missing paramete for postMessage()'; 441 | if (!p.id) throw 'missing id for postMessage()'; 442 | 443 | var id = p.id; 444 | var content = p.content; 445 | 446 | if (shouldDowngrade()) { 447 | compatMessagePost(id, content); 448 | } 449 | else { 450 | dd.runtime.message.post({ 451 | to: [id], 452 | content: content, 453 | onSuccess: p.onSuccess || function () {}, 454 | onFail: p.onFail || errorHandler 455 | }); 456 | } 457 | } 458 | 459 | 460 | var STORAGE_MESSAGE_KEY = '__message__5'; 461 | 462 | function compatMessagePost(id, content) { 463 | if (!window.localStorage) alert('localStorage is not supported'); 464 | if (!id) throw 'post message with an undefined id'; 465 | if (!frameId) throw 'thie frame is not initialized with a frame id'; 466 | 467 | var m = localStorage[STORAGE_MESSAGE_KEY]; 468 | if (!m) m = JSON.stringify({}); 469 | 470 | var messages = JSON.parse(m); 471 | 472 | var msgList = messages[id]; 473 | if (!msgList) msgList = []; 474 | 475 | msgList.push({from: frameId, content: content}); 476 | messages[id] = msgList; 477 | 478 | localStorage[STORAGE_MESSAGE_KEY] = JSON.stringify(messages); 479 | 480 | } 481 | 482 | function compatMessageFetch(id) { 483 | if (!window.localStorage) alert('localStorage is not supported'); 484 | if (!id) throw 'fetch message with an undefined id'; 485 | 486 | var m = localStorage[STORAGE_MESSAGE_KEY]; 487 | if (!m) m = JSON.stringify({}); 488 | 489 | var messages = JSON.parse(m); 490 | var msgList = messages[id]; 491 | 492 | messages[id] = []; 493 | localStorage[STORAGE_MESSAGE_KEY] = JSON.stringify(messages); 494 | return msgList || []; 495 | } 496 | 497 | /** 498 | * compareVersion: function(oldVersion, newVersion, containEqual) 499 | * 500 | * oldVersion 老版本 501 | * newVersion 新版本 502 | * containEqual 是否包含相等的情况 503 | * result: newVersion >[=] oldVersion 504 | **/ 505 | function shouldDowngrade() { 506 | return dd.ios && dd.compareVersion(dd.version, '2.7.0', true); 507 | } 508 | 509 | 510 | if (dd.support) throw 'dd.support is already defined'; 511 | 512 | dd.support = {}; 513 | 514 | dd.support.logger = logger; 515 | 516 | dd.support.message = { 517 | setKey: function(key) { 518 | if (shouldDowngrade()) { 519 | STORAGE_MESSAGE_KEY = key; 520 | } 521 | }, 522 | clear: function() { 523 | if (shouldDowngrade() && window.localStorage) { 524 | localStorage[STORAGE_MESSAGE_KEY] = JSON.stringify({}); 525 | } 526 | } 527 | }; 528 | 529 | dd.support.call = call; 530 | 531 | dd.support.nav = { 532 | init: initNav, 533 | preload: preload, 534 | go: go, 535 | recycle: dd.ui.nav.recycle, 536 | close: dd.ui.nav.close, 537 | backTo: backTo, 538 | getCurrentId: dd.ui.nav.getCurrentId 539 | }; 540 | 541 | dd.support.drawer = { 542 | init: initDrawer, 543 | config: dd.ui.drawer.config, 544 | enable: dd.ui.drawer.enable, 545 | disable: dd.ui.drawer.disable, 546 | open: openDrawer, 547 | close: dd.ui.drawer.close, 548 | }; 549 | }) (); -------------------------------------------------------------------------------- /src/main/webapp/index.jsp: -------------------------------------------------------------------------------- 1 | 2 | <%@ page language="java" import="java.util.*" contentType="text/html;charset=utf-8"%> 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 50 | 51 | 52 | 53 | 企业开发者首页 54 | 60 | 61 | 63 | 65 | // 免登相关代码 66 | 69 | 73 | 74 | 75 | 76 | 77 |
78 | 头像 79 |
80 |
81 | UserName: 82 |
83 |
84 |
85 | UserId: 86 |
87 |
88 |
89 | 91 |
    我们为您提供了文档+api帮助您快速的开发微应用并接入钉钉。
92 |
93 |
    94 |
  • 95 |
    96 |
    企业接入指南
    97 |
  • 98 | 106 |
  • 107 |
    108 |
    使用JSAPI
    109 |
  • 110 |
  • 111 |
    112 |
    List展示(当前仅支持Android)
    113 |
  • 114 |
  • 115 |
    116 |
    侧拉展现(当前仅支持Android)
    117 |
  • 118 |
  • 119 |
    120 |
    Tab页面(当前仅支持Android)
    121 |
  • 122 |
  • 123 |
    124 |
    企业通讯录
    125 |
  • 126 |
127 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /src/main/webapp/indexPC.jsp: -------------------------------------------------------------------------------- 1 | 2 | <%@ page language="java" import="java.util.*" contentType="text/html;charset=utf-8"%> 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 50 | 51 | 52 | 53 | 企业开发者首页 54 | 60 | 61 | 63 | 65 | 68 | 72 | 73 | 74 | 75 | 76 | 钉钉PC版接入 77 |
78 | 头像 79 |
80 |
81 | UserName: 82 |
83 |
84 |
85 | UserId: 86 |
87 |
88 |
89 | 91 |
    我们为您提供了文档+api帮助您快速的开发微应用并接入钉钉。
92 |
93 |
    94 |
  • 95 |
    96 |
    企业接入指南
    97 |
  • 98 | 106 |
  • 107 |
    108 |
    使用JSAPI
    109 |
  • 110 |
  • 111 |
    112 |
    List展示(当前仅支持Android)
    113 |
  • 114 |
  • 115 |
    116 |
    侧拉展现(当前仅支持Android)
    117 |
  • 118 |
  • 119 |
    120 |
    Tab页面(当前仅支持Android)
    121 |
  • 122 |
  • 123 |
    124 |
    企业通讯录
    125 |
  • 126 |
127 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /src/main/webapp/javascripts/contacts.js: -------------------------------------------------------------------------------- 1 | //dd.config({ 2 | // agentId : _config.agentid, 3 | // corpId : _config.corpId, 4 | // timeStamp : _config.timeStamp, 5 | // nonceStr : _config.nonceStr, 6 | // signature : _config.signature, 7 | // jsApiList : [ 'runtime.info', 'biz.contact.choose', 8 | // 'device.notification.confirm', 'device.notification.alert', 9 | // 'device.notification.prompt', 'biz.ding.post', 10 | // 'biz.util.openLink' ] 11 | // }); 12 | 13 | $.ajax({ 14 | url : 'contactsinfo?corpid='+ _config.corpId, 15 | type : 'GET', 16 | success : function(data, status, xhr) { 17 | var json = data; 18 | // alert('data:'+json); 19 | 20 | var jj = eval("(" + json + ")");; 21 | var show =""; 22 | for(var i=0; i' 24 | for(var j=0;j< jj.department[i].member.length;j++){ 25 | show += '
'+jj.department[i].member[j].name+'
'; 26 | } 27 | show+='
'; 28 | show+='
'; 29 | } 30 | // alert("div:"+show); 31 | 32 | document.getElementById("contactId").innerHTML = show; 33 | 34 | }, 35 | error : function(xhr, errorType, error) { 36 | logger.e("yinyien:" + _config.corpId); 37 | alert(errorType + ', ' + error); 38 | } 39 | }); -------------------------------------------------------------------------------- /src/main/webapp/javascripts/demo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by liqiao on 8/10/15. 3 | */ 4 | 5 | /** 6 | * _config comes from server-side template. see views/index.jade 7 | */ 8 | dd.config({ 9 | agentId : _config.agentId, 10 | corpId : _config.corpId, 11 | timeStamp : _config.timeStamp, 12 | nonceStr : _config.nonceStr, 13 | signature : _config.signature, 14 | jsApiList : [ 'runtime.info', 'biz.contact.choose', 15 | 'device.notification.confirm', 'device.notification.alert', 16 | 'device.notification.prompt', 'biz.ding.post', 17 | 'biz.util.openLink' ] 18 | }); 19 | 20 | 21 | dd.ready(function() { 22 | dd.biz.navigation.setTitle({ 23 | title: '钉钉demo', 24 | onSuccess: function(data) { 25 | }, 26 | onFail: function(err) { 27 | log.e(JSON.stringify(err)); 28 | } 29 | }); 30 | // alert('dd.ready rocks!'); 31 | 32 | dd.runtime.info({ 33 | onSuccess : function(info) { 34 | logger.e('runtime info: ' + JSON.stringify(info)); 35 | }, 36 | onFail : function(err) { 37 | logger.e('fail: ' + JSON.stringify(err)); 38 | } 39 | }); 40 | dd.ui.pullToRefresh.enable({ 41 | onSuccess: function() { 42 | }, 43 | onFail: function() { 44 | } 45 | }) 46 | 47 | dd.biz.navigation.setMenu({ 48 | backgroundColor : "#ADD8E6", 49 | items : [ 50 | { 51 | id:"此处可以设置帮助",//字符串 52 | // "iconId":"file",//字符串,图标命名 53 | text:"帮助" 54 | } 55 | , 56 | { 57 | "id":"2", 58 | "iconId":"photo", 59 | "text":"我们" 60 | } 61 | , 62 | { 63 | "id":"3", 64 | "iconId":"file", 65 | "text":"你们" 66 | } 67 | , 68 | { 69 | "id":"4", 70 | "iconId":"time", 71 | "text":"他们" 72 | } 73 | ], 74 | onSuccess: function(data) { 75 | alert(JSON.stringify(data)); 76 | 77 | }, 78 | onFail: function(err) { 79 | alert(JSON.stringify(err)); 80 | } 81 | }); 82 | 83 | 84 | dd.runtime.permission.requestAuthCode({ 85 | corpId : _config.corpId, 86 | onSuccess : function(info) { 87 | // alert('authcode: ' + info.code); 88 | $.ajax({ 89 | url : 'userinfo?code=' + info.code + '&corpid=' 90 | + _config.corpId, 91 | type : 'GET', 92 | success : function(data, status, xhr) { 93 | alert(data); 94 | var info = JSON.parse(data); 95 | 96 | document.getElementById("userName").innerHTML = info.name; 97 | document.getElementById("userId").innerHTML = info.userid; 98 | 99 | // 图片 100 | if(info.avatar.length != 0){ 101 | var img = document.getElementById("userImg"); 102 | img.src = info.avatar; 103 | img.height = '100'; 104 | img.width = '100'; 105 | } 106 | 107 | }, 108 | error : function(xhr, errorType, error) { 109 | logger.e("yinyien:" + _config.corpId); 110 | alert(errorType + ', ' + error); 111 | } 112 | }); 113 | 114 | }, 115 | onFail : function(err) { 116 | alert('fail: ' + JSON.stringify(err)); 117 | } 118 | }); 119 | }); 120 | 121 | dd.error(function(err) { 122 | alert('dd error: ' + JSON.stringify(err)); 123 | }); 124 | -------------------------------------------------------------------------------- /src/main/webapp/javascripts/demoPC.js: -------------------------------------------------------------------------------- 1 | DingTalkPC.config({ 2 | agentId : _config.agentId, 3 | corpId : _config.corpId, 4 | timeStamp : _config.timeStamp, 5 | nonceStr : _config.nonceStr, 6 | signature : _config.signature, 7 | jsApiList : [ 'runtime.info', 'biz.contact.choose', 8 | 'device.notification.confirm', 'device.notification.alert', 9 | 'device.notification.prompt', 'biz.ding.post', 10 | 'biz.util.openLink' ] 11 | }); 12 | 13 | 14 | DingTalkPC.ready(function() { 15 | DingTalkPC.biz.navigation.setTitle({ 16 | title: '钉钉demo', 17 | onSuccess: function(data) { 18 | }, 19 | onFail: function(err) { 20 | log.e(JSON.stringify(err)); 21 | } 22 | }); 23 | 24 | DingTalkPC.runtime.info({ 25 | onSuccess : function(info) { 26 | logger.e('runtime info: ' + JSON.stringify(info)); 27 | }, 28 | onFail : function(err) { 29 | logger.e('fail: ' + JSON.stringify(err)); 30 | } 31 | }); 32 | DingTalkPC.ui.pullToRefresh.enable({ 33 | onSuccess: function() { 34 | }, 35 | onFail: function() { 36 | } 37 | }) 38 | 39 | 40 | DingTalkPC.runtime.permission.requestAuthCode({ 41 | corpId : _config.corpId, 42 | onSuccess : function(info) { 43 | // alert('authcode: ' + info.code); 44 | $.ajax({ 45 | url : 'userinfo?code=' + info.code + '&corpid=' 46 | + _config.corpId, 47 | type : 'GET', 48 | success : function(data, status, xhr) { 49 | alert(data); 50 | var info = JSON.parse(data); 51 | 52 | document.getElementById("userName").innerHTML = info.name; 53 | document.getElementById("userId").innerHTML = info.userid; 54 | 55 | // 图片 56 | if(info.avatar.length != 0){ 57 | var img = document.getElementById("userImg"); 58 | img.src = info.avatar; 59 | img.height = '100'; 60 | img.width = '100'; 61 | } 62 | 63 | }, 64 | error : function(xhr, errorType, error) { 65 | logger.e("yinyien:" + _config.corpId); 66 | alert(errorType + ', ' + error); 67 | } 68 | }); 69 | 70 | }, 71 | onFail : function(err) { 72 | alert('fail: ' + JSON.stringify(err)); 73 | } 74 | }); 75 | }); 76 | 77 | DingTalkPC.error(function(err) { 78 | alert('dd error: ' + JSON.stringify(err)); 79 | }); 80 | -------------------------------------------------------------------------------- /src/main/webapp/javascripts/logger.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by liqiao on 8/14/15. 3 | */ 4 | 5 | var log = document.createElement('div'); 6 | log.setAttribute('id', 'log'); 7 | document.body.appendChild(log); 8 | 9 | var logger = { 10 | i: function(info) { 11 | add(info, 'i'); 12 | }, 13 | e: function(err) { 14 | add(err, 'e'); 15 | } 16 | }; 17 | 18 | function add(msg, level) { 19 | var row = document.createElement('div'); 20 | row.setAttribute('class', 'log-row log-' + level); 21 | row.innerHTML = msg; 22 | 23 | document.querySelector('#log').appendChild(row); 24 | } -------------------------------------------------------------------------------- /src/main/webapp/list/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/src/main/webapp/list/1.jpg -------------------------------------------------------------------------------- /src/main/webapp/list/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/src/main/webapp/list/2.jpg -------------------------------------------------------------------------------- /src/main/webapp/list/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/src/main/webapp/list/3.jpg -------------------------------------------------------------------------------- /src/main/webapp/list/detail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | List 7 | 43 | 44 | 45 | 47 | 48 | 92 | 93 | -------------------------------------------------------------------------------- /src/main/webapp/list/detail2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | List 7 | 56 | 57 | 58 |
59 |
sdasfsdfsd
60 | 61 | 84 | 85 | -------------------------------------------------------------------------------- /src/main/webapp/list/list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | List 6 | 38 | 39 | 40 | 41 |
    42 |
  • 43 |
    44 |
    asdasdasdasdasdasdasdasdas
    45 |
  • 46 |
  • 47 |
    48 |
    asdasd
    49 |
  • 50 |
  • 51 |
    52 |
    asdasd
    53 |
  • 54 |
  • 55 |
    56 |
    asdasdasdasdasdasdasdasdas
    57 |
  • 58 |
  • 59 |
    60 |
    asdasd
    61 |
  • 62 |
  • 63 |
    64 |
    asdasd
    65 |
  • 66 |
  • 67 |
    68 |
    asdasd
    69 |
  • 70 |
71 | 148 | 149 | -------------------------------------------------------------------------------- /src/main/webapp/list/num1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/src/main/webapp/list/num1.png -------------------------------------------------------------------------------- /src/main/webapp/list/num11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/src/main/webapp/list/num11.png -------------------------------------------------------------------------------- /src/main/webapp/list/num2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/src/main/webapp/list/num2.png -------------------------------------------------------------------------------- /src/main/webapp/list/num3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/src/main/webapp/list/num3.png -------------------------------------------------------------------------------- /src/main/webapp/list/num33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/src/main/webapp/list/num33.png -------------------------------------------------------------------------------- /src/main/webapp/list/num4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/src/main/webapp/list/num4.png -------------------------------------------------------------------------------- /src/main/webapp/list/num5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/src/main/webapp/list/num5.png -------------------------------------------------------------------------------- /src/main/webapp/list/num6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/src/main/webapp/list/num6.png -------------------------------------------------------------------------------- /src/main/webapp/list/num7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/src/main/webapp/list/num7.png -------------------------------------------------------------------------------- /src/main/webapp/list/num8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/src/main/webapp/list/num8.png -------------------------------------------------------------------------------- /src/main/webapp/list/num9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/src/main/webapp/list/num9.png -------------------------------------------------------------------------------- /src/main/webapp/nav/1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Nav 1 6 | 7 | 8 | 13 | 14 | 16 | 196 | 197 | 198 |

Nav 1

199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 | 207 | 213 | -------------------------------------------------------------------------------- /src/main/webapp/nav/2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Nav 2 6 | 7 | 8 | 13 | 14 | 15 | 179 | 180 | 181 |

Nav 1

182 |
183 |
184 |
185 |
186 |
187 | 188 | -------------------------------------------------------------------------------- /src/main/webapp/nav/3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Nav 3 6 | 7 | 8 | 9 | 10 | 144 | 145 | 146 |

Nav 3

147 |
148 |
149 |
150 | 151 | -------------------------------------------------------------------------------- /src/main/webapp/nav/4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Nav 4 7 | 13 | 14 | 29 | 30 | 31 |

Nav 4

32 | 33 | -------------------------------------------------------------------------------- /src/main/webapp/nav/5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Nav 5 7 | 13 | 14 | 59 | 60 | 61 |

Nav 5

62 |

preload baidu, 测试在百度中发生url跳转后,点返回键能正确返回

63 |
64 |
65 | 66 | -------------------------------------------------------------------------------- /src/main/webapp/nav/6.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Nav 1 7 | 13 | 14 | 63 | 64 | 65 |

Nav 1

66 |

preload 2, 3

67 |
68 |
69 |
70 | 71 | -------------------------------------------------------------------------------- /src/main/webapp/nav/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/src/main/webapp/nav/default.png -------------------------------------------------------------------------------- /src/main/webapp/nav/javascripts/base.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by liqiao on 10/16/15. 3 | */ 4 | 5 | var log = { 6 | i: function(info) { 7 | add(info, 'i'); 8 | }, 9 | e: function(err) { 10 | add(err, 'e'); 11 | } 12 | }; 13 | 14 | function add(msg, level) { 15 | var row = document.createElement('div'); 16 | row.setAttribute('class', 'log-row log-' + level); 17 | row.innerHTML = msg; 18 | 19 | document.querySelector('#log').appendChild(row); 20 | } -------------------------------------------------------------------------------- /src/main/webapp/nav/log.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/main/webapp/nav/stylesheets/base.css: -------------------------------------------------------------------------------- 1 | body { 2 | } 3 | 4 | a { 5 | color: #00B7FF; 6 | } 7 | 8 | button { 9 | width: 48%; 10 | height: 50px; 11 | font-size: 20px; 12 | margin-top: 5px; 13 | margin-left: 5px; 14 | border-radius: 10px; 15 | display: inline-block; 16 | float: left; 17 | } 18 | 19 | .clear-float { 20 | clear: both; 21 | } 22 | 23 | #log { 24 | padding-top: 10px; 25 | padding-left: 10px; 26 | } 27 | 28 | .log-i { 29 | color: green; 30 | } 31 | 32 | .log-e { 33 | color: red; 34 | } 35 | 36 | .tag { 37 | display: inline-block; 38 | width: 170px; 39 | } 40 | 41 | .api { 42 | display: inline-block; 43 | width: 300px; 44 | } 45 | 46 | .hidden { 47 | display: none; 48 | } 49 | 50 | .log-row { 51 | word-break: break-all;word-wrap: break-word; 52 | } -------------------------------------------------------------------------------- /src/main/webapp/pic/comp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/src/main/webapp/pic/comp.png -------------------------------------------------------------------------------- /src/main/webapp/pic/isv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-java/443e4a9fbd9c6ffda2e2253d82eee1f9380b0610/src/main/webapp/pic/isv.png -------------------------------------------------------------------------------- /src/main/webapp/preIndex.jsp: -------------------------------------------------------------------------------- 1 | 2 | <%@ page language="java" import="java.util.*" contentType="text/html;charset=utf-8"%> 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 哈雷 13 | 14 | 195 | 196 | 197 | 198 | 199 | 200 | 241 | 247 | 248 | 249 |
250 |
251 | 254 | 255 |
256 |
257 |
258 | 259 |
 我是企业开发者
260 |
261 |
262 | 263 |
264 | 267 | 268 | 269 | 270 | 271 | 272 | -------------------------------------------------------------------------------- /src/main/webapp/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | } 3 | 4 | a { 5 | color: #00B7FF; 6 | } 7 | 8 | button { 9 | width: 48%; 10 | height: 50px; 11 | font-size: 20px; 12 | margin-top: 5px; 13 | margin-left: 5px; 14 | border-radius: 10px; 15 | display: inline-block; 16 | float: left; 17 | } 18 | 19 | .clear-float { 20 | clear: both; 21 | } 22 | 23 | #log { 24 | padding-top: 10px; 25 | padding-left: 10px; 26 | } 27 | 28 | .log-i { 29 | color: green; 30 | } 31 | 32 | .log-e { 33 | color: red; 34 | } 35 | 36 | .tag { 37 | display: inline-block; 38 | width: 170px; 39 | } 40 | 41 | .api { 42 | display: inline-block; 43 | width: 300px; 44 | } 45 | 46 | .hidden { 47 | display: none; 48 | } 49 | 50 | .log-row { 51 | word-break: break-all;word-wrap: break-word; 52 | } -------------------------------------------------------------------------------- /src/main/webapp/tab/css/base.css: -------------------------------------------------------------------------------- 1 | body { 2 | } 3 | 4 | a { 5 | color: #00B7FF; 6 | } 7 | 8 | button { 9 | width: 48%; 10 | height: 50px; 11 | font-size: 20px; 12 | margin-top: 5px; 13 | margin-left: 5px; 14 | border-radius: 10px; 15 | display: inline-block; 16 | float: left; 17 | } 18 | 19 | .clear-float { 20 | clear: both; 21 | } 22 | 23 | #log { 24 | padding-top: 10px; 25 | padding-left: 10px; 26 | } 27 | 28 | .log-i { 29 | color: green; 30 | } 31 | 32 | .log-e { 33 | color: red; 34 | } 35 | 36 | .tag { 37 | display: inline-block; 38 | width: 170px; 39 | } 40 | 41 | .api { 42 | display: inline-block; 43 | width: 300px; 44 | } 45 | 46 | .hidden { 47 | display: none; 48 | } 49 | 50 | .log-row { 51 | word-break: break-all;word-wrap: break-word; 52 | } -------------------------------------------------------------------------------- /src/main/webapp/tab/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tab test 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 122 | 123 | 124 |

tab test

125 |
126 |
127 |
128 |
129 | 130 | -------------------------------------------------------------------------------- /src/main/webapp/tab/js/base.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by liqiao on 12/28/15. 3 | */ 4 | var log = { 5 | i: function(info) { 6 | add(info, 'i'); 7 | }, 8 | e: function(err) { 9 | add(err, 'e'); 10 | } 11 | }; 12 | 13 | function add(msg, level) { 14 | var row = document.createElement('div'); 15 | row.setAttribute('class', 'log-row log-' + level); 16 | row.innerHTML = msg; 17 | 18 | document.querySelector('#log').appendChild(row); 19 | } -------------------------------------------------------------------------------- /src/main/webapp/tab/js/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by liqiao on 12/26/15. 3 | */ 4 | -------------------------------------------------------------------------------- /src/main/webapp/tab/tab1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tab 1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 36 | 37 | 38 |

tab 1

39 | 40 |
41 |
42 | 43 | -------------------------------------------------------------------------------- /src/main/webapp/tab/tab2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tab 2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 70 | 71 | 72 |

tab 2

73 | 74 |
75 |
76 | 77 |
78 |
79 | 80 | -------------------------------------------------------------------------------- /src/main/webapp/tab/tab3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tab 3 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 30 | 31 | 32 |

tab 3

33 | 34 |
35 |
36 | 37 | --------------------------------------------------------------------------------