webapi 上传文件失败 钉钉_webapi call another webapi - CSDN
精华内容
参与话题
  • 官方提供的接口说明和示例是基于java的,却没有对具体的header作出更详细的说明,导致很难使用C#语言转换,几经测试,总算找到了个... /// 比如钉钉上传媒体文件使用的是media,该值用于服务端接收到数据时读取该keyna

    官方提供的接口说明和示例是基于java的,却没有对具体的header作出更详细的说明,导致很难使用C#语言转换,几经测试,总算找到了个不是太完整的解决方法,代码如下。

    /// <summary>
            ///POST文件
            /// </summary>
            /// <param name="url"></param>
            /// <param name="timeout"></param>
            /// <param name="fileKeyName">比如钉钉上传媒体文件使用的是media,该值用于服务端接收到数据时读取该keyname之后相关的数据。</param>
            /// <param name="fileBuffer">文件数据</param>
            /// <param name="fileName">文件名</param>
            /// <returns></returns>
            public static string Post(string url,
                                      string fileKeyName,
                                      byte[] fileBuffer,
                                      String fileName,
                                      int timeout)
            {
    
                var boundary = SecurityHelper.GenerateRadomStr();
                WebClient webClient = new WebClient();
                webClient.Headers.Add("Content-Type", string.Format("multipart/form-data; boundary={0}", boundary));
                string fileFormdataTemplate =
                                "\r\n--" + boundary +
                                "\r\nContent-Disposition:form-data;name=\"{0}\";filename=\"{1}\"" +
                                "\r\nContent-Type:application/octet-stream" +
                                "\r\n\r\n";
                string formDataHeader = String.Format(fileFormdataTemplate, "media", fileName);
                byte[] formDataHeaderBuffer = Encoding.UTF8.GetBytes(formDataHeader);
    
                string begin = $"--{boundary}\r\n";
                byte[] beginBuffer = Encoding.UTF8.GetBytes(begin);
    
                string end = $"\r\n--{boundary}--\r\n";
                byte[] endBuffer = Encoding.UTF8.GetBytes(end); ;
    
                byte[] dataStream = new byte[formDataHeaderBuffer.Length + beginBuffer.Length + fileBuffer.Length + endBuffer.Length];
                formDataHeaderBuffer.CopyTo(dataStream, 0);
                beginBuffer.CopyTo(dataStream, formDataHeaderBuffer.Length);
                fileBuffer.CopyTo(dataStream, formDataHeaderBuffer.Length + begin.Length);
                endBuffer.CopyTo(dataStream, formDataHeaderBuffer.Length + begin.Length + fileBuffer.Length);
                var returnBuffer = webClient.UploadData(url, "POST", dataStream);
                Encoding encode = Encoding.UTF8;
                string resultJson = encode.GetString(returnBuffer);
                return resultJson;
            }
    
    
    
    调用的代码

    int timeout = 1000 * 60 * 5;
    String resultJson = RequestHelper.Post(requestUrl, "media", fileBuffer, fileName, timeout);//media是固定的字符串
    其中fileBuffer为文件的字节流,requestUrl按照接口的方式找接而成https://oapi.dingtalk.com/media/upload?access_token=ACCESS_TOKEN&type=TYPE。
    目前测出,type=file时是可行的,type=image时不知为何总是提示【系统繁忙】,还请路过的大家们能够提供解决方案。

    在上传成功后,需要再下载下来,下载时,由于成功和失败的返回数据不一样,所以需要先对前面的若干字节进行了试探处理,之后再依据试探结果继续处理,代码如下

    附上读取媒体文件的方法
    #region FetchMediaFile Function
            /// <summary>
            /// 获取媒体文件
            /// </summary>
            /// <param name="mediaId">媒体文件的id</param>
            /// <returns></returns>
            public static DDMediaFetchResult FetchMediaFile(string mediaId)
            {
                DDMediaFetchResult result = null;
    
                string apiurl = FormatApiUrlWithToken(Urls.media_get);
                apiurl = $"{apiurl}&{Keys.media_id}={mediaId}";
                WebClient webClient = new WebClient();
                var data = webClient.DownloadData(apiurl);
    
               int testHeaderMaxLength = 100;
            var testHeaderBuffer = new byte[(data.Length < testHeaderMaxLength ? data.Length : testHeaderMaxLength)];
                Array.Copy(data, 0, testHeaderBuffer, 0, testHeaderBuffer.Length);
                Encoding encoder = Encoding.UTF8;
                String testHeaderStr = encoder.GetString(testHeaderBuffer);
                if (testHeaderStr.StartsWith("--"))
                {//正常返回数据时,第一行数据为分界线,而分界线必然以"--"开始.
                    var tempArr = testHeaderStr.Split(new String[] { Environment.NewLine }, StringSplitOptions.None);
                    string boundary = tempArr[0] + Environment.NewLine;
                    int boundaryByteLength = encoder.GetBytes(boundary).Length;
                   byte[] destData = new byte[data.Length-boundaryByteLength];
               Array.Copy(data, boundaryByteLength, destData, 0, destData.Length);
               result = new DDMediaFetchResult();
               result.ErrCode = DDErrCodeEnum.OK;
               result.ErrMsg = "OK";
              result.Data = destData;
    
                    const string Content_Length = "Content-Length";
                    if (webClient.ResponseHeaders == null || (!webClient.ResponseHeaders.AllKeys.Contains(Content_Length)))
                    {
                        result.FileLength = -1;
                    }
    
                    var lengthStr = webClient.ResponseHeaders[Content_Length];
                    int length = 0;
                    if (int.TryParse(lengthStr, out length))
                    {
                        result.FileLength = length;
                    }
                    else
                    {
                        result.FileLength = 0;
                    }
    
                    const string Content_Type = "Content-Type";
                    if (webClient.ResponseHeaders == null || (!webClient.ResponseHeaders.AllKeys.Contains(Content_Type)))
                    {
                        result.FileType = "unknown";
                    }
                    else
                    {
                        result.FileType = webClient.ResponseHeaders[Content_Type];
                    }
                }
                else
                {
                    string resultJson = encoder.GetString(data);
                    result = DDRequestAnalyzer.AnalyzeResult<DDMediaFetchResult>(resultJson);//将resultJson反序化为DDMediaFetchResult
                }
                return result;
            }
            #endregion
    
    /// <summary>
        /// 媒体文件获取结果
        /// </summary>
        public class DDMediaFetchResult 
        {
        /// <summary>
            /// 错误码
            /// </summary>
        public int ErrCode{get;set;
    
            /// <summary>
            /// 错误消息
            /// </summary>
            public string ErrMsg { get; set; }}
    
            /// <summary>
            /// HTTP响应头
            /// </summary>
            public Dictionary<string, string> Header { get; set; }
    
            /// <summary>
            /// 获取的数据
            /// </summary>
            public byte[] Data { get; set; }
    
            /// <summary>
            /// 文件长度
            /// </summary>
            public int FileLength { get; set; }
    
            /// <summary>
            /// 文件类型
            /// </summary>
            public String FileType { get; set; }
                    
        }
    将收到的数据存成文件即可,比如将前面上传得到的mediaid传入后,下载得到了下面的图

    附生成随机串的方法

       #region GenerateRadomStr
            /// <summary>
            /// 生成随机字符串
            /// </summary>
            /// <param name="length">随机串的长度</param>
            /// <returns></returns>
            public static string GenerateRadomStr(int length = 16)
            {
                string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
                string str = "";
                Random rad = new Random();
                for (int i = 0; i < length; i++)
                {
                    str += chars.Substring(rad.Next(0, chars.Length - 1), 1);
                }
                return str;
            }
            #endregion

    欢迎打描左侧二维码打赏。

    转载请注明出处。






    展开全文
  • 钉钉小程序开发注意事项

    千次阅读 2019-09-09 16:15:57
    在dd.httpRequest({})中无法获取this对象,需要在请求外面定义变量去过渡钉钉变量 this.setData({ 'array[0]': 1, 'obj.x':2, }); 2.dataset在组件中可以定义数据,这些数据将会通过事件传递给逻辑层。 ...

    1. setData 改变对应的this.data的值

    注意:不要直接修改this.data对应的数据。

    在dd.httpRequest({})中无法获取this对象,需要在请求外面定义变量去过渡钉钉变量

    this.setData({
      'array[0]': 1,
      'obj.x':2,
    });

     2. dataset 在组件中可以定义数据,这些数据将会通过事件传递给逻辑层。

    <view data-alpha-beta="1" data-alphaBeta="2" onTap="bindViewTap"> DataSet Test </view>
    
    Page({
      bindViewTap:function(event){
        event.target.dataset.alphaBeta === 1 // - 会转为驼峰写法
        event.target.dataset.alphabeta === 2 // 大写会转为小写
      }
    })

    3. 在page中定义的分页面,页面元素不能使用.page类名,否则会与app的样式冲突。

    4. 绘制F2图表,图表图例 legend不够显示,可以考虑添加css样式 padding 值。tooltip提示信息遮挡部分内容时,考虑 设置 offsetY 值

    5. 钉钉中使用iconFont

    第一种:引用iconfont第三方域名在线链接
    进入iconFont官网,在 【Unicode】 选项中, 选择【查看在线链接】
    选择相对应的icon图标,【取最后四位】使用

    //1.css
    @font-face {    
      font-family: 'iconfont'; /* project id 800366 */
      src: url('//at.alicdn.com/t/font_800366_tdlza9osbci.eot');
      src: url('//at.alicdn.com/t/font_800366_tdlza9osbci.eot?#iefix') format('embedded-opentype'),
             url('//at.alicdn.com/t/font_800366_tdlza9osbci.woff') format('woff'),
             url('//at.alicdn.com/t/font_800366_tdlza9osbci.ttf') format('truetype'),
             url('//at.alicdn.com/t/font_800366_tdlza9osbci.svg#iconfont') format('svg');
    }
    .iconfont {
      font-family: "iconfont" !important;
      font-size: 60rpx;
    }
    .icon-mediation-case:before {content: "\e6a0"}
    //2.代码中引用
    <view class="iconfont icon-backColor-case icon-mediation-case"></view>   

     

    第二种:直接下载转化为base64使用

    1. 进入iconFont官网,在 【Unicode】 选项中, 选择【下载至本地】
    2. https://transfonter.org/,将下载文件中的【iconfont.ttf】文件转化为base64文件,下载后取出【stylesheet.css】复制到本地【app.acss】中进行复用。

    6.发布钉钉流程:

    1. 编辑器左上角选择 【关联应用】【体验组织】后,右上角 【上传】
    2. 发布体验版 钉钉开发平台 ,选择登录管理后台的企业和管理密码,选择【应用开发】,找到相应的版本将发布的版本设置为 体验版。
    3. 发正式版本都需要在编辑器左上角选择【企业内部应用】

     7. 钉钉websocket

    // 钉钉 websocket 
    Page({
      data: {},
      onLoad() {
        dd.showLoading({ content: '数据加载中...' });
        dd.onSocketClose(function(res) {
          console.log('WebSocket 已关闭!')
        })
    
        let url = app.globalData.baseUrl_ws;
        dd.connectSocket({ url: url });
          
        dd.onSocketOpen(function(res) {
          // dd.alert({ content: '连接已打开!' });
        });
    
        dd.onSocketError(function(res) {
          // dd.alert('WebSocket 连接打开失败,请检查!' + res);
        });
    
        dd.onSocketMessage(function(res) {
          dd.hideLoading();
          let params = JSON.parse(res.data);
        })  
      }
    })
    // 原生webSocket
    let url = app.globalData.baseUrl_ws;
    let ws = new WebSocket(url);
    ws.onopen = function () {
        console.log('连接成功');
    }
    ws.onmessage = function (event) {
        dd.hideLoading();
        let params = JSON.parse(event.data);
    }

     

    展开全文
  • 5、钉钉平台

    2020-04-08 09:41:09
    文章目录应用管理运行环境应用类型编程模式风神工作台基础应用信息接口权限应用发布钉钉集成参数设置钉钉接口地址常量钉钉集成登录钉钉事件回调接口注册通讯录事件回调群会话事件回调签到事件回调审批事件回调日志...

    文章目录

    应用管理

    钉钉端开发小程序/微应用,实现钉钉与LCP平台的深度集成

    运行环境

    当前存在两套钉钉环境

    环境名称 适用范围 描述 备注
    风神测试 DEV/SIT 包含全套组织架构,包含信息技术部员工,少量其它部门参与测试的员工 可将公司员工手机号作替换,实现无工号人员正常使用本环境
    风神物流 UAT/PROD 包含全套组织架构,包含公司所有员工,外部员工

    应用类型

    钉钉开放平台支持小程序和H5微应用两种类型的应用开发。

    • 小程序是指使用钉钉自有语法,使用专用工具开发移动端应用。小程序暂不支持PC端部署。
    • 微应用是指传统的H5方式开发的移动端应用。
    • 原则上选择微应用,即H5原生开发。自行展开H5开发具备最大的开发灵活性。
    对比项 小程序 微应用
    加载性能 首次使用略慢,后续加载快 受到很多因素影响,优化不够好,容易慢
    使用体验 非常顺滑,接近 Native 容易卡顿
    页面跳转,返回 和 Native 的效果一样 做不到和 Native 一样的体验
    开发环境搭建 提供 IDE,快速创建项目 成本高
    调试 提供 IDE,可以在电脑上调试大部分功能 在电脑上只能调 UI,涉及到钉钉的 jsapi,必须真机调试
    使用开源 UI 组件 目前不支持 支持
    使用 npm 包 支持 支持
    模块化组织代码 支持 小程序 特有的模块化 使用 vue, React 等框架可以轻松获得模块化支持
    灰度发布 钉钉提供 需要自己实现
    CDN 小程序包默认在CDN 需要开发者自己购买相关服务
    开发个人应用 支持 不支持
    应用离线化 支持 不支持

    编程模式

    当前存在两套编程模式

    名称 运行环境 用途 调用方式
    前端API 钉钉客户端 从客户端发出对钉钉服务器的各类请求,构建客户端界面,调用设备API 小程序API,在钉钉iOS客户端、Android客户端均可正常使 \ H5微应用API,使用script标签引入JSAPI,不同的方法有不同的使用范围(Android,IOS,PC)
    服务端API 后台环境 从服务端后台发出对钉钉服务器的各类请求,同步应用数据,实现后台管理等 支持Java,PHP,.Net,Python后台运行环境。

    前端API

    容器 弹窗 摇一摇 设备 日期&月历
    导航栏 UI控件 获取免登授权码 扫码 存储
    地图 业务 文件 图片 打开新页面
    电话 发钉 通讯录选人 外部联系人 自定义联系人
    会话 钉盘 音频接口 支付 数据加解密
    旋转屏幕 统一跳转协议 页面事件监听

    后台API

    身份验证 通讯录管理 消息通知 智能工作流 考勤
    智能人事 日志 Ding日程 签到 公告
    钉钉运动 应用管理 群机器人 文件存储 业务事件回调

    风神工作台

    在每个人的钉钉工作台,显示 风神工作台 应用图标

    • 在PC端点击后,免密进入PC端统一门户页面
    • 手机端点击后,免密进入移动端待办面板页面

    在这里插入图片描述

    进入钉钉工作台管理后台,创建微应用。选择企业内部自主开发,开发模式选择开发应用,开发应用类型选择微应用,录入名称与Logo,应用首页链接输入移动端待办面板页面链接,PC端首页地址输入PC端统一门户页面,最后录入服务器出口IP,之后发布至所有用户即可。
    在这里插入图片描述

    默认情况下,新加入的应用显示在钉钉工作台最下端。管理员可通过移动端管理工作台功能,调整显示分组与顺序。
    在这里插入图片描述

    基础应用信息

    属性 描述
    应用名称 应用的名字
    应用类型 开发应用-微应用:微应用开发类型 \ 开发应用-小程序:小程序开发类型 \ 快捷链接:仅作地址跳转
    应用logo 应用的图标
    应用简介 应用的简单描述
    应用首页地址 用户在手机端钉钉工作台上打开这个应用并显示该链接内容。
    PC端管理地址 用户在PC版钉钉工作台上打开这个应用并显示该链接内容。
    可使用范围 能够使用本应用的员工范围
    AgentId 在创建应用时,系统会自动生成一个AgentId,可用于发送企业会话消息等场景。
    服务器出口IP 为支撑前端应用而布署的服务器授予访问权限
    AppKey 在创建应用时,系统会自动分配一对AppKey和AppSecret,该AppKey是应用开发过程中的唯一性标识。
    AppSecret AppSecret和上面AppKey一同生成,使用AppKey和AppSecret来换取access_token。

    不同环境,出口IP与各页面地址配置如下。内网需要同步作Nginx配置,将外网域名请求转发到正确的内网服务器端口上

    环境 出口IP 应用首页链接 PC端首页地址
    DEV 219.135.191.*,218.107.3.* https://dingtest.fslgz.com/自定义路径 https://dingtest.fslgz.com/自定义路径
    SIT 219.135.191.*,218.107.3.* https://dingtest.fslgz.com/portal/activiti/system_task_collect.html https://dingtest.fslgz.com/portal/
    UAT 58.248.166.*,14.23.175.* https://app-uat.fslgz.com/portal/activiti/system_task_collect.html https://app-uat.fslgz.com/portal/
    PROD 58.248.166.*,14.23.175.* https://app.fslgz.com/portal/activiti/system_task_collect.html https://app.fslgz.com/portal/

    接口权限

    后台服务端向钉钉服务器请求数据的接口权限

    名称 描述 默认状态
    基础权限
    身份验证 获取员工的基本信息,用于登录系统/应用
    消息通知 获取企业的消息通道给企业及员工发送消息
    高级权限-企业通讯录
    通讯录只读权限 获取企业员工通讯录数据的权限
    通讯录编辑权限 获取企业员工通讯录数据、新增、删除、修改企业通讯录的权限
    手机号码信息 获取授权范围内的成员手机号码信息
    邮箱等个人信息 获取授权范围内的成员信息(如邮箱、工作地点、扩展字段等)
    未登录员工列表 企业使用此接口可查询指定日期内未登录钉钉的企业员工列表
    手机号获取userid 通过手机号获取其所对应员工的userid
    高级权限-微应用
    钉盘 获取钉盘文件的存储、删除、更新的权限
    签到 获取企业员工签到数据的权限
    考勤 获取企业员工考勤数据的权限
    审批 获取企业人员审批数据、发起审批的权限
    日志 获取企业员工日志数据的权限
    公告 获取企业员工公告数据的权限
    钉钉运动 获取企业员工运动数据的权限
    智能人事 获取企业员工智能人事数据的权限
    企业会话 获取、管理企业会话数据的权限
    DING日程 管理钉钉日程数据权限

    应用发布

    发布后,该应用将会出现在钉钉的工作台上,企业组织内部的用户将会看到该应用。

    名称 描述
    全部员工 组织内所有员工都可使用
    部份员工 选定范围内的员工才能使用,可按部门或人员选择
    仅限管理员 仅管理员才能使用

    钉钉集成

    将钉钉SDK集成至本平台,简化钉钉对接难度

    参数设置

    # 消息通知相关,当前系统的路径
    sys.webroot.url=https://dingtest.fslgz.com/portal
    
    # 钉钉配置
    dingtalk.corp.id=ding6b6ffebcd75ef63335c2f4657eb6378f
    dingtalk.app.key=dinggkwxz61kgu7vvw8a
    dingtalk.app.secret=VnA-wL-bqviMkIsw_Qn81TqH-Q7fqzCchOglmBjplbjeBZvdzH5MZ8e66vDNefo-
    dingtalk.default.agentid=280426666
    

    当项目不作修改时,平台内置的默认参数来源如下

    环境名称 应用名称
    DEV 风神工作台SIT
    SIT 风神工作台SIT
    UAT 风神工作台UAT
    PROD 风神工作台

    钉钉接口

    com.fsl.lcp.dingding.service.IDingDingService:提供调用钉钉服务的服务端接口

    方法 参数 说明
    身份方法
    String getAccessToken() 获取access_token,默认过期时间为6900秒。一般情况下,使用者不需使用此方法。LCP钉钉接口所有方法均实现了access_token的自动刷新
    String getJsTicket() 获取js_ticket,默认过期时间为6900秒。一般情况下,使用者不需使用此方法。LCP钉钉接口所有方法均实现了js_ticket的自动刷新
    JsApiSignatureVo getJsApiSignature(JsApiSignatureVo apiSignatureVo) JsApiSignatureVo:签名参数。包含 使用票据,随机字符串,时间戳,页面地址生成本页面唯一的签名,并返回本次使用的企业Id,应用Id,随机字符串,时间戳,页面地址,签名
    阿里服务调用
    T execute(String serviceUrl, TaobaoRequest request) serviceUrl:服务地址 \ request:请求参数 调用钉钉接口获取返回值。有四个方法重载
    消息推送
    OapiMessageCorpconversationAsyncsendV2Response sendWorkMsg(NotifyMessageRecord messageRecord) messageRecord:钉钉消息 发送钉钉工作通知,如果messageRecord对象eUrl为空,则发送markdown消息,否则为卡片消息
    void sendWorkMsgAsync(NotifyMessageRecord messageRecord) messageRecord:钉钉消息 异步发送钉钉工作通知
    ResponseData recallTodoAndCopyNotify(Long agentId, Long notifyTaskId) agentId:应用Id \ notifyTaskId:钉钉工作通知Id 撤回工作通知消息.
    void sendWebHookMsg(NotifyMessageRecord messageRecord) messageRecord:钉钉消息 发送钉钉webHook消息(群机器人消息)
    void sendWebHookMsgAsync(NotifyMessageRecord messageRecord) messageRecord:钉钉消息 用异步,多线程方式发送钉钉webHook消息(群机器人消息)
    工作流调度
    String createProcessTemplate(Long agentId) agentId:应用ID 创建钉钉智能工作流模板
    ResponseData createProcessInstance(DdProcessInst processInst, boolean saveData) processInst:钉钉流程实例 \ saveData:是否保存钉钉流程数据 创建钉钉智能工作流实例
    ResponseData updateProcessInstance(DdProcessInst processInst, boolean saveData) processInst:钉钉流程实例 \ saveData:是否保存钉钉流程数据 更新钉钉智能工作流实例
    ResponseData createProcessTask(DdProcessTask processTask, boolean saveData) processTask:钉钉任务实例 \ saveData:是否保存钉钉任务数据 创建钉钉工作流待办任务.
    ResponseData updateProcessTask(DdProcessTask processTask, boolean saveData) processTask:钉钉任务实例 \ saveData:是否保存钉钉任务数据 更新钉钉工作流待办任务.
    void updateProcessCopyTask(DdProcessTask processTask, boolean saveData) processTask:钉钉任务实例 \ saveData:是否保存钉钉任务数据 更新钉钉工作流抄送类型的待办任务.
    OapiProcessWorkrecordTaskQueryResponse.PageResult queryDdProcessTaskList(String employeeCode, Long offset, Long count, Long status) employeeCode:工号 \ offset:页码 \ count:每页数量 \ status:待办类型。0表示待处理,-1表示已经移除 查询钉钉工作流待办任务列表
    void recallTodoAndNotify(String employeeCode, String taskId) employeeCode:工号 \ taskId:钉钉任务实例Id 撤回当前用户和taskId匹配的数据的待办通知消息和钉钉待办列表
    void recallCopyAndNotify(String employeeCode, String processInstanceId) employeeCode:工号 \ processInstanceId:钉钉流程实例Id 撤回当前用户和taskId匹配的数据的待办通知消息和钉钉待办列表.
    信息查询
    OapiUserGetResponse queryUserInfo(String userId) userId:钉钉 user_id 基于钉钉 user_id查询钉钉用户信息
    List queryAllDdDeptRelation() 查询所有钉钉部门关联信息
    List queryDdDeptRelationList(DdDeptRelation ddDeptRelation, int page, int pageSize) ddDeptRelation:钉钉部门关联表查询条件 \ page:当前页数 \ pageSize:每页行数 分页查询钉钉部门关联信息
    DdDeptRelation queryDdDeptRelation(DdDeptRelation ddDeptRelation) ddDeptRelation:钉钉部门关联表查询条件 查询单个钉钉部门关联信息
    List getFunctionByEmployeeCode(String empCode, String eid) empCode:员工编码 \ eid:功能模块编码 基于模块编码与员工编码查询功能清单

    地址常量

    com.fsl.lcp.dingding.constant.UrlConstant:封装常用的钉钉服务地址信息

    接口名称 接口 地址
    获取access_token GET_TOKEN https://oapi.dingtalk.com/gettoken
    获取用户详情(GET) USER_GET https://oapi.dingtalk.com/user/get
    通过免登授权码和 access_token 获取用户的 userid(GET) USER_GET_USERINFO https://oapi.dingtalk.com/user/getuserinfo
    获取部门用户userid列表(GET) USER_GET_DEPT_MEMBER https://oapi.dingtalk.com/user/getDeptMember
    获取部门用户(GET) USER_SIMPLE_LIST https://oapi.dingtalk.com/user/simplelist
    获取部门用户详情(GET) USER_LIST_BY_PAGE https://oapi.dingtalk.com/user/listbypage
    获取管理员列表(GET) USER_GET_ADMIN https://oapi.dingtalk.com/user/get_admin
    获取管理员通讯录权限范围(GET) USER_GET_ADMIN_SCOPE https://oapi.dingtalk.com/topapi/user/get_admin_scope
    根据unionid获取userid(GET) USER_GET_BY_UNIONID https://oapi.dingtalk.com/user/getUseridByUnionid
    创建用户(POST) USER_CREATE https://oapi.dingtalk.com/user/create
    更新用户(POST) USER_UPDATE https://oapi.dingtalk.com/user/update
    删除用户(GET) USER_DELETE https://oapi.dingtalk.com/user/delete
    获取子部门ID列表(GET) DEPARTMENT_LIST_IDS https://oapi.dingtalk.com/department/list_ids
    获取部门列表(GET) DEPARTMENT_LIST https://oapi.dingtalk.com/department/list
    获取部门详情(GET) DEPARTMENT_GET https://oapi.dingtalk.com/department/get
    查询部门的所有上级父部门路径(GET) DEPARTMENT_LIST_PARENT_BY https://oapi.dingtalk.com/department/list_parent_depts_by_dept
    查询指定用户的所有上级父部门路径(GET) DEPARTMENT_LIST_PARENT https://oapi.dingtalk.com/department/list_parent_depts
    获取企业员工人数(GET) USER_GET_COUNT https://oapi.dingtalk.com/user/get_org_user_count
    创建部门(POST) DEPARTMENT_CREATE https://oapi.dingtalk.com/department/create
    更新部门(POST) DEPARTMENT_UPDATE https://oapi.dingtalk.com/department/update
    删除部门(GET) DEPARTMENT_DELETE https://oapi.dingtalk.com/department/delete
    获取角色下的员工列表(POST) ROLE_SIMPLE_LIST https://oapi.dingtalk.com/topapi/role/simplelist
    发送工作通知消息(POST) MESSAGE_CORPCONVERSATION https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2
    发起待办(POST) WORKRECORD_ADD https://oapi.dingtalk.com/topapi/workrecord/add
    更新待办(POST) WORKRECORD_UPDATE https://oapi.dingtalk.com/topapi/workrecord/update
    获取用户待办事项(POST) WORKRECORD_GET_BY_USER https://oapi.dingtalk.com/topapi/workrecord/getbyuserid
    上传媒体文件(POST) MEDIA_UPLOAD https://oapi.dingtalk.com/media/upload
    单步文件上传(POST) FIEL_UPLOAD_SINGLE https://oapi.dingtalk.com/file/upload/single
    分块上传文件-开启/提交分块上传事务(POST) FIEL_UPLOAD_TRANSACTION https://oapi.dingtalk.com/file/upload/transaction
    分块上传文件-上传文件块(POST) FIEL_UPLOAD_CHUNK https://oapi.dingtalk.com/file/upload/chunk
    注册业务事件回调接口(POST) CALL_BACK_REGISTER https://oapi.dingtalk.com/call_back/register_call_back
    查询事件回调接口(POST) CALL_BACK_GET https://oapi.dingtalk.com/call_back/get_call_back
    更新事件回调接口(POST) CALL_BACK_UPDATE https://oapi.dingtalk.com/call_back/update_call_back
    删除事件回调接口(GET) CALL_BACK_DELETE https://oapi.dingtalk.com/call_back/delete_call_back
    获取回调失败的结果(GET) CALL_BACK_GET_FAILED_RESULT https://oapi.dingtalk.com/call_back/get_call_back_failed_result
    签到事件回调类型 CALL_BACK_EVENT_CHECK_IN check_in
    企业考勤排班详情(POST) ATTENDANCE_LIST_SCHEDULE https://oapi.dingtalk.com/topapi/attendance/listschedule
    企业考勤组详情(POST) ATTENDANCE_GET_SIMPLEGROUP https://oapi.dingtalk.com/topapi/attendance/getsimplegroups
    获取打卡详情(POST) ATTENDANCE_LIST_RECORD https://oapi.dingtalk.com/attendance/listRecord
    获取打卡结果(POST) ATTENDANCE_LIST https://oapi.dingtalk.com/attendance/list
    获取请假时长(POST) ATTENDANCE_GET_LEAVEDURATION https://oapi.dingtalk.com/topapi/attendance/getleaveapproveduration
    查询请假状态(POST) ATTENDANCE_GET_LEAVESTATUS https://oapi.dingtalk.com/topapi/attendance/getleavestatus
    获取用户考勤组(POST) ATTENDANCE_GET_USERGROUP https://oapi.dingtalk.com/topapi/attendance/getusergroup
    获取部门用户签到记录(GET) CHECKIN_RECORD https://oapi.dingtalk.com/checkin/record
    获取用户签到记录(POST) CHECKIN_RECORD_GET https://oapi.dingtalk.com/topapi/checkin/record/get

    钉钉集成登录

    对于引用了移动端头页面 mobile_header.html 的页面,如果当前是钉钉执行环境且当前用户未登录系统,平台将使用钉钉提供的免登接口通过两次ajax请求自动完成系统登录。不再有使用cas登录时的页面三次跳转,首次使用体验大幅提升。

    首先,在 applicationContext-security.xml 文件中,启用钉钉集成。可以看到,非钉钉环境依然延用cas登录

    <!-- 根据项目需求选择登录方式:标准,CAS,钉钉CAS联合 -->
    <beans:import resource="ddAndCasSecurity.xml"/>
    <!-- <beans:import resource="casSecurity.xml"/> -->
    <!-- <beans:import resource="standardSecurity.xml"/> -->
    

    然后,尽可能将页面初始化代码都放到Lcp.ready方法里。页面所有对后台接口的调用,都必须在Lcp.ready里、或之后执行。Lcp.ready函数保证了代码执行顺序,其一定会在(mui完成初始化、dd完成初始化、页面完成登录)这三者之后执行。建议使用Lcp.ready替代mui.ready或dd.ready。

    var viewM = null;
    Lcp.ready(function () {
    	viewM = new Vue({
    		el: '#mui-content',
    		methods: {
    			pickEmployee: function () {
    				dd.biz.util.openLink({
    					url: document.location.origin + '${base.contextPath}/hr/mb_employee_picker.html?multiSelect=false'
    				});
    			}
    		}
    	});
    });
    

    如果存在需要鉴权才使用的api,可以使用Lcp.ddConfig函数。建议Lcp.ddConfig在Lcp.ready之外调用。示例如下:

    Lcp.ddConfig({
    	jsApiList: [
    		'runtime.info',
    		'biz.contact.choose',
    		'device.notification.confirm',
    		'device.notification.alert',
    		'device.notification.prompt',
    		'biz.ding.post',
    		'biz.util.openLink',
    		'biz.util.scanCard',
    		'device.geolocation.get',
    	]
    })
    Lcp.ready(function() {
    	...
    )}
    

    钉钉事件回调

    当在钉钉上做指定操作时,钉钉会将相关事件主动推送至指定RESTFul接口,实现事件通知。

    接口注册

    向钉钉服务器指定地址发起请求,将回调URL作为参数告知钉钉(注册回调接口)。钉钉服务器会立刻向回调URL发起【测试回调URL】事件,来验证URL的合法性,回调URL服务器需要在接收到回调之后返回字符串“success”的加密json数据,才能完成注册。
    在这里插入图片描述

    通讯录事件回调

    事件类型 类别 说明
    user_add_org 用户变更 通讯录用户增加
    user_modify_org 用户变更 通讯录用户更改
    user_leave_org 用户变更 通讯录用户离职
    user_active_org 用户变更 加入企业后用户激活
    org_admin_add 用户变更 通讯录用户被设为管理员
    org_admin_remove 用户变更 通讯录用户被取消设置管理员
    org_dept_create 部门变更 通讯录企业部门创建
    org_dept_modify 部门变更 通讯录企业部门修改
    org_dept_remove 部门变更 通讯录企业部门删除
    org_remove 企业信息变更 企业被解散
    org_change 企业信息变更 企业信息发生变更
    label_user_change 角色变更 员工角色信息发生变更
    label_conf_add 角色变更 增加角色或者角色组
    label_conf_del 角色变更 删除角色或者角色组
    label_conf_modify 角色变更 修改角色或者角色组

    群会话事件回调

    事件类型 说明
    chat_add_member 群会话添加人员
    chat_remove_member 群会话删除人员
    chat_quit 群会话用户主动退群
    chat_update_owner 群会话更换群主
    chat_update_title 群会话更换群名称
    chat_disband 群会话解散群

    签到事件回调

    事件类型 说明
    check_in 用户签到

    审批事件回调

    事件类型 说明
    bpms_instance_change 审批实例开始,结束
    bpms_task_change 审批任务开始,结束,转交

    日志管理

    通过 IDingDingService.execute 方法调用接口,系统会自动记录接口调用日志,方便问题查看和调试。

    mber|群会话删除人员|
    |chat_quit|群会话用户主动退群|
    |chat_update_owner|群会话更换群主|
    |chat_update_title|群会话更换群名称|
    |chat_disband|群会话解散群|

    签到事件回调

    事件类型 说明
    check_in 用户签到

    审批事件回调

    事件类型 说明
    bpms_instance_change 审批实例开始,结束
    bpms_task_change 审批任务开始,结束,转交

    日志管理

    通过 IDingDingService.execute 方法调用接口,系统会自动记录接口调用日志,方便问题查看和调试。
    在这里插入图片描述

    展开全文
  •  最近由于单位原有业务更改,导致原有周报获取数据方式有所更改,周报汇总数也随之需要重新定义输出、展现方式,在这里对之前所做的操作做下总结: 由于是要生成图片所以图表绘制方面在技术选型时就选用了...

     

         最近由于单位原有业务更改,导致原有周报获取数据方式有所更改,周报汇总数也随之需要重新定义输出、展现方式,在这里对之前所做的操作做下总结:

    由于是要生成图片所以图表绘制方面在技术选型时就选用了JfreeChart,尽管JfreeChart有很多的缺点譬如说:生成的图片、文字不清晰,JfreeChart的文档要收费等等,但是这些都不是事,图片文字(尤其是对于中文乱码的问题)这个问题都可以后期自己解决的。

    什么?  一步步来?  好吧
    一、准备下基础的东西

    1.1

    pom文件加入依赖(版本号可以自行选择)

    1.2

    创建接收图表横纵坐标数据的DTO(get、set ...已省略)

    1.3  获取横纵坐标数据(在获取数据时,若有数据不完全(网络超时等原因),则重启线程进行重试,最大重启次数设置为重启3次,重启3次后依然失败此次获取数据宣告失败),程序结束。以下是项目中获取友盟统计信息的部分代码:

    
    
    @Component
    public class GetUmengWeeklyActiveUsersImage {
    
    	private static final Logger logger = LoggerFactory.getLogger(GetUmengWeeklyActiveUsersImage.class);
    
    	@Autowired
    	AmazonS3 amazonS3;
    
    	@Autowired
    	ExecutorThreadPools pool;
    
    	@Autowired
    	UmengOpenApiClient umengOpenApiClient;
    
    	@Value("${umeng.static.appKey.****sh.ios}")
    	String appKeyIos = "";
    
    	@Value("${umeng.static.appKey.****sh.android}")
    	String appKeyAndroid = "";
    
    	@Autowired
    	UcenterServiceUserStaticsApiV1Client ucenterServiceUserStaticsApiV1Client;
    
    	@Autowired
    	MessageCenterDingCustomBotV1Client messageCenterDingCustomBotV1Client;
    
    	@Value("${cloud.aws.cephInternetPrefix}")
    	String cephPrefix;
    
    	String token = "*******************************************";
    
    	public void execute() {
    		pool.getExecuteReportPool().execute(new Worker().enableExeLimit().setExeLimit(3).setInterval(10000L));
    	}
    
    	class Worker extends ConfigurableThread {
    
    		@Override
    		protected void doRun() throws Exception {
    
    			String endDate = new SimpleDateFormat("yyyy-MM-dd").format(DateUtil.addDays(DateUtil.today(), 0));
    			String startDate = new SimpleDateFormat("yyyy-MM-dd").format(DateUtil.addDays(DateUtil.today(), -7));
    			MarkdownMessageExt reportMarkDown = new MarkdownMessageExt();
    
    			// umeng 周活统计
    			int thisWeekUsers = 0;
    			int thisWeekNewUsers = 0;
    			int thisWeekActiveUsers = 0;
    			int thisWeekLauches = 0;
    			String fileName = "";
    			String path = "";
    			HashMap<Integer, Integer> weeklyMap = new HashMap<Integer, Integer>();
    			List<SimpleColumnarGraphDataDTO> list = new ArrayList<SimpleColumnarGraphDataDTO>();
    			try {
    				UmengUappCountData[] WeeklyIosActiveUsers = umengOpenApiClient.getActiveUsers(appKeyIos, startDate,
    						new SimpleDateFormat("yyyy-MM-dd").format(DateUtil.addDays(DateUtil.today(), -1)), "daily");
    
    				for (UmengUappCountData umengUappCountData : WeeklyIosActiveUsers) {
    					String date = umengUappCountData.getDate();
    					int parseInt = Integer.parseInt(date.replace("-", ""));
    					Integer value = umengUappCountData.getValue();
    					weeklyMap.put(parseInt, value);
    				}
    				UmengUappCountData[] WeeklyAndroidActiveUsers = umengOpenApiClient.getActiveUsers(appKeyAndroid,
    						startDate, new SimpleDateFormat("yyyy-MM-dd").format(DateUtil.addDays(DateUtil.today(), -1)),
    						"daily");
    				int weeklyActiveUsers = 0;
    
    				for (UmengUappCountData umengUappCountData : WeeklyAndroidActiveUsers) {
    					String date = umengUappCountData.getDate();
    					int parseInt = Integer.parseInt(date.replace("-", ""));
    					Integer value = umengUappCountData.getValue();
    					for (Entry<Integer, Integer> map : weeklyMap.entrySet()) {
    						if (map.getKey().equals(parseInt)) {
    							map.setValue(map.getValue() + value);
    							SimpleColumnarGraphDataDTO dto = new SimpleColumnarGraphDataDTO();
    							dto.setColumnKey(parseInt + "");
    							dto.setValue(weeklyMap.get(parseInt));
    							list.add(dto);
    						}
    					}
    				}
    				for (Integer key : weeklyMap.keySet()) {
    					weeklyActiveUsers += weeklyMap.get(key);
    
    				}
    				Map<String, Object> dataMap = new HashMap<String, Object>();
    				dataMap.put("本周日活量总数", weeklyActiveUsers);
    				dataMap.put("日活量日均", weeklyActiveUsers / 7);
    				dataMap.put("ActiveUsers", list);
    				if (!weeklyMap.isEmpty()) {
    					MakeAppDataImage makeAppDataImage = new MakeAppDataImage();
    					JFreeChart chart = makeAppDataImage.makeAppDataChart(dataMap);
    
    					File createTempFile = File.createTempFile("按天统计日活量", ".png",
    							new File(System.getProperty("java.io.tmpdir")));
    					ChartUtilities.saveChartAsPNG(createTempFile, chart, 1100, 500);
    					fileName = createTempFile.getName();
    					logger.info("文件名称为:{}", fileName);
    					path = uploadToCeph(createTempFile);
    					logger.info("按天统计用户注册数成功生成图片---> {}" + createTempFile);
    				} else {
    					logger.error("按天统计用户注册数出图失败");
    				}
    			} catch (Exception e) {
    				logger.info("钉钉消息发送失败." + e.getMessage(), e);
    			}
    			try {
    				logger.info("开始向钉钉发送消息----");
    				reportMarkDown.setTitle("按天统计日活量");
    				reportMarkDown.add(MarkdownMessage.getHeaderText(3, "按天统计日活量 "));
    				reportMarkDown.add(MarkdownMessage
    						.getImageText("https://eximages.12306.cn/wificloud/wifi-monitor/weeklyreport/" + fileName));
    				
    				if (fileName.length() > 0) {
    					ResultBean msgBean = messageCenterDingCustomBotV1Client.sendMarkdown(token, reportMarkDown);
    					logger.info("消息返回  {}", msgBean);
    					if (msgBean.getStatus() == COMMON_API_WRAPPER_STATIC_VALUE.RESULTCODE.SUCCESS) {
    						markAsSuccess();
    					} else {
    						for (int tryCount = 1; tryCount < 4; tryCount++) {
    							execute();
    							logger.info("重试第{}次重新获取数据/生成图片", tryCount);
    						}
    					}
    				}
    
    			} catch (Exception e) {
    				logger.info("钉钉消息发送失败." + e.getMessage(), e);
    			}
    			// }
    		}
    
    		private String uploadToCeph(File sourceFile) {
    			String path = "wifi-monitor/weeklyreport/" + sourceFile.getName();
    			logger.info("开始上传到s3 {}/{}", COMMON_API_WRAPPER_STATIC_VALUE.RESOURCE.WIFI_BUCKET_NAME, path);
    			PutObjectRequest putObjectRequest = new PutObjectRequest(
    					COMMON_API_WRAPPER_STATIC_VALUE.RESOURCE.WIFI_BUCKET_NAME, path, sourceFile);
    			putObjectRequest.withCannedAcl(CannedAccessControlList.PublicRead);
    			PutObjectResult result = amazonS3.putObject(putObjectRequest);
    			logger.info("上传ceph的result返回结果为:{}", JSONDataUtil.toJSONString(result));
    			sourceFile.delete();
    			return COMMON_API_WRAPPER_STATIC_VALUE.RESOURCE.WIFI_BUCKET_NAME + "/" + path;
    		}
    
    		@Override
    		protected void onTimeout() {
    			logger.info("统计生成超时,线程结束");
    		}
    
    		@Override
    		protected void onTryout() {
    			logger.info("统计尝试次数超出,线程结束");
    		}
    
    		@Override
    		protected void onSuccess() {
    			logger.info("统计执行成功,线程结束");
    		}
    
    		@Override
    		protected void threadExceptionHandle(Exception e) {
    			logger.info("统计执行错误,程序退出.错误信息:" + e);
    		}
    	}
    
    	/**
    	 * 使用 Map按key进行排序
    	 * 
    	 * @param map
    	 * @return
    	 */
    	private static Map<Integer, Integer> sortMapByKey(Map<Integer, Integer> map) {
    		if (map == null || map.isEmpty()) {
    			return null;
    		}
    		Map<Integer, Integer> sortMap = new TreeMap<Integer, Integer>(new MapKeyComparator());
    		sortMap.putAll(map);
    		return sortMap;
    	}
    
    }
    

    1.4获取数据后,进行数据图表图片的生成,生成图表图片后将其上传至CEPH ,然后钉钉将其根据ceph图片地址对其进行获取发送。(生成图片时要特别注意处理的是  JfreeChart 关于中文乱码的问题 )

    
    /**
     * @author zyq 报表图形实现类
     */
    public class MakeAppDataImage {
    
    	/**
    	 * 
    	 * @param dataMap
    	 *            map(key--value):{ title--图表标题 path--图片导出地址
    	 *            chartdata--图表数据(list<SimpleColumnarGraphDataDTO>集合)
    	 *            rowTitle--横轴标题 columnTitle--纵轴标题 }
    	 * @return
    	 * @throws Exception
    	 */
    	public JFreeChart makeAppDataChart(Map<String, Object> dataMap) throws Exception {
    
    		List<SimpleColumnarGraphDataDTO> dataList = (List<SimpleColumnarGraphDataDTO>) dataMap.get("ActiveUsers");
    		CategoryDataset createDataset = createDataset(dataList);
    
    //		StandardChartTheme standardChartTheme = new StandardChartTheme("CN");
    //		// 设置标题字体
    //		standardChartTheme.setExtraLargeFont(new Font("隶书", Font.BOLD, 20));
    //		// 设置图例的字体
    //		standardChartTheme.setRegularFont(new Font("宋书", Font.PLAIN, 15));
    //		// 设置轴向的字体
    //		standardChartTheme.setLargeFont(new Font("宋书", Font.PLAIN, 15));
    //		// 应用主题样式
    //		ChartFactory.setChartTheme(standardChartTheme);
    
    		JFreeChart chart = ChartFactory.createLineChart(
    				"本周日活量    总量 " + dataMap.get("本周日活量总数") + " 日均 " + dataMap.get("日活量日均"), "", "人次", createDataset,
    				PlotOrientation.VERTICAL, // 绘制方向
    				false, // 显示图例
    				false, // 采用标准生成器
    				false // 是否生成超链接
    		);
    
    		chart.getTitle().setFont(new Font("黑体", Font.BOLD, 18)); // 设置标题字体
    		// chart.getLegend().setItemFont(new Font("宋体", Font.PLAIN, 15));//
    		// 设置图例类别字体
    		chart.setBackgroundPaint(Color.WHITE);// 设置背景色
    		// 获取绘图区对象
    		CategoryPlot plot = chart.getCategoryPlot();
    		plot.setBackgroundPaint(Color.WHITE); // 设置绘图区背景色
    		plot.setRangeGridlinePaint(Color.GRAY); // 设置水平方向背景线颜色
    		plot.setRangeGridlinesVisible(true);// 设置是否显示水平方向背景线,默认值为true
    		plot.setDomainGridlinePaint(Color.WHITE); // 设置垂直方向背景线颜色
    		plot.setDomainGridlinesVisible(true); // 设置是否显示垂直方向背景线,默认值为false
    
    		CategoryAxis domainAxis = plot.getDomainAxis();
    		domainAxis.setLabelFont(new Font("黑体", Font.BOLD, 15)); // 设置横轴字体
    		domainAxis.setTickLabelFont(new Font("黑体", Font.BOLD, 15));// 设置坐标轴标尺值字体
    		domainAxis.setLowerMargin(0.01);// 左边距 边框距离
    		domainAxis.setUpperMargin(0.06);// 右边距 边框距离,防止最后边的一个数据靠近了坐标轴。
    		domainAxis.setMaximumCategoryLabelLines(2);
    
    		ValueAxis rangeAxis = plot.getRangeAxis();
    		rangeAxis.setLabelFont(new Font("黑体", Font.BOLD, 15));
    		rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());// Y轴显示整数
    		rangeAxis.setAutoRangeMinimumSize(1); // 最小跨度
    		rangeAxis.setUpperMargin(0.18);// 上边距,防止最大的一个数据靠近了坐标轴。
    		rangeAxis.setLowerBound(0); // 最小值显示0
    		rangeAxis.setAutoRange(false); // 不自动分配Y轴数据
    		rangeAxis.setTickMarkStroke(new BasicStroke(1.6f)); // 设置坐标标记大小
    		rangeAxis.setTickMarkPaint(Color.BLACK); // 设置坐标标记颜色
    
    		// 获取折线对象
    		LineAndShapeRenderer renderer = (LineAndShapeRenderer) plot.getRenderer();
    		renderer.setBaseItemLabelsVisible(true);
    		renderer.setBasePositiveItemLabelPosition(
    				new ItemLabelPosition(ItemLabelAnchor.OUTSIDE12, TextAnchor.BASELINE_CENTER));
    		renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator());
    		renderer.setSeriesPaint(0, Color.blue);// 设置线条颜色
    		BasicStroke realLine = new BasicStroke(1.8f); // 设置实线
    		// 设置虚线
    		float dashes[] = { 5.0f };
    		BasicStroke brokenLine = new BasicStroke(2.2f, // 线条粗细
    				BasicStroke.CAP_ROUND, // 端点风格
    				BasicStroke.JOIN_ROUND, // 折点风格
    				8f, dashes, 0.6f);
    		for (int i = 0; i < createDataset.getRowCount(); i++) {
    			if (i % 2 == 0)
    				renderer.setSeriesStroke(i, realLine); // 利用实线绘制
    			else
    				renderer.setSeriesStroke(i, brokenLine); // 利用虚线绘制
    		}
    
    		plot.setNoDataMessage("无对应的数据,请重新查询。");
    		plot.setNoDataMessageFont(new Font("宋体", Font.PLAIN, 15));// 字体的大小
    		plot.setNoDataMessagePaint(Color.BLUE);// 字体颜色
    		return chart;
    	}
    
    	private CategoryDataset createDataset(List<SimpleColumnarGraphDataDTO> dataList) {
    		// 统计图数据
    		DefaultCategoryDataset dataset = new DefaultCategoryDataset();
    		for (SimpleColumnarGraphDataDTO rowdata : dataList) {
    			dataset.addValue(rowdata.getValue(), "日活数", rowdata.getColumnKey());
    		}
    		return dataset;
    	}
    
    	private CategoryDataset createData(Map<Integer, SimpleColumnarGraphDataDTO> dataMap) {
    		// 统计图数据
    		DefaultCategoryDataset dataset = new DefaultCategoryDataset();
    		return dataset;
    	}
    
    }

    1.5 然后自己开一个debug测试请求,启动程序进行测试(这样就避免有些同学,启用定时任务在等定时任务的尴尬(别笑,很早之前我就等过定时任务触发时间  哈哈))

    package com.rails.wifi.monitquerylayer.api;
    
    import java.util.Date;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.rails.wifi.commonapiwrapper.bean.ResultBean;
    import com.rails.wifi.commonapiwrapper.factory.ResultFactory;
    import com.rails.wifi.monitquerylayer.core.worker.GetRegisterByDayImage;
    import com.rails.wifi.monitquerylayer.core.worker.GetUmengWeeklyActiveUsersImage;
    import com.rails.wifi.monitquerylayer.core.worker.WeeklyUserLogStaticsReporterPre;
    
    @RestController
    @RequestMapping(value = "/debugApi")
    public class TaskDebugWeeklyApi {
    
    	private static final Logger logger = LoggerFactory.getLogger(TaskDebugWeeklyApi.class);
    
    	@Autowired
    	WeeklyUserLogStaticsReporterPre weeklyUserLogStaticsReporterPre;
    	
    	@Autowired
    	GetRegisterByDayImage getRegisterByDayImage;
    	
    	@Autowired
    	GetUmengWeeklyActiveUsersImage getUmengWeeklyActiveUsersImage;
    	
    	
    //  日活周报发送
    	@GetMapping(value = "triggerUserWeeklyReport")
    	public ResultBean triggerUserWeeklyReport() {
    		logger.info("触发周报用户日活图表生成: {}", new Date());
    		getUmengWeeklyActiveUsersImage.execute();
    		return ResultFactory.success();
    	}
    	
    
    
    }
    

     来看看测试后的结果 (此图是钉钉机器人发到群里的图片)=_=:

     

     到这里图片生成并且能够正常发送到钉钉群,下篇总结下如何添加钉钉机器人,完成图片,文字。。。等内容发送到指定钉钉群。

     

     

    展开全文
  • 一、简单介绍因为一个平台一个平台进行弄太麻烦,也不够统一,所以领导选择了umeng(我的内心是拒绝的),umeng的文档写的便于查找,但是比较分散,对于比较熟悉的很好用,没接触过umeng的肯定一脸懵逼,比如我,...
  • 近期在公司安排项目,钉钉企业内部应用开发时,遇到棘手问题,引入钉钉SDK,idea工具使用跑项目时没有任何问题,打包之后却,无法将SDK打到war 中。 pom配置如下: 在这里,说下如何解决类似的问题。首先,我们...
  • 之前写过一篇文章关于上传目录文件:uni-app系统目录文件上传(非只图片和视频)解决方案,这次来解决文件预览问题。   uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、...
  • Grafana 在 Kubernetes 中的使用

    千次阅读 2019-01-25 17:26:41
    安装 grafana 是一个可视化面板,有着非常漂亮的图表和布局展示,功能齐全的度量仪表盘和图形编辑器,支持 Graphite、zabbix、InfluxDB、Prometheus、OpenTSDB、Elasticsearch 等作为数据源,比 Prometheus 自带的...
  • 《转载-Github上的python开源项目》

    千次阅读 2018-11-13 11:33:07
    Python开源项目,期待大家和我们一起共同维护 github排名榜单 https://github.com/trending github搜索榜单:https://github.com/search 说明 以下总结大部分来至于 [TimLiu-Python] ... Feedback 期...
  • 钉钉接口学习笔记

    2020-09-20 17:17:39
    接口说明格式 请求方式:GET/POST(HTTPS) 请求地址:...userid=USERID 请求包体: ​ … SDK请求示例: ​ … 返回结果: ...3)请求包体/参数说明,标明请求参数实例及说明,参数说明包括字段含义、取
  • Python学习资料总结

    千次阅读 2018-11-01 18:23:36
    其他Web相关 用户图形接口相关 网络相关 数据库相关 游戏相关 开源框架 大数据与人工智能相关 网络爬虫 测试与代码分析审核 安全与破解相关 图表及图像相关 语音相关 运维相关 树莓派 第三方平台 ...
  • 有时候不看官方文档进行配置,可能会出现奇奇怪怪的问题,SO转载一下官方文档,顺带学习。 想超长体验此软件,请搜索本博客内容,有破解方法,仅用来学习使用,顺带进行二次开发,请勿使用在商业用途,谢谢。...
  • vue项目开发心得和一些最佳实践

    千次阅读 2020-03-12 17:09:57
    本文记录了一些vue项目中的一些经验,包括项目结构,路由管理,路由使用技巧,公有组件抽取,公有方法抽取,样式管理等,欢迎围观。
  • Airflow

    千次阅读 2019-07-25 19:04:35
    Airflow概述特性比较下载Python2.7pipsetuptoolsairflow功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左...
  • 这是作者网络安全自学教程系列,主要是关于安全工具和实践操作的在线笔记,特分享出来与博友们学习,希望您喜欢,一起进步。这篇文章将带着大家来学习《Windows黑客编程技术详解》,其作者是甘迪文老师,推荐大家...
  • 2 小时完成物联网养老 SaaS 平台搭建

    千次阅读 2019-08-14 23:30:43
    用技术解决阿尔茨海默病护理的问题,让老人和其护理者有更好的生活质量,是技术可以解决的。基于物联网技术,已经有一些设备实现了阿尔茨海默病老人走失定位。但是我们要做更高一层,除了单独分发的硬件之外,我们要...
  • Awesome Mac :好用的Mac软件和教程

    千次阅读 2019-07-09 09:57:56
    文章目录Awesome Mac目录开发者工具编辑器开发者实用工具正则编辑器API开发和分析网络分析命令行工具版本控制GUI版本控制系统数据库设计和产品设计工具原型流程作图工具截图工具其它工具虚拟机通信Email文件共享数据...
  • android安卓源码海量项目合集打包-1

    万次阅读 2019-06-11 16:16:24
    下载地址 最后更新共计113个分类5177套源码29.2 GB。 卷 新加卷 的文件夹 PATH 列表 卷序列号为 00000200 5E7A:7F30 F:. ├─前台界面 │ ├─3D标签云卡片热门 │ │ Android TagCloudView云标签的灵活运用.rar ...
  • 概述:实现小程序上传图片到服务器 ,并保存到本地。仅供测试 我是刚开始学习小程序,如有问题请下方评论,跪拜!!!!! 实现效果:效果想要大家都看到 所以有点失真和变形,请谅解 实现步骤: 1:查看官方...
  • HbuilderX 2.6.15 版本说明

    千次阅读 2020-04-25 10:36:13
    HBuilder X - Release Notes =================================...新增 内置web服务器 支持自定义端口 (菜单【设置】【运行配置】) 新增 FTP插件 支持通过commands在【自定义快捷键】中配置快捷键 详情 新增 项目...
1 2 3 4 5 ... 8
收藏数 159
精华内容 63
关键字:

webapi 上传文件失败 钉钉