前言

2019年12月到 2020年04月,小编参与到公司项目与第三方OA的对接,即华为云WeLink市场的对接工作,华为云市场、WeLink市场两部分对接工作,我负责华为云市场项目对接接口、另一个同事负责WeLink对接。主要经历了四个阶段:熟悉第三方文档需求及相关业务、研发、测试、上线并解决问题。

主要工作

第三方接口调用、开通第三方租户(租户包括本项目、第三方)

第三方接口调用

一、准备工作

  • 熟悉整体业务流程图

  • 项目分工
    小编负责:调试华为云市场生产接口(5个)、开通租户成功后发送消息(同事进行通讯录-组织结构人员同步)

    二、接口列表

  • 新购商品(5个接口共用一个接口,内部逻辑判断)

  • 商品续费

  • 商品过期

  • 商品资源释放

  • 商品升级

三、接口调试

调试过程中,不断熟悉接口定义,以及业务需求。

开发过程

一、第三方接口开发注意事项

  • 注意接口参数(必填与非必填,以及选取项目有用参数)
  • 接口传参时,使用自定义类,不要传httpServletRequest;
  • 明确参数定义,以及整套流程的参数关联性

二、代码精华

(一)第三方接口,安全校验
  1. 校验消息合法性
    (1)校验通知消息的合法性
    /*** 校验通知消息的合法性** @param request       http请求通知消息* @param accessKey     接入码* @param encryptLength 加密长度* @return 验证结果*/public static ResponseMessage verificateRequestParams(HttpServletRequest request, String accessKey, int encryptLength) {//解析出url内容Map<String, String[]> paramsMap = new HashMap<>(request.getParameterMap());String timeStamp = null;String authToken = null;String[] timeStampArray = paramsMap.get("timeStamp");if (null != timeStampArray && timeStampArray.length > 0) {timeStamp = timeStampArray[0];}String[] authTokenArray = paramsMap.remove("authToken");if (null != authTokenArray && authTokenArray.length > 0) {authToken = authTokenArray[0].replace(" ", "+");}//对剩下的参数进行排序,拼接成加密内容Map<String, String[]> sortedMap = new TreeMap<>();sortedMap.putAll(paramsMap);StringBuffer strBuffer = new StringBuffer();Set<String> keySet = sortedMap.keySet();Iterator<String> iter = keySet.iterator();while (iter.hasNext()) {String key = iter.next();String value = sortedMap.get(key)[0];if (value.contains(" ")) {value = value.replace(" ", "+");}strBuffer.append("&").append(key).append("=").append(value);}//修正消息体,去除第一个参数前面的&String reqParams = strBuffer.toString().substring(1);String key = accessKey + timeStamp;String signature;try {signature = generateResponseBodySignature(key, reqParams);} catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | UnsupportedEncodingException e) {// TODO Auto-generated catch blockreturn ResponseMessage.buildResponse(CodeUtils.OTHER_ERROR, "其它服务内部错误");}if (!authToken.equals(signature)) {return ResponseMessage.buildResponse(CodeUtils.FAIL_AUTH, "鉴权失败");}return ResponseMessage.buildResponse(CodeUtils.SUCCESS, "验证消息成功");}

(2)消息体签名

/*** 生成消息体签名** @param key  用户在isv console分配的accessKey,请登录后查看* @param body http响应的报文* @return 加密结果* @throws InvalidKeyException* @throws NoSuchAlgorithmException* @throws IllegalStateException* @throws UnsupportedEncodingException*/public static String generateResponseBodySignature(String key, String body) throws InvalidKeyException, NoSuchAlgorithmException, IllegalStateException, UnsupportedEncodingException {return base_64(hmacSHA256(key, body));}

(3)字节数组转字符串

 /*** 字节数组转字符串** @param bytes 字节数组* @return 字符串*/public static String base_64(byte[] bytes) {return new String(Base64.encodeBase64(bytes));}
  1. 返回消息体签名
    /*** Http Body签名** @param responseMessage* @param key* @param response* @return*/public static void setBodySign(ResponseMessage responseMessage, String key, HttpServletResponse response) throws IOException {// 消息体签名String bodySignature = null;String httpBody = JSONObject.toJSONString(responseMessage);try {bodySignature = generateResponseBodySignature(key, httpBody);} catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | UnsupportedEncodingException e) {log.error("error:" + e);}// 添加消息体签名signType = "HMAC-SHA256";StringBuffer bodySign = new StringBuffer();bodySign.append("sign_type=").append('"').append(signType).append('"').append(',').append("signature=").append('"').append(bodySignature).append('"');response.setHeader("Body-Sign", bodySign.toString());log.info("bodySign " + response.getHeader("Body-Sign"));}

3.一个通用的生产接口
(1)消息验证合法性、响应体消息签名、获取解析不同订单参数

 @GetMapping(value = "/activity")public ResponseMessage Tenancy(HttpServletRequest request, HttpServletResponse response) throws IOException {ResponseMessage responseMessage = EncDesCode.verificateRequestParams(request, CodeUtils.KEY, CodeUtils.ENCRYPT_LENGTH);String resultCode = responseMessage.getResultCode();// 转换订单实体OrderInfoBean orderInfoBean = EncDesCode.revertRequst(request);// 区分不同的activity订单行为if (resultCode.equals(CodeUtils.SUCCESS)) {switch (orderInfoBean.getActivity()) {case CodeUtils.ACTIVITY_NEW:responseMessage = orderService.newInstance(orderInfoBean);break;case CodeUtils.ACTIVITY_REFRESH:responseMessage = orderService.refreshInstance(orderInfoBean);break;case CodeUtils.ACTIVITY_EXPIRE:responseMessage = orderService.expireInstance(orderInfoBean);break;case CodeUtils.ACTIVITY_RELEASE:responseMessage = orderService.releaseInstance(orderInfoBean);break;case CodeUtils.ACTIVITY_UPGRADE:responseMessage = orderService.upgrade(orderInfoBean);break;default:log.info("default");}EncDesCode.setBodySign(responseMessage, CodeUtils.KEY, response);}return responseMessage;}

(2)获取request中的参数,保存参数到自定义类OrderInfoBean

 /*** 获取request中的参数** @param request* @return*/public static OrderInfoBean revertRequst(HttpServletRequest request) {Map<String, String[]> paramMap = request.getParameterMap();Map<String, String> params = new HashMap<>();OrderInfoBean orderInfoBean;for (String key : paramMap.keySet()) {params.put(key, paramMap.get(key)[0]);}for (String param : params.keySet()) {params.get(param);}orderInfoBean = map2Bean(params, OrderInfoBean.class);//获取request中加密的参数值return decryptParams(orderInfoBean);}/*** Map转换层Bean,使用泛型免去了类型转换的麻烦。** @param <T>* @param map* @param myClass* @return*/private static <T> T map2Bean(Map<String, String> map, Class<T> myClass) {T bean = null;try {bean = myClass.newInstance();BeanUtils.populate(bean, map);} catch (InstantiationException e) {log.error("error:" + e);} catch (IllegalAccessException e) {log.error("error:" + e);} catch (InvocationTargetException e) {log.error("error:" + e);}return bean;}/*** 解密参数** @return*/private static OrderInfoBean decryptParams(OrderInfoBean orderInfoBean) {String saasExtendParams = orderInfoBean.getSaasExtendParams();String mobilePhone = orderInfoBean.getMobilePhone();if (StringUtils.isNotEmpty(saasExtendParams)) {saasExtendParams = saasExtendParams.replace(" ", "+");saasExtendParams = base_64Decode(saasExtendParams.getBytes());log.info("DES saasExtendParams:" + saasExtendParams);String[] params = JSON.parseObject(saasExtendParams, String[].class);ExtendParam extendParam;WeLinkTenantInfo weLinkTenantInfo;log.info("saasExtendParams:{}", params);for (String paramObject : params) {extendParam = JSONObject.parseObject(paramObject, ExtendParam.class);if (extendParam.getName().equals(CodeUtils.CONTACT_NAME)) {orderInfoBean.setContactName(extendParam.getValue());orderInfoBean.setTenantType(CodeUtils.TENANT_TYPE_HUAWEIYUN);}if (extendParam.getName().equals(CodeUtils.COMPANY)) {orderInfoBean.setCompanyName(extendParam.getValue());}// WeLink开放平台开发的商品所需参数if (extendParam.getName().equals(CodeUtils.PLATFORM_PARAMS)) {weLinkTenantInfo = JSONObject.parseObject(extendParam.getValue(), WeLinkTenantInfo.class);orderInfoBean.setTenantType(CodeUtils.TENANT_TYPE_WELINK);log.info("weLinkTenantInfo:{}",weLinkTenantInfo);orderInfoBean.setTenantId(weLinkTenantInfo.getTennantId());orderInfoBean.setTenantName(weLinkTenantInfo.getTenantName());orderInfoBean.setWeLinkUserId(weLinkTenantInfo.getUserId());}}log.info("orderInfoBean:" + orderInfoBean);}if (StringUtils.isNotEmpty(mobilePhone)) {mobilePhone = mobilePhone.replace(" ", "+");orderInfoBean.setAdminAccountAES(mobilePhone);mobilePhone = decryptMobilePhoneOrEMail(CodeUtils.KEY, mobilePhone, CodeUtils.ENCRYPT_LENGTH);orderInfoBean.setMobilePhone(mobilePhone);log.info("DES mobilePhone:" + mobilePhone);}return orderInfoBean;}
(二)开通租户

开通租户,使用预置租户的思想,租户开通即用预置(不用等待),开通成功发送mq消息(部分代码)

 //  获取预置企业if (cloudEntWelinkPreDone.size() > 0) {cloudEntWelink = cloudEntWelinkRepository.findByStatusAndTenantIdOrderByCreatedOn(CodeUtils.PRE_DONE, null).get(0);// 存放redis数据redisDao.del(CodeUtils.PRE_WELINK_ENT_LIST);for (CloudEntWelink cloudEntWelinkRedis : cloudEntWelinkPreDone) {redisDao.lpush(CodeUtils.PRE_WELINK_ENT_LIST, cloudEntWelinkRedis.getEntCode());}log.info("redisDao.llen{{}}", redisDao.llen(CodeUtils.PRE_WELINK_ENT_LIST));// 重试获取entCode,避免冲突while (1 != redisDao.setnx(cloudEntWelink.getEntCode(), "-1", 30)) {if (redisDao.llen(CodeUtils.PRE_WELINK_ENT_LIST) > 0) {cloudEntWelink.setEntCode(redisDao.rpop(CodeUtils.PRE_WELINK_ENT_LIST));redisDao.rpop(CodeUtils.PRE_WELINK_ENT_LIST);log.info("rpop llen{{}}", redisDao.llen(CodeUtils.PRE_WELINK_ENT_LIST));} else {return ResponseMessage.buildResponse(CodeUtils.NO_INSTANCE, "no instance");}}} else {return ResponseMessage.buildResponse(CodeUtils.NO_INSTANCE, "no instance");}

小结

小编在实现功能以及上线调试,有一些经验之谈:开发和解决问题的过程中,即时记录开发重点,打印重要参数输出日志、建立思维导图,树立全局思维,能够快速定位并解决问题。
华为云市场文档

【第三方OA对接】01华为云WeLink对接项目总结相关推荐

  1. 华为云WeLink端云协同会议是什么?

    华为云WeLink端云协同会议,通过华为云WeLink可实现,内网会议用户与公网会议系统,公网会议用户等的联接,并提供统一的会控管理平台.实现内外部会议用户无缝对接. 华为云WeLink源自华为数字化 ...

  2. 沟通不受限,审批不堆积 深大智能通过华为云WeLink+OA实现办公提速

    浙江深大智能集团专注于提供智慧旅游全产业链产品创新.产业规划和运营服务,致力于发展成为拥有旅游业大数据的信息化与运营综合服务商.自1996年成立以来,已成为集行业整体解决方案.智慧旅游公众服务支撑平台 ...

  3. 为什么说华为云WeLink是“最硬核”的协同办公工具?

    华为云WeLink ( HUAWEI CLOUD WeLink),是华为云企业打造的协同办公平台,源自华为19万员工的数字化办公实践,支持iOS.Android和Windows三大平台,融合消息.邮件 ...

  4. 华为云WeLink是什么软件?什么远程办公软件好用?

    华为云WeLink是什么?什么远程办公软件好用? 经过自己的研究测评,华为云WeLink它是华为内部打磨多年的协同办公平台.远程办公软件.移动办公平台.协同办公软件,来源华为19万员工的数字化办公实践 ...

  5. 畅联“5机”,华为云WeLink勇当数字化联接器

    2020年9月23日,华为全联接大会在上海举行,本次大会以"5机协同,共创行业新价值"为主题,与千行百业共同探讨合作共赢之道.在当天下午的"华为云协同办公"专题 ...

  6. 智能工作平台那么多,我们为什么选择了华为云WeLink?

    "市场上的智能工作平台那么多,我们为什么选择了华为云WeLink?"在12月26日举行的华为云WeLink智能工作平台发布会上,中软国际高级副总裁曹雁这样自问,同时也把问题抛给了参 ...

  7. 华为云welink考试试题_华为内部开启WeLink项目,华为云是这样考虑的-通信/网络-与非网...

    协同办公市场竞争激烈 华为云 WeLink 是华为旗下智能工作平台,它融合消息,邮件,会议.音视频.云空间.小程序等服务,可助力用户随时.随地.通过各类终端设备等实现协作办公.华为还宣布携手合作伙伴成 ...

  8. 华为云WeLink正式发布,这是更懂企业的智能工作平台一枚!

    今日,华为云在京发布智能工作平台WeLink. 对此,华为云副总裁.联接与协同业务总裁薛浩表示:"华为云WeLink源自华为数字化转型实践,是更懂企业的智能工作平台,具备智能高效.安全可靠. ...

  9. 华为云WeLink:让智能办公行走云端

    北大学者陈春花有一种观点认为,"效率来源于协同而非分工". 传统的企业通过流程化的分工使得劳动效率最大化得以实现,而在互联网时代,企业更需要解决的是整体效率,所以高效的智能办公带来 ...

最新文章

  1. SpringMVC框架 学习DAY_01:框架概括 / 简易应用 / 核心执行流程图 /在框架下显示HTML模板页面/ 接受请求
  2. 基于IOC的GUI框架设计与实现
  3. c语言章节作业,怎么用C语言编写菜单?
  4. redis 主从模式_Redis主从模式部署文档
  5. C/C++ atoi函数 - C语言零基础入门教程
  6. 【Vue.js源码解析 二】-- 虚拟 DOM
  7. 如何测试连接MsSQL数据库-------UDL文件
  8. ES6-ES11新特性_ECMAScript相关名词介绍_---JavaScript_ECMAScript工作笔记002
  9. 黑客新技术在ATM钻洞就让其吐钱
  10. 手机论文查重软件哪个靠谱?
  11. 中呜机器人编程视频教程_中鸣快车编程入门篇—5.1补充的知识.doc
  12. photoshop标尺工具_工具设置:PhotoShop
  13. 关于闰年和平年的每个月有多少天
  14. 京东用户行为数据分析——以Python为主要工具
  15. 一篇关于批处理文件的经典文章
  16. 请教一下水卡校验算法
  17. Android 多厂商推送集成
  18. 输出魔方阵,所谓魔方阵是指这样的方阵,它的每一行,每一列和对角线之和均相等。例如,三阶魔方阵为
  19. 重磅!京东云区块链数据服务(BDS)正式开源
  20. Kaggle与机器学习流程

热门文章

  1. 短信语音双保障,验证通通不是事儿
  2. 2022-2028中国祛痘贴市场现状研究分析与发展前景预测报告
  3. html实现图片切割,利用CSS切割图片技术来动态显示图片
  4. 动态建立Vxlan隧道实现跨子网互访实验配置(分布式网关单租户多子网场景)
  5. pdf做成翻页电子书_如何一分钟将PDF制作成电子书?这个热门电子书刊制作软件推荐给你!...
  6. iphone,ipad模拟器截图(小方法)
  7. FL Studio21电脑版免费音乐编曲宿主软件下载
  8. 软件立项评审报告模板
  9. vue前端下载本地excel文件
  10. DELL服务器安装windows server 2008 R2系统