丽知:新增:u8c对接钉钉

This commit is contained in:
zhengyf 2024-10-22 18:16:53 +08:00
parent 4426946bdb
commit c51f59b8ca
18 changed files with 1694 additions and 5 deletions

View File

@ -0,0 +1,44 @@
package com.hzya.frame.plugin.lets.dingtalk.callback;
import com.alibaba.fastjson.JSONObject;
import com.hzya.frame.plugin.lets.dingtalk.callback.service.CallBackService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Map;
/**
* 钉钉回调u8c接口
*/
@Controller("ding_talk_call_back")
@RequestMapping("/ding_talk_call_back")
public class CallBackController {
protected Logger logger = LogManager.getLogger(CallBackController.class);
@Autowired
private CallBackService callBackService;
@RequestMapping(value = "res")
@ResponseBody
public Map<String, String> res(@RequestParam(value = "msg_signature", required = false) String msg_signature,
@RequestParam(value = "timestamp", required = false) String timeStamp,
@RequestParam(value = "nonce", required = false) String nonce,
@RequestBody(required = false) JSONObject json) {
try {
logger.info("=============================================================================================================================钉钉回调执行快开始》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》");
Map<String, String> map = callBackService.saveDingTalkCallBackRes(msg_signature, timeStamp, nonce, json);
logger.info("=============================================================================================================================钉钉回调执行结束《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《");
return map;
} catch (Exception e) {
logger.error("execute", e);
}
return null;
}
}

View File

@ -0,0 +1,9 @@
package com.hzya.frame.plugin.lets.dingtalk.callback.service;
import com.alibaba.fastjson.JSONObject;
import java.util.Map;
public interface CallBackService {
Map<String, String> saveDingTalkCallBackRes(String msg_signature, String timeStamp, String nonce, JSONObject json);
}

View File

@ -0,0 +1,67 @@
package com.hzya.frame.plugin.lets.dingtalk.callback.service.impl;
import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hzya.frame.plugin.lets.dingtalk.callback.service.CallBackService;
import com.hzya.frame.plugin.lets.dingtalk.vo.DingCallbackCrypto;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service("callBackService")
public class CallBackServiceImpl implements CallBackService {
protected static Logger logger = LogManager.getLogger(CallBackServiceImpl.class);
@Value("${DING.call_back_token}")
private String token;
@Value("${DING.call_back_aes_key}")
private String aes_key;
@Value("${DING.APPKEY}")
private String corpId;
@Override
public Map<String, String> saveDingTalkCallBackRes(String msg_signature, String timeStamp, String nonce, JSONObject json) {
logger.info("=============================================================================================================================钉钉回调执行快开始》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》");
try {
// 1. 从http请求中获取加解密参数
// 2. 使用加解密类型
// 2调用订阅事件接口订阅的事件为企业级事件推送此时OWNER_KEY为开发者后台应用的Client ID原企业内部应用 appKey )
//tokenase_key均为钉钉开放平台后台管理应用设置的事件订阅,http推送中设置的参数
DingCallbackCrypto callbackCrypto = new DingCallbackCrypto(token, aes_key, corpId);
String encryptMsg = json.getString("encrypt");
String decryptMsg = callbackCrypto.getDecryptMsg(msg_signature, timeStamp, nonce, encryptMsg);
// 3. 反序列化回调事件json数据
JSONObject eventJson = JSON.parseObject(decryptMsg);
String eventType = eventJson.getString("EventType");
// 4. 根据EventType分类处理
if ("check_url".equals(eventType)) {
// 测试回调url的正确性
logger.info("测试回调url的正确性");
} else if ("bpms_instance_change".equals(eventType)) {
// 处理通讯录用户增加事件
logger.info("发生了:" + eventType + "事件");
} else {
// 添加其他已注册的
logger.info("发生了:" + eventType + "事件");
}
// 5. 返回success的加密数据
Map<String, String> successMap = callbackCrypto.getEncryptedMap("success");
logger.info("=============================================================================================================================钉钉回调执行结束《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《");
return successMap;
}catch (Exception e){
logger.error("execute", e);
Assert.state(false, "excute:{}", e);
}
return null;
}
}

View File

@ -0,0 +1,49 @@
package com.hzya.frame.plugin.lets.dingtalk.push;
import com.hzya.frame.plugin.lets.dingtalk.push.serivce.PushU8CService;
import com.hzya.frame.plugin.lets.dingtalk.vo.DjzbVO;
import com.hzya.frame.web.action.DefaultController;
import com.hzya.frame.web.entity.JsonResultEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* u8c单据推送钉钉表单
*/
@RestController
@RequestMapping("/letsPush")
public class PushController extends DefaultController {
@Autowired
private PushU8CService pushU8CService;
/** 委外采购(付款单)->采购付款申请(新) **/
@RequestMapping(value = "U8COutSourceBill")
@ResponseBody
public JsonResultEntity pushU8COutSourceBill(DjzbVO djzbVO, List<String> fileBase64s){
pushU8CService.outSource(djzbVO,fileBase64s);
System.out.println("com.hzya.frame.plugin.lets.dingtalk.push.pushController.pushU8COutSourceBill");
return getSuccessMessageEntity("112233445566778899");
}
/** 代理采购(付款单)->采购付款申请(店群专用) **/
@RequestMapping(value = "U8CAgencyBill")
@ResponseBody
public JsonResultEntity pushU8CAgencyBill(DjzbVO djzbVO, List<String> fileBase64s){
System.out.println("com.hzya.frame.plugin.lets.dingtalk.push.pushController.pushU8CAgencyBill");
return getSuccessMessageEntity("112233445566778899");
}
/** 调拨单内部交易(付款单)->特殊业务处理(新) **/
@RequestMapping(value = "U8CTransferBill")
@ResponseBody
public JsonResultEntity pushU8CTransferBill(DjzbVO djzbVO, List<String> fileBase64s){
System.out.println("com.hzya.frame.plugin.lets.dingtalk.push.pushController.pushU8CTransferBill");
return getSuccessMessageEntity("112233445566778899");
}
}

View File

@ -0,0 +1,14 @@
package com.hzya.frame.plugin.lets.dingtalk.push.serivce;
import com.hzya.frame.plugin.lets.dingtalk.vo.DjzbVO;
import java.util.List;
public interface PushU8CService {
/** 委外采购(付款单)->采购付款申请(新) **/
public String outSource(DjzbVO djzbVO, List<String> fileBase64s);
/** 代理采购(付款单)->采购付款申请(店群专用) **/
public String agency(DjzbVO djzbVO, List<String> fileBase64s);
/** 调拨单内部交易(付款单)->特殊业务处理(新) **/
public String transfer(DjzbVO djzbVO, List<String> fileBase64s);
}

View File

@ -0,0 +1,66 @@
package com.hzya.frame.plugin.lets.dingtalk.push.serivce.impl;
import cn.hutool.core.lang.Assert;
import com.hzya.frame.plugin.lets.dingtalk.push.serivce.PushU8CService;
import com.hzya.frame.plugin.lets.dingtalk.vo.DjzbVO;
import org.springframework.stereotype.Service;
import java.io.File;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
/**
* u8c单据推送钉钉表单
* fileBase64s为文件转了base64之后
*/
@Service("PushU8CService")
public class PushU8CServiceImpl implements PushU8CService {
/** 委外采购(付款单)->采购付款申请(新) */
@Override
public String outSource(DjzbVO djzbVO, List<String> fileBase64s) {
checkParameter(djzbVO,fileBase64s);
return null;
}
/** 代理采购(付款单)->采购付款申请(店群专用) */
@Override
public String agency(DjzbVO djzbVO, List<String> fileBase64s) {
checkParameter(djzbVO,fileBase64s);
return null;
}
/** 调拨单内部交易(付款单)->特殊业务处理(新) */
@Override
public String transfer(DjzbVO djzbVO, List<String> fileBase64s) {
checkParameter(djzbVO,fileBase64s);
return null;
}
/**
* 检查参数VO不能为空文件不能空
* @param djzbVO
* @param fileBase64s
*/
public void checkParameter(DjzbVO djzbVO, List<String> fileBase64s){
Assert.notNull(djzbVO,"u8c单据推送钉钉表单,检查参数VO不能为空");
if (fileBase64s.size() == 0) {
Assert.state(false,"u8c单据推送钉钉表单,检查参数,文件不能空");
}
}
/**
* 文件转换
* 将base64转为文件上传钉盘并返回钉钉的 List<FileModuleVO>
* @return
*/
public List<File> baseTransformFile(List<String> fileBase64s){
List<File> fileList=new ArrayList<>();
for (String fileBase64 : fileBase64s) {
// 解码Base64字符串
byte[] decodedBytes = Base64.getDecoder().decode(fileBase64);
}
return fileList;
}
}

View File

@ -0,0 +1,562 @@
package com.hzya.frame.plugin.lets.dingtalk.utils;
import cn.hutool.core.lang.Assert;
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenResponse;
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenResponseBody;
import com.aliyun.dingtalkstorage_1_0.models.AddPermissionResponse;
import com.aliyun.dingtalkstorage_1_0.models.CommitFileRequest;
import com.aliyun.dingtalkstorage_1_0.models.CommitFileResponse;
import com.aliyun.dingtalkstorage_1_0.models.GetFileUploadInfoResponse;
import com.aliyun.dingtalkworkflow_1_0.models.*;
import com.aliyun.tea.TeaException;
import com.aliyun.teautil.models.RuntimeOptions;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.*;
import com.dingtalk.api.response.*;
import com.taobao.api.ApiException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.List;
public class DingTalkUtils {
protected static Logger logger = LogManager.getLogger(DingTalkUtils.class);
private final static String PROTOCOL = "https";
private final static String REGIONID = "central";
public static com.aliyun.dingtalkworkflow_1_0.Client createClient() throws Exception {
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
config.protocol = PROTOCOL;
config.regionId = REGIONID;
return new com.aliyun.dingtalkworkflow_1_0.Client(config);
}
/**
* 企业内部应用
* 获取钉钉access_token
*
* @return
* @throws Exception
*/
public static GetAccessTokenResponseBody getTokenBody(String appkey, String appsecret) throws Exception {
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
config.protocol = PROTOCOL;
config.regionId = REGIONID;
com.aliyun.dingtalkoauth2_1_0.Client client = new com.aliyun.dingtalkoauth2_1_0.Client(config);
com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenRequest getAccessTokenRequest = new com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenRequest()
.setAppKey(appkey)//正式环境
.setAppSecret(appsecret);//正式环境
try {
GetAccessTokenResponse accessToken = client.getAccessToken(getAccessTokenRequest);
return accessToken.getBody();
} catch (TeaException err) {
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
logger.error(err);
}
} catch (Exception _err) {
TeaException err = new TeaException(_err.getMessage(), _err);
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
logger.error(err);
}
}
return null;
}
/**
* 获取人员ID列表
*
* @param accessToken
* @return
*/
public static OapiUserListidResponse getUserIdList(String accessToken) {
try {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/user/listid");
OapiUserListidRequest req = new OapiUserListidRequest();
req.setDeptId(1L);
OapiUserListidResponse rsp = client.execute(req, accessToken);
System.out.println(rsp.getBody());
return rsp;
} catch (ApiException e) {
e.printStackTrace();
logger.error(e);
}
return null;
}
/**
* 获取部门列表
*
* @param accessToken
* @return
*/
public static OapiV2DepartmentListsubResponse getDeptList(String accessToken, Long deptid) {
try {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/department/listsub");
OapiV2DepartmentListsubRequest req = new OapiV2DepartmentListsubRequest();
if (deptid != null) {
req.setDeptId(deptid);
}
req.setLanguage("zh_CN");
OapiV2DepartmentListsubResponse rsp = client.execute(req, accessToken);
return rsp;
} catch (ApiException e) {
e.printStackTrace();
}
return null;
}
/**
* 根据部门ID获取部门详情
*
* @param accessToken
* @param deptId
* @return
*/
public static OapiV2DepartmentGetResponse getDeptByDeptId(String accessToken, Long deptId) {
OapiV2DepartmentGetResponse oapiV2DepartmentGetResponse = new OapiV2DepartmentGetResponse();
try {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/department/get");
OapiV2DepartmentGetRequest req = new OapiV2DepartmentGetRequest();
req.setDeptId(deptId);
oapiV2DepartmentGetResponse = client.execute(req, accessToken);
} catch (ApiException e) {
e.printStackTrace();
Assert.state(false, e.getMessage());
}
return oapiV2DepartmentGetResponse;
}
/**
* 根据部门id查询用户id列表
* 开发组dept_id--624820307L
*
* @param accessToken
* @param deptId
*/
public static OapiUserListidResponse getUserIdListByDeptId(String accessToken, Long deptId) {
try {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/user/listid");
OapiUserListidRequest req = new OapiUserListidRequest();
req.setDeptId(deptId);
OapiUserListidResponse rsp = client.execute(req, accessToken);
return rsp;
} catch (ApiException e) {
e.printStackTrace();
}
return null;
}
/**
* 根据用户id查询用户详情
*
* @param accessToken
* @param userId
* @return
*/
public static OapiV2UserGetResponse getUserById(String accessToken, String userId) {
try {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/get");
OapiV2UserGetRequest req = new OapiV2UserGetRequest();
req.setUserid(userId);
OapiV2UserGetResponse rsp = client.execute(req, accessToken);
return rsp;
} catch (ApiException e) {
e.printStackTrace();
}
return null;
}
/**
* 根据手机号查询用户详情
*
* @param accessToken
* @param mobile
* @return
*/
public static OapiV2UserGetbymobileResponse getUserByMobile(String accessToken, String mobile) {
try {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/getbymobile");
OapiV2UserGetbymobileRequest req = new OapiV2UserGetbymobileRequest();
req.setMobile(mobile);
OapiV2UserGetbymobileResponse rsp = client.execute(req, accessToken);
return rsp;
} catch (ApiException e) {
e.printStackTrace();
}
return null;
}
/**
* 根据部门id获取用户详细列表
*
* @return
*/
public static OapiV2UserListResponse getUserDetailsByDeptId(String accessToken, Long deptId, Long cursor, Long size) {
try {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/list");
OapiV2UserListRequest req = new OapiV2UserListRequest();
req.setDeptId(deptId);
req.setCursor(cursor);
req.setSize(size);
req.setLanguage("zh_CN");
OapiV2UserListResponse rsp = client.execute(req, accessToken);
return rsp;
} catch (ApiException e) {
e.printStackTrace();
}
return null;
}
/**
* 根据模板名称获取审批模板code
*
* @param accessToken
* @param TemplateName
* @return
* @throws Exception
*/
public static GetProcessCodeByNameResponse getProcessCode(String accessToken, String TemplateName) throws Exception {
try {
com.aliyun.dingtalkworkflow_1_0.Client client = createClient();
GetProcessCodeByNameHeaders getProcessCodeByNameHeaders = new GetProcessCodeByNameHeaders();
getProcessCodeByNameHeaders.xAcsDingtalkAccessToken = accessToken;
GetProcessCodeByNameRequest getProcessCodeByNameRequest = new GetProcessCodeByNameRequest()
.setName(TemplateName);
GetProcessCodeByNameResponse processCodeByNameWithOptions = client.getProcessCodeByNameWithOptions(getProcessCodeByNameRequest, getProcessCodeByNameHeaders, new RuntimeOptions());
return processCodeByNameWithOptions;
} catch (TeaException err) {
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
Assert.state(false, err.getMessage());
}
} catch (Exception _err) {
TeaException err = new TeaException(_err.getMessage(), _err);
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
Assert.state(false, err.getMessage());
}
}
return null;
}
/**
* 根据模板code获取表单的具体内容
*
* @param accessToken
* @param processCode
* @return
* @throws Exception
*/
public static QuerySchemaByProcessCodeResponse getBillSchema(String accessToken, String processCode) throws Exception {
com.aliyun.dingtalkworkflow_1_0.Client client = createClient();
QuerySchemaByProcessCodeHeaders querySchemaByProcessCodeHeaders = new QuerySchemaByProcessCodeHeaders();
querySchemaByProcessCodeHeaders.xAcsDingtalkAccessToken = accessToken;
QuerySchemaByProcessCodeRequest querySchemaByProcessCodeRequest = new QuerySchemaByProcessCodeRequest()
.setProcessCode(processCode);
try {
QuerySchemaByProcessCodeResponse res = client.querySchemaByProcessCodeWithOptions(querySchemaByProcessCodeRequest, querySchemaByProcessCodeHeaders, new RuntimeOptions());
return res;
} catch (TeaException err) {
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
Assert.state(false, err.getMessage());
}
} catch (Exception _err) {
TeaException err = new TeaException(_err.getMessage(), _err);
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
Assert.state(false, err.getMessage());
}
}
return null;
}
/**
* 服务端APIOA审批官方OA审批审批实例发起审批实例
* 发起审批实例
*
* @param accessToken accessToken
* @param originatorUserId 审批发起人的userId
* @param processCode 审批流的唯一码,模板编码
* @param deptId 审批发起人所在的部门ID
* @param microappAgentId 应用标识AgentId
* @param approverList 不使用审批流模板时直接指定的审批人列表
* @param formComponentValuesList 表单数据内容控件列表最大列表长度150
* @return
* @throws Exception
*/
public static StartProcessInstanceResponse InitiateApprovalInstance(String accessToken, String originatorUserId, String processCode, Long deptId, Long microappAgentId, List<StartProcessInstanceRequest.StartProcessInstanceRequestApprovers> approverList, List<StartProcessInstanceRequest.StartProcessInstanceRequestFormComponentValues> formComponentValuesList) throws Exception {
com.aliyun.dingtalkworkflow_1_0.Client client = createClient();
StartProcessInstanceHeaders startProcessInstanceHeaders = new StartProcessInstanceHeaders();
startProcessInstanceHeaders.xAcsDingtalkAccessToken = accessToken;
StartProcessInstanceRequest startProcessInstanceRequest = new StartProcessInstanceRequest()
.setDeptId(deptId)
.setApprovers(approverList)
.setMicroappAgentId(microappAgentId)
.setOriginatorUserId(originatorUserId)
.setProcessCode(processCode)
.setFormComponentValues(formComponentValuesList);
try {
StartProcessInstanceResponse startProcessInstanceResponse = client.startProcessInstanceWithOptions(startProcessInstanceRequest, startProcessInstanceHeaders, new RuntimeOptions());
return startProcessInstanceResponse;
} catch (TeaException err) {
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
// err 中含有 code message 属性可帮助开发定位问题
Assert.state(false, err.getMessage());
}
} catch (Exception _err) {
TeaException err = new TeaException(_err.getMessage(), _err);
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
// err 中含有 code message 属性可帮助开发定位问题
Assert.state(false, err.getMessage());
}
}
return null;
}
/**
* 根据审批实例ID获取该审批表单的详情内容
*
* @param accessToken
* @param processInstanceId
* @return
* @throws Exception
*/
public static GetProcessInstanceResponse getProcessInstanceDetails(String accessToken, String processInstanceId) throws Exception {
com.aliyun.dingtalkworkflow_1_0.Client client = createClient();
GetProcessInstanceHeaders getProcessInstanceHeaders = new GetProcessInstanceHeaders();
getProcessInstanceHeaders.xAcsDingtalkAccessToken = accessToken;
GetProcessInstanceRequest getProcessInstanceRequest = new GetProcessInstanceRequest()
.setProcessInstanceId(processInstanceId);
try {
GetProcessInstanceResponse processInstanceWithOptions = client.getProcessInstanceWithOptions(getProcessInstanceRequest, getProcessInstanceHeaders, new RuntimeOptions());
return processInstanceWithOptions;
} catch (TeaException err) {
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
Assert.state(false, err.getMessage());
}
} catch (Exception _err) {
TeaException err = new TeaException(_err.getMessage(), _err);
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
Assert.state(false, err.getMessage());
}
}
return null;
}
/**
* 根据审批实例ID撤销审批实例
*
* @param accessToken accessToken
* @param processInstanceId 审批实例ID
* @param isSystem 是否通过系统操作 - **true**由系统直接终止 - **false**由指定的操作者终止需要传发起人才能撤销妈的这里有bug不是发起人也能删除
* @param remark 终止说明
* @param operatingUserId 发起人ID
* @throws Exception
*/
public static TerminateProcessInstanceResponse revocationProcessInstance(String accessToken, String processInstanceId, boolean isSystem, String remark, String operatingUserId) throws Exception {
TerminateProcessInstanceResponse terminateProcessInstanceResponse = new TerminateProcessInstanceResponse();
com.aliyun.dingtalkworkflow_1_0.Client client = createClient();
com.aliyun.dingtalkworkflow_1_0.models.TerminateProcessInstanceHeaders terminateProcessInstanceHeaders = new com.aliyun.dingtalkworkflow_1_0.models.TerminateProcessInstanceHeaders();
terminateProcessInstanceHeaders.xAcsDingtalkAccessToken = accessToken;
com.aliyun.dingtalkworkflow_1_0.models.TerminateProcessInstanceRequest terminateProcessInstanceRequest = new com.aliyun.dingtalkworkflow_1_0.models.TerminateProcessInstanceRequest()
.setIsSystem(isSystem)
.setProcessInstanceId(processInstanceId)
.setRemark(remark)
.setOperatingUserId(operatingUserId);
try {
terminateProcessInstanceResponse = client.terminateProcessInstanceWithOptions(terminateProcessInstanceRequest, terminateProcessInstanceHeaders, new RuntimeOptions());
} catch (TeaException err) {
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
Assert.state(false, err.getMessage());
}
} catch (Exception _err) {
TeaException err = new TeaException(_err.getMessage(), _err);
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
Assert.state(false, err.getMessage());
}
}
return terminateProcessInstanceResponse;
}
/**
* 获取审批钉盘空间信息
*
* @param accessToken 获取审批钉盘空间信息
* @param userId 用户的userId
*/
public static GetAttachmentSpaceResponse getProcessDingPanSpaceInfo(String accessToken, String userId) throws Exception {
com.aliyun.dingtalkworkflow_1_0.Client client = createClient();
com.aliyun.dingtalkworkflow_1_0.models.GetAttachmentSpaceHeaders getAttachmentSpaceHeaders = new com.aliyun.dingtalkworkflow_1_0.models.GetAttachmentSpaceHeaders();
getAttachmentSpaceHeaders.xAcsDingtalkAccessToken = accessToken;
com.aliyun.dingtalkworkflow_1_0.models.GetAttachmentSpaceRequest getAttachmentSpaceRequest = new com.aliyun.dingtalkworkflow_1_0.models.GetAttachmentSpaceRequest()
.setUserId(userId);
GetAttachmentSpaceResponse getAttachmentSpaceResponse = new GetAttachmentSpaceResponse();
try {
getAttachmentSpaceResponse = client.getAttachmentSpaceWithOptions(getAttachmentSpaceRequest, getAttachmentSpaceHeaders, new RuntimeOptions());
} catch (TeaException err) {
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
Assert.state(false, err.getMessage());
}
} catch (Exception _err) {
TeaException err = new TeaException(_err.getMessage(), _err);
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
Assert.state(false, err.getMessage());
}
}
return getAttachmentSpaceResponse;
}
/**
* 获取文件上传信息
*
* @param accessToken token
* @param spaceId 钉盘空间id
* @param unionId 操作者ID
* @param protocol HEADER_SIGNATURE
* @param multipart 是否需要分片上传 - **true**需要 - **false**不需要 >- 5G以下文件设为false简化上传步骤 >- 5G以上文件必须设为true否则会上传失败
*/
public static GetFileUploadInfoResponse getFileUploadInfo(String accessToken, String spaceId, String unionId, String protocol, Boolean multipart) throws Exception {
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
config.protocol = PROTOCOL;
config.regionId = REGIONID;
com.aliyun.dingtalkstorage_1_0.Client client = new com.aliyun.dingtalkstorage_1_0.Client(config);
GetFileUploadInfoResponse getFileUploadInfoResponse = new GetFileUploadInfoResponse();
com.aliyun.dingtalkstorage_1_0.models.GetFileUploadInfoHeaders getFileUploadInfoHeaders = new com.aliyun.dingtalkstorage_1_0.models.GetFileUploadInfoHeaders();
getFileUploadInfoHeaders.xAcsDingtalkAccessToken = accessToken;
com.aliyun.dingtalkstorage_1_0.models.GetFileUploadInfoRequest getFileUploadInfoRequest = new com.aliyun.dingtalkstorage_1_0.models.GetFileUploadInfoRequest()
.setProtocol(protocol)
.setMultipart(multipart)
.setUnionId(unionId);
try {
getFileUploadInfoResponse = client.getFileUploadInfoWithOptions(spaceId, getFileUploadInfoRequest, getFileUploadInfoHeaders, new RuntimeOptions());
} catch (TeaException err) {
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
Assert.state(false, err.getMessage());
}
} catch (Exception _err) {
TeaException err = new TeaException(_err.getMessage(), _err);
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
Assert.state(false, err.getMessage());
}
}
return getFileUploadInfoResponse;
}
/**
* 提交文件
*
* @param accessToken token
* @param spaceId 空间ID
* @param unionId 操作者unionId
* @param uploadKey 添加文件唯一标识获得文件上传信息是获得
* @param fileName 文件名称
* @param parentId 父目录Id根目录时该参数是0传FileManager中对应的id
* @param commitFileRequestOptionAppProperties 当前文件的应用属性信息
* @return
* @throws Exception
*/
public static CommitFileResponse submitFile(String accessToken, String spaceId, String unionId, String uploadKey, String fileName, String parentId, CommitFileRequest.CommitFileRequestOptionAppProperties commitFileRequestOptionAppProperties) throws Exception {
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
config.protocol = PROTOCOL;
config.regionId = REGIONID;
com.aliyun.dingtalkstorage_1_0.Client client = new com.aliyun.dingtalkstorage_1_0.Client(config);
CommitFileResponse commitFileResponse = new CommitFileResponse();
com.aliyun.dingtalkstorage_1_0.models.CommitFileHeaders commitFileHeaders = new com.aliyun.dingtalkstorage_1_0.models.CommitFileHeaders();
commitFileHeaders.xAcsDingtalkAccessToken = accessToken;
com.aliyun.dingtalkstorage_1_0.models.CommitFileRequest.CommitFileRequestOption option = new com.aliyun.dingtalkstorage_1_0.models.CommitFileRequest.CommitFileRequestOption()
.setAppProperties(java.util.Arrays.asList(
commitFileRequestOptionAppProperties
));
com.aliyun.dingtalkstorage_1_0.models.CommitFileRequest commitFileRequest = new com.aliyun.dingtalkstorage_1_0.models.CommitFileRequest()
.setName(fileName)
.setUploadKey(uploadKey)
.setParentId(parentId)
.setOption(option)
.setUnionId(unionId);
try {
commitFileResponse = client.commitFileWithOptions(spaceId, commitFileRequest, commitFileHeaders, new RuntimeOptions());
} catch (TeaException err) {
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
Assert.state(false, err.getMessage());
}
} catch (Exception _err) {
TeaException err = new TeaException(_err.getMessage(), _err);
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
Assert.state(false, err.getMessage());
}
}
return commitFileResponse;
}
/**
* 根据用户的unionId添加权限上传钉盘时用户总掉权限导致上传失败所以需要根据用户的unionId添加权限
* 仅限审批实例中有附件时
* @param accessToken 调用企业接口凭证
* @param spaceId 空间Id
* @param unionId 操作用户的unionId
* @return
* @throws Exception
*/
public static AddPermissionResponse addFileStorageByUnionId(String accessToken, String spaceId, String unionId) throws Exception {
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
config.protocol = "https";
config.regionId = "central";
com.aliyun.dingtalkstorage_1_0.Client client = new com.aliyun.dingtalkstorage_1_0.Client(config);
AddPermissionResponse addPermissionResponse = new AddPermissionResponse();
com.aliyun.dingtalkstorage_1_0.models.AddPermissionHeaders addPermissionHeaders = new com.aliyun.dingtalkstorage_1_0.models.AddPermissionHeaders();
addPermissionHeaders.xAcsDingtalkAccessToken = accessToken;
com.aliyun.dingtalkstorage_1_0.models.AddPermissionRequest.AddPermissionRequestMembers members0 = new com.aliyun.dingtalkstorage_1_0.models.AddPermissionRequest.AddPermissionRequestMembers()
.setType("USER")
.setId(unionId);
com.aliyun.dingtalkstorage_1_0.models.AddPermissionRequest addPermissionRequest = new com.aliyun.dingtalkstorage_1_0.models.AddPermissionRequest()
.setUnionId(unionId)
.setRoleId("MANAGER")
.setMembers(java.util.Arrays.asList(
members0
));
try {
addPermissionResponse = client.addPermissionWithOptions(spaceId, "0", addPermissionRequest, addPermissionHeaders, new RuntimeOptions());
} catch (TeaException err) {
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
Assert.state(false, err.getMessage());
}
} catch (Exception _err) {
TeaException err = new TeaException(_err.getMessage(), _err);
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
Assert.state(false, err.getMessage());
}
}
return addPermissionResponse;
}
}

View File

@ -0,0 +1,111 @@
package com.hzya.frame.plugin.lets.dingtalk.utils;
import cn.hutool.core.lang.Assert;
import com.aliyun.dingtalkstorage_1_0.models.AddPermissionResponse;
import com.aliyun.dingtalkstorage_1_0.models.CommitFileRequest;
import com.aliyun.dingtalkstorage_1_0.models.CommitFileResponse;
import com.aliyun.dingtalkstorage_1_0.models.GetFileUploadInfoResponse;
import com.hzya.frame.plugin.lets.dingtalk.vo.FileModuleVO;
import com.hzya.frame.plugin.lets.dingtalk.vo.SysFileVO;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.Map;
public class FileUtil {
protected static Logger logger = LogManager.getLogger(FileUtil.class);
@Value("${DING.SPACEID}")
private static String spaceId;
/**
* 审批文件上传钉盘
* @param accessToken 应用Token
* @param unionid 钉钉用户unionid
* @param dingFileUrlID 钉盘审批实例中附件地址
* @param fileVO 的文件信息
* @param stream 文件读取的字节流
* @return
*/
public static FileModuleVO get4DingDileModuleVO(String accessToken, String unionid, String dingFileUrlID, SysFileVO fileVO, ByteArrayOutputStream stream){
FileModuleVO fileModuleVO = new FileModuleVO();
try {
//钉钉--上传附件
//1.调用服务端API-获取审批钉盘空间信息接口获取审批空间spaceId
System.out.println(spaceId);
//2.调用服务端API-获取文件上传信息接口获取审批空间文件上传信息并执行上传
/////////////////////////////////////////////////////////////////////////////////////////////////此处很容易调权限钉钉问题添加权限
AddPermissionResponse addPermissionResponse = DingTalkUtils.addFileStorageByUnionId(accessToken, spaceId, unionid);
if(addPermissionResponse.getBody().getSuccess()!=true){
Assert.state(false,"钉钉添加审批实例附件到钉盘权限失败");
}
/////////////////////////////////////////////////////////////////////////////////////////////////此处很容易调权限钉钉问题添加权限
//2.1调用本接口获取上传文件需要的resourceUrls和headers参数值
GetFileUploadInfoResponse headerSignature = DingTalkUtils.getFileUploadInfo(accessToken, spaceId, unionid, "HEADER_SIGNATURE", false);
String uploadKey = headerSignature.getBody().getUploadKey();
List<String> resourceUrls = headerSignature.getBody().getHeaderSignatureInfo().getResourceUrls();
if (resourceUrls.size() > 1) {
Assert.state(false, "获取文件上传信息接口中resourceUrls不唯一");
}
Map<String, String> headers = headerSignature.getBody().getHeaderSignatureInfo().getHeaders();
//2.2使用OSS的header加签方式上传文件参考示例
// 从接口返回信息中拿到headers
URL url = new URL(resourceUrls.get(0));
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
if (headers != null) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
connection.setRequestProperty(entry.getKey(), entry.getValue());
}
}
connection.setDoOutput(true);
connection.setRequestMethod("PUT");
connection.setUseCaches(false);
connection.setReadTimeout(10000);
connection.setConnectTimeout(10000);
connection.connect();
OutputStream out = connection.getOutputStream();
out.write(stream.toByteArray());
out.flush();
out.close();
int responseCode = connection.getResponseCode();
connection.disconnect();
if (responseCode == 200) {
logger.info("审批实例附件上传审批钉盘成功,UploadKey:-->" + headerSignature.getBody().getUploadKey());
} else {
Assert.state(false,"审批实例附件上传审批钉盘失败");
}
//2.3调用提交文件接口完成文件上传
CommitFileRequest.CommitFileRequestOptionAppProperties commitFileRequestOptionAppProperties = new CommitFileRequest.CommitFileRequestOptionAppProperties();
commitFileRequestOptionAppProperties.setName(fileVO.getFileName());
commitFileRequestOptionAppProperties.setValue(fileVO.getFileName());
commitFileRequestOptionAppProperties.setVisibility("PUBLIC");//属性可见性 - PUBLIC所有应用都可见 - PRIVATE仅限当前应用可见
//3.提交文件
CommitFileResponse commitFileResponse = DingTalkUtils.submitFile(accessToken, spaceId, unionid, uploadKey, fileVO.getFileName(), dingFileUrlID, commitFileRequestOptionAppProperties);
//钉盘文件唯一id
String dingFileUniqueId = commitFileResponse.getBody().getDentry().getId();
//4.组装钉钉文件控件-->"[{\"spaceId\": \"163xxxx658\", \"fileName\": \"2644.JPG\", \"fileSize\": \"333\", \"fileType\": \"jpg\", \"fileId\": \"643xxxx140\"}]"
fileModuleVO.setSpaceId(spaceId);
fileModuleVO.setFileName(fileVO.getFileName());
fileModuleVO.setFileSize(String.valueOf(fileVO.getFileSize()));
String s = StringUtils.substringAfter(fileVO.getFileName(), ".");
fileModuleVO.setFileType(s);//取后缀
fileModuleVO.setFileId(dingFileUniqueId);
}catch (Exception e){
e.printStackTrace();
logger.error(e);
Assert.state(false,"文件存钉盘失败,失败信息:{}",e.getMessage());
}
return fileModuleVO;
}
}

View File

@ -0,0 +1,36 @@
package com.hzya.frame.plugin.lets.dingtalk.utils;
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenResponseBody;
import com.aliyun.dingtalkworkflow_1_0.models.GetAttachmentSpaceResponse;
import com.dingtalk.api.response.OapiUserListidResponse;
import com.dingtalk.api.response.OapiV2UserGetResponse;
import com.dingtalk.api.response.OapiV2UserGetbymobileResponse;
public class Test {
public static void main(String[] args) throws Exception {
String appKey="ding5qfifcktfqfjlem0";
String appSecret="BVuLOy_K0F8f5np69VuBeKdb1zfwqhuPsAV49lNbYVbx5TnfSTHjwEcad9Vwzfq1";
//获取token
GetAccessTokenResponseBody tokenBody = DingTalkUtils.getTokenBody(appKey, appSecret);
System.out.println(tokenBody.getAccessToken());
//获取用户列表
// OapiUserListidResponse userIdList = DingTalkUtils.getUserIdList(tokenBody.getAccessToken());
// System.out.println(userIdList);
//根据手机号查询用户详情,userid:6715600736721738
// OapiV2UserGetbymobileResponse userByMobile = DingTalkUtils.getUserByMobile(tokenBody.getAccessToken(), "13783530043");
// System.out.println(userByMobile.getBody());
//查询用户详情,unionId:xXpSQXdGCyYNcQXxrgezOwiEiE
// OapiV2UserGetResponse userById = DingTalkUtils.getUserById(tokenBody.getAccessToken(), "6715600736721738");
// System.out.println(userById.getBody());
//获取审批钉盘空间信息,spaceId:20852670637
// GetAttachmentSpaceResponse processDingPanSpaceInfo = DingTalkUtils.getProcessDingPanSpaceInfo(tokenBody.getAccessToken(), "6715600736721738");
// System.out.println(processDingPanSpaceInfo.getBody().getResult().getSpaceId());
}
}

View File

@ -0,0 +1,395 @@
package com.hzya.frame.plugin.lets.dingtalk.vo;
import com.alibaba.fastjson.JSON;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Security;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class DingCallbackCrypto {
private static final Charset CHARSET = Charset.forName("utf-8");
private static final Base64 base64 = new Base64();
private byte[] aesKey;
private String token;
private String corpId;
/**
* ask getPaddingBytes key固定长度
**/
private static final Integer AES_ENCODE_KEY_LENGTH = 43;
/**
* 加密随机字符串字节长度
**/
private static final Integer RANDOM_LENGTH = 16;
/**
* 构造函数
*
* @param token 钉钉开放平台上开发者设置的token
* @param encodingAesKey 钉钉开放台上开发者设置的EncodingAESKey
* @param corpId 企业自建应用-事件订阅, 使用appKey
* 企业自建应用-注册回调地址, 使用corpId
* 第三方企业应用, 使用suiteKey
* @throws DingTalkEncryptException 执行失败请查看该异常的错误码和具体的错误信息
*/
public DingCallbackCrypto(String token, String encodingAesKey, String corpId) throws DingTalkEncryptException {
if (null == encodingAesKey || encodingAesKey.length() != AES_ENCODE_KEY_LENGTH) {
throw new DingTalkEncryptException(DingTalkEncryptException.AES_KEY_ILLEGAL);
}
this.token = token;
this.corpId = corpId;
aesKey = Base64.decodeBase64(encodingAesKey + "=");
}
public Map<String, String> getEncryptedMap(String plaintext) throws DingTalkEncryptException {
return getEncryptedMap(plaintext, System.currentTimeMillis(), Utils.getRandomStr(16));
}
/**
* 将和钉钉开放平台同步的消息体加密,返回加密Map
*
* @param plaintext 传递的消息体明文
* @param timeStamp 时间戳
* @param nonce 随机字符串
* @return
* @throws DingTalkEncryptException
*/
public Map<String, String> getEncryptedMap(String plaintext, Long timeStamp, String nonce)
throws DingTalkEncryptException {
if (null == plaintext) {
throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_PLAINTEXT_ILLEGAL);
}
if (null == timeStamp) {
throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_TIMESTAMP_ILLEGAL);
}
if (null == nonce) {
throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_NONCE_ILLEGAL);
}
// 加密
String encrypt = encrypt(Utils.getRandomStr(RANDOM_LENGTH), plaintext);
String signature = getSignature(token, String.valueOf(timeStamp), nonce, encrypt);
Map<String, String> resultMap = new HashMap<String, String>();
resultMap.put("msg_signature", signature);
resultMap.put("encrypt", encrypt);
resultMap.put("timeStamp", String.valueOf(timeStamp));
resultMap.put("nonce", nonce);
return resultMap;
}
/**
* 密文解密
*
* @param msgSignature 签名串
* @param timeStamp 时间戳
* @param nonce 随机串
* @param encryptMsg 密文
* @return 解密后的原文
* @throws DingTalkEncryptException
*/
public String getDecryptMsg(String msgSignature, String timeStamp, String nonce, String encryptMsg)
throws DingTalkEncryptException {
//校验签名
String signature = getSignature(token, timeStamp, nonce, encryptMsg);
if (!signature.equals(msgSignature)) {
throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_SIGNATURE_ERROR);
}
// 解密
String result = decrypt(encryptMsg);
return result;
}
/*
* 对明文加密.
* @param text 需要加密的明文
* @return 加密后base64编码的字符串
*/
private String encrypt(String random, String plaintext) throws DingTalkEncryptException {
try {
byte[] randomBytes = random.getBytes(CHARSET);
byte[] plainTextBytes = plaintext.getBytes(CHARSET);
byte[] lengthByte = Utils.int2Bytes(plainTextBytes.length);
byte[] corpidBytes = corpId.getBytes(CHARSET);
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
byteStream.write(randomBytes);
byteStream.write(lengthByte);
byteStream.write(plainTextBytes);
byteStream.write(corpidBytes);
byte[] padBytes = PKCS7Padding.getPaddingBytes(byteStream.size());
byteStream.write(padBytes);
byte[] unencrypted = byteStream.toByteArray();
byteStream.close();
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
byte[] encrypted = cipher.doFinal(unencrypted);
String result = base64.encodeToString(encrypted);
return result;
} catch (Exception e) {
throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_ENCRYPT_TEXT_ERROR);
}
}
/*
* 对密文进行解密.
* @param text 需要解密的密文
* @return 解密得到的明文
*/
private String decrypt(String text) throws DingTalkEncryptException {
byte[] originalArr;
try {
// 设置解密模式为AES的CBC模式
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
// 使用BASE64对密文进行解码
byte[] encrypted = Base64.decodeBase64(text);
// 解密
originalArr = cipher.doFinal(encrypted);
} catch (Exception e) {
throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_ERROR);
}
String plainText;
String fromCorpid;
try {
// 去除补位字符
byte[] bytes = PKCS7Padding.removePaddingBytes(originalArr);
// 分离16位随机字符串,网络字节序和corpId
byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);
int plainTextLegth = Utils.bytes2int(networkOrder);
plainText = new String(Arrays.copyOfRange(bytes, 20, 20 + plainTextLegth), CHARSET);
fromCorpid = new String(Arrays.copyOfRange(bytes, 20 + plainTextLegth, bytes.length), CHARSET);
} catch (Exception e) {
throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_LENGTH_ERROR);
}
// corpid不相同的情况
if (!fromCorpid.equals(corpId)) {
throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_CORPID_ERROR);
}
return plainText;
}
/**
* 数字签名
*
* @param token isv token
* @param timestamp 时间戳
* @param nonce 随机串
* @param encrypt 加密文本
* @return
* @throws DingTalkEncryptException
*/
public String getSignature(String token, String timestamp, String nonce, String encrypt)
throws DingTalkEncryptException {
try {
String[] array = new String[]{token, timestamp, nonce, encrypt};
Arrays.sort(array);
System.out.println(JSON.toJSONString(array));
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 4; i++) {
sb.append(array[i]);
}
String str = sb.toString();
System.out.println(str);
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(str.getBytes());
byte[] digest = md.digest();
StringBuffer hexstr = new StringBuffer();
String shaHex = "";
for (int i = 0; i < digest.length; i++) {
shaHex = Integer.toHexString(digest[i] & 0xFF);
if (shaHex.length() < 2) {
hexstr.append(0);
}
hexstr.append(shaHex);
}
return hexstr.toString();
} catch (Exception e) {
throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_SIGNATURE_ERROR);
}
}
public static class Utils {
public Utils() {
}
public static String getRandomStr(int count) {
String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < count; ++i) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
public static byte[] int2Bytes(int count) {
byte[] byteArr = new byte[]{(byte) (count >> 24 & 255), (byte) (count >> 16 & 255), (byte) (count >> 8 & 255),
(byte) (count & 255)};
return byteArr;
}
public static int bytes2int(byte[] byteArr) {
int count = 0;
for (int i = 0; i < 4; ++i) {
count <<= 8;
count |= byteArr[i] & 255;
}
return count;
}
}
public static class PKCS7Padding {
private static final Charset CHARSET = Charset.forName("utf-8");
private static final int BLOCK_SIZE = 32;
public PKCS7Padding() {
}
public static byte[] getPaddingBytes(int count) {
int amountToPad = 32 - count % 32;
if (amountToPad == 0) {
amountToPad = 32;
}
char padChr = chr(amountToPad);
String tmp = new String();
for (int index = 0; index < amountToPad; ++index) {
tmp = tmp + padChr;
}
return tmp.getBytes(CHARSET);
}
public static byte[] removePaddingBytes(byte[] decrypted) {
int pad = decrypted[decrypted.length - 1];
if (pad < 1 || pad > 32) {
pad = 0;
}
return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
}
private static char chr(int a) {
byte target = (byte) (a & 255);
return (char) target;
}
}
public static class DingTalkEncryptException extends Exception {
public static final int SUCCESS = 0;
public static final int ENCRYPTION_PLAINTEXT_ILLEGAL = 900001;
public static final int ENCRYPTION_TIMESTAMP_ILLEGAL = 900002;
public static final int ENCRYPTION_NONCE_ILLEGAL = 900003;
public static final int AES_KEY_ILLEGAL = 900004;
public static final int SIGNATURE_NOT_MATCH = 900005;
public static final int COMPUTE_SIGNATURE_ERROR = 900006;
public static final int COMPUTE_ENCRYPT_TEXT_ERROR = 900007;
public static final int COMPUTE_DECRYPT_TEXT_ERROR = 900008;
public static final int COMPUTE_DECRYPT_TEXT_LENGTH_ERROR = 900009;
public static final int COMPUTE_DECRYPT_TEXT_CORPID_ERROR = 900010;
private static Map<Integer, String> msgMap = new HashMap();
private Integer code;
static {
msgMap.put(0, "成功");
msgMap.put(900001, "加密明文文本非法");
msgMap.put(900002, "加密时间戳参数非法");
msgMap.put(900003, "加密随机字符串参数非法");
msgMap.put(900005, "签名不匹配");
msgMap.put(900006, "签名计算失败");
msgMap.put(900004, "不合法的aes key");
msgMap.put(900007, "计算加密文字错误");
msgMap.put(900008, "计算解密文字错误");
msgMap.put(900009, "计算解密文字长度不匹配");
msgMap.put(900010, "计算解密文字corpid不匹配");
}
public Integer getCode() {
return this.code;
}
public DingTalkEncryptException(Integer exceptionCode) {
super((String) msgMap.get(exceptionCode));
this.code = exceptionCode;
}
}
static {
try {
Security.setProperty("crypto.policy", "limited");
RemoveCryptographyRestrictions();
} catch (Exception var1) {
}
}
private static void RemoveCryptographyRestrictions() throws Exception {
Class<?> jceSecurity = getClazz("javax.crypto.JceSecurity");
Class<?> cryptoPermissions = getClazz("javax.crypto.CryptoPermissions");
Class<?> cryptoAllPermission = getClazz("javax.crypto.CryptoAllPermission");
if (jceSecurity != null) {
setFinalStaticValue(jceSecurity, "isRestricted", false);
PermissionCollection defaultPolicy = (PermissionCollection) getFieldValue(jceSecurity, "defaultPolicy", (Object) null, PermissionCollection.class);
if (cryptoPermissions != null) {
Map<?, ?> map = (Map) getFieldValue(cryptoPermissions, "perms", defaultPolicy, Map.class);
map.clear();
}
if (cryptoAllPermission != null) {
Permission permission = (Permission) getFieldValue(cryptoAllPermission, "INSTANCE", (Object) null, Permission.class);
defaultPolicy.add(permission);
}
}
}
private static Class<?> getClazz(String className) {
Class clazz = null;
try {
clazz = Class.forName(className);
} catch (Exception var3) {
}
return clazz;
}
private static void setFinalStaticValue(Class<?> srcClazz, String fieldName, Object newValue) throws Exception {
Field field = srcClazz.getDeclaredField(fieldName);
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & -17);
field.set((Object) null, newValue);
}
private static <T> T getFieldValue(Class<?> srcClazz, String fieldName, Object owner, Class<T> dstClazz) throws Exception {
Field field = srcClazz.getDeclaredField(fieldName);
field.setAccessible(true);
return dstClazz.cast(field.get(owner));
}
}

View File

@ -0,0 +1,253 @@
package com.hzya.frame.plugin.lets.dingtalk.vo;
import lombok.Data;
import java.util.List;
/**
* arap_djzb 单据主表
* arap_djfb 单据辅表
*/
@Data
public class DjzbVO {
private Arap_djzb parentvo;
private List<Arap_djfb> children;
@Data
public static class Arap_djzb {
private String bbje;//本币金额合计
private String bzbm;//币种主键
private String bzbm_code;//币种编码
private String bzbm_name;//币种名称
private String def1;//自定义项名称1
private String def10;//自定义项名称10
private String def11;//自定义项名称11
private String def12;//自定义项名称12
private String def13;//自定义项名称13
private String def14;//自定义项名称14
private String def15;//自定义项名称15
private String def16;//自定义项名称16
private String def17;//自定义项名称17
private String def18;//自定义项名称18
private String def19;//自定义项名称19
private String def2;//自定义项名称2
private String def20;//自定义项名称20
private String def21;//自定义项名称21
private String def22;//自定义项名称22
private String def23;//自定义项名称23
private String def24;//自定义项名称24
private String def25;//自定义项名称25
private String def26;//自定义项名称26
private String def27;//自定义项名称27
private String def28;//自定义项名称28
private String def29;//自定义项名称29
private String def3;//自定义项名称3
private String def30;//自定义项名称30
private String def4;//自定义项名称4
private String def5;//自定义项名称5
private String def6;//自定义项名称6
private String def7;//自定义项名称7
private String def8;//自定义项名称8
private String def9;//自定义项名称9
private String deptid;//部门主键
private String deptid_code;//部门编码
private String deptid_name;//部门名称
private String dfyhzh;//付款银行帐号
private String djbh;//单据编号
private String djdl;//单据大类
private String djkjnd;//会计年度
private String djkjqj;//会计期间
private String djrq;//单据日期
private String djzt;//单据状态
private String dwbm;//公司主键
private String dwbm_code;//公司编码
private String dwbm_name;//公司名称
private String dzrq;//对帐日期
private String enduser;//最终修改人5.1开始支持
private String hbbm;//客商主键
private String hbbm_code;//客商编码
private String hbbm_name;//客商名称
private String lrr;//录入人主键
private String lrr_code;//录入人编码
private String lrr_name;//录入人名称
private String paydate;//支付日期5.1及以后版本支持
private String prepay;//预收付标志
private String qcbz;//是期初
private String qrr;//确认人主键
private String qrr_code;//确认人编码
private String qrr_name;//确认人名称
private String scomment;//备注
private String shr;//审核人主键
private String shr_code;//审核人编码
private String shr_name;//审核人名称
private String shrq;//审核日期
private String spzt;//审批状态
private String szxmid;//收支项目主键
private String szxmid_code;//收支项目编码
private String szxmid_name;//收支项目名称
private String ts;//时间戳V5.1开始支持
private String vouchid;//主表主键
private String wldx;//往来对象标识
private String xslxbm;//业务流程主键
private String xslxbm_code;//业务流程编码
private String xslxbm_name;//业务流程名称
private String ybje;//原币金额合计
private String ywbm;//交易类型主键
private String ywbm_code;//交易类型编码
private String ywbm_name;//交易类型名称
private String ywybm;//业务员主键
private String ywybm_code;//业务员编码
private String ywybm_name;//业务员名称
private String zdr;//制单人主键
private String zdr_code;//制单人编码
private String zdr_name;//制单人名称
private String zdrq;//制单日期
private String zyx1;//自定义项1
private String zyx10;//自定义项10
private String zyx11;//自定义项11
private String zyx12;//自定义项12
private String zyx13;//自定义项13
private String zyx14;//自定义项14
private String zyx15;//自定义项15
private String zyx16;//自定义项16
private String zyx17;//自定义项17
private String zyx18;//自定义项18
private String zyx19;//自定义项19
private String zyx2;//自定义项2
private String zyx20;//自定义项20
private String zyx21;//自定义项21
private String zyx22;//自定义项22
private String zyx23;//自定义项23
private String zyx24;//自定义项24
private String zyx25;//自定义项25
private String zyx26;//自定义项26
private String zyx27;//自定义项27
private String zyx28;//自定义项28
private String zyx29;//自定义项29
private String zyx3;//自定义项3
private String zyx30;//自定义项30
private String zyx4;//自定义项4
private String zyx5;//自定义项5
private String zyx6;//自定义项6
private String zyx7;//自定义项7
private String zyx8;//自定义项8
private String zyx9;//自定义项9
private String zzzt;//支付状态
}
@Data
public static class Arap_djfb {
private String bbhl;//本币汇率
private String bfyhzh;//本方银行帐号主键
private String bzbm;//币种主键
private String bzbm_code;//币种编码
private String bzbm_name;//币种名称
private String cinventoryid;//存货主键
private String cinventoryid_code;//存货编码
private String cinventoryid_name;//存货名称
private String contractno;//采购合同号 5.1开始支持
private String ddh;//订单号 5.0sp开始支持
private String def1;//自定义项名称1
private String def10;//自定义项名称10
private String def11;//自定义项名称11
private String def12;//自定义项名称12
private String def13;//自定义项名称13
private String def14;//自定义项名称14
private String def15;//自定义项名称15
private String def16;//自定义项名称16
private String def17;//自定义项名称17
private String def18;//自定义项名称18
private String def19;//自定义项名称19
private String def2;//自定义项名称2
private String def20;//自定义项名称20
private String def21;//自定义项名称21
private String def22;//自定义项名称22
private String def23;//自定义项名称23
private String def24;//自定义项名称24
private String def25;//自定义项名称25
private String def26;//自定义项名称26
private String def27;//自定义项名称27
private String def28;//自定义项名称28
private String def29;//自定义项名称29
private String def3;//自定义项名称3
private String def30;//自定义项名称30
private String def4;//自定义项名称4
private String def5;//自定义项名称5
private String def6;//自定义项名称6
private String def7;//自定义项名称7
private String def8;//自定义项名称8
private String def9;//自定义项名称9
private String deptid;//部门主键
private String deptid_code;//部门编码
private String deptid_name;//部门名称
private String dfbbje;//贷方本币金额
private String dfbbsj;//贷方本币税金
private String dfbbwsje;//贷方本币无税金额
private String dfybje;//贷方原币金额
private String dfybsj;//贷方原币税金
private String dfybwsje;//贷方原币无税金额
private String dfyhzh;//对方银行帐号主键
private String dj;//单价
private String djlxbm;//单据类型编码
private String fb_oid;//子表主键
private String fkyhmc;//付款银行帐号名称
private String fkyhzh;//付款银行帐号编码
private String fph;//发票号 5.0sp开始支持
private String hbbm;//客商主键
private String hbbm_code;//客商编码
private String hbbm_name;//客商名称
private String hsdj;//含税单价
private String jfbbje;//借方本币金额
private String jfbbsj;//借方本币税金
private String jfshl;//借方数量
private String jfybje;//借方原币金额
private String jfybsj;//借方原币税金
private String jfybwsje;//借方原币无税金额
private String jobid;//专项基本档案id(项目)
private String kslb;//扣税类别
private String skyhmc;//收款银行帐号名称
private String skyhzh;//收款银行帐号编码
private String sl;//税率
private String szxmid;//收支项目主键
private String szxmid_code;//收支项目编码
private String szxmid_name;//收支项目名称
private String ts;//时间戳V5.1开始支持
private String vouchid;//主表主键
private String wbfbbje;//借方本币无税金额
private String wldx;//往来对象标识 0客户1供应商2部门3业务员
private String ywybm;//业务员主键
private String ywybm_code;//业务员编码
private String ywybm_name;//业务员名称
private String zy;//摘要
private String zyx1;//自定义项1
private String zyx10;//自定义项10
private String zyx11;//自定义项11
private String zyx12;//自定义项12
private String zyx13;//自定义项13
private String zyx14;//自定义项14
private String zyx15;//自定义项15
private String zyx16;//自定义项16
private String zyx17;//自定义项17
private String zyx18;//自定义项18
private String zyx19;//自定义项19
private String zyx2;//自定义项2
private String zyx20;//自定义项20
private String zyx21;//自定义项21
private String zyx22;//自定义项22
private String zyx23;//自定义项23
private String zyx24;//自定义项24
private String zyx25;//自定义项25
private String zyx26;//自定义项26
private String zyx27;//自定义项27
private String zyx28;//自定义项28
private String zyx29;//自定义项29
private String zyx3;//自定义项3
private String zyx30;//自定义项30
private String zyx4;//自定义项4
private String zyx5;//自定义项5
private String zyx6;//自定义项6
private String zyx7;//自定义项7
private String zyx8;//自定义项8
private String zyx9;//自定义项9
}
}

View File

@ -0,0 +1,16 @@
package com.hzya.frame.plugin.lets.dingtalk.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FileModuleVO {
private String spaceId;
private String fileName;
private String fileSize;
private String fileType;
private String fileId;
}

View File

@ -0,0 +1,15 @@
package com.hzya.frame.plugin.lets.dingtalk.vo;
import lombok.Data;
@Data
public class SysFileVO {
/** 文件名称 */
private String fileName;
/** 文件类型 */
private String type;
/** 文件大小kb */
private Long fileSize;
/** base64编码 */
private String baseCode;
}

View File

@ -0,0 +1,7 @@
package com.hzya.frame.plugin.lets.plugin.dingtalk;
/**
* 为避免回调超时每天定时检查所有未审批完成的审批实例
*/
public class DingCallBackPluginInitializer {
}

View File

@ -49,4 +49,26 @@ OA:
letsofs:
url: http://39.98.168.188:30002/api/edi/u8c/ofs/in
appKey: 2097046829
secret: 35282f251476a3af4f00c7b36
secret: 35282f251476a3af4f00c7b36
#钉钉
DING:
CorpId: dingc5b16c82b7f25e64acaaa37764f94726
APIToken: c5530fb4faed387593ee17f4e6bb748d
#钉钉回调
call_back_token: q9llHUSJnD3br9iOSHcJh2I5qa4l3lau29vfv3Kwud9ExEKC1dWLQP2RQT9
call_back_aes_key: tMcQS7SdmvyeuFB6iKaxYG46WmbTfU2CjBDr3IRCqci
#Client ID (原 AppKey 和 SuiteKey)
APPKEY: ding5qfifcktfqfjlem0
#Client Secret (原 AppSecret 和 SuiteSecret)
AppSecret: BVuLOy_K0F8f5np69VuBeKdb1zfwqhuPsAV49lNbYVbx5TnfSTHjwEcad9Vwzfq1
#钉盘审批空间
SPACEID: 20852670637
#审批表单空间
APPROVE:
#特殊业务处理(新)
SPECIAL_SERVICE_PROCESSING_FILE_ID: 1
#采购付款申请(新)
PURCHASE_PAYMENT_REQUEST_NEW_FILE_ID: 1
#采购付款申请(店群专用)
PURCHASE_PAYMENT_REQUEST_SHOP_FILE_ID: 1

View File

@ -41,4 +41,26 @@ OA:
letsofs:
url: http://39.98.168.188:30002/api/edi/u8c/ofs/in
appKey: 2097046829
secret: 35282f251476a3af4f00c7b36
secret: 35282f251476a3af4f00c7b36
#钉钉
DING:
CorpId: dingc5b16c82b7f25e64acaaa37764f94726
APIToken: c5530fb4faed387593ee17f4e6bb748d
#钉钉回调
call_back_token: q9llHUSJnD3br9iOSHcJh2I5qa4l3lau29vfv3Kwud9ExEKC1dWLQP2RQT9
call_back_aes_key: tMcQS7SdmvyeuFB6iKaxYG46WmbTfU2CjBDr3IRCqci
#Client ID (原 AppKey 和 SuiteKey)
APPKEY: ding5qfifcktfqfjlem0
#Client Secret (原 AppSecret 和 SuiteSecret)
AppSecret: BVuLOy_K0F8f5np69VuBeKdb1zfwqhuPsAV49lNbYVbx5TnfSTHjwEcad9Vwzfq1
#钉盘审批空间
SPACEID: 20852670637
#审批表单空间
APPROVE:
#特殊业务处理(新)
SPECIAL_SERVICE_PROCESSING_FILE_ID: 1
#采购付款申请(新)
PURCHASE_PAYMENT_REQUEST_NEW_FILE_ID: 1
#采购付款申请(店群专用)
PURCHASE_PAYMENT_REQUEST_SHOP_FILE_ID: 1

View File

@ -30,7 +30,7 @@ public class BaseTest {
@Test
public void t01(){
// skuPluginInitializer.start("test0717001");
skuPluginInitializer.start("gdkfc778");
skuPluginInitializer.start("3664454000268");
// skuPluginInitializer.start("6971229882248");
}
@Test

View File

@ -95,7 +95,7 @@ public class TransferTest {
}
@Test
public void t41(){
adjustOutPluginInitializer.start("LETS-SH2024092000000004");
adjustOutPluginInitializer.start("LETS-SH2024101800017063");
}
@Test
public void t42(){
@ -138,7 +138,8 @@ public class TransferTest {
@Test
public void t81(){
refundOnlyPluginInitializer.start("2023-08-20","2023-08-25");
// refundOnlyPluginInitializer.start("2023-08-20","2023-08-25");
refundOnlyPluginInitializer.start("2024-09-30","2024-09-30");
}
////////////////////////////////////////////////////////////////////////////////////仅退款