C#笔记(1、钉钉机器人推送帆软报表图片)

打印 上一主题 下一主题

主题 555|帖子 555|积分 1665

C#笔记——钉钉机器人推送帆软报表图片(一)

1、前言

​        2024年末了一个月,家里多了个小公主,在家休息了一段时间。2025年,休完假上班第一天,领导就说:哎,我看总部那边做了个逐日产出的报表推送到钉钉群,来看筹划告竣率。我们基地这边能不能做啊。我内心默默一想,然后大声一喊能做(内心os:上家公司做过类似的功能,不外他们是自己的通讯工具,不是钉钉)。既然有了活,那就开干吧。终极忙活了几天,终于算是实现了这个功能吧,记载下整个过程,以及踩到的坑吧,终极实现效果如图。

2、下载帆软报表图片到本地

因为上家做过类似的功能,那就直接借鉴下之前的代码,先把帆软报表以图片格式下载到本地。大概过程如下
1、在帆软的报表访问页面的url后面添加&format=image&extype=PNG
2、使用http请求访问,下载到本地,添加动态反射以用来传帆软报表的查询条件参数
3、裁剪图片留白区域,添加水印等
  1. public async Task SendImageOutput()
  2. {
  3.     try
  4.     {
  5.         //string fineReportUrl = "http://localhost:8075/webroot/decision/view/report?viewlet=tzreport/E04.cpt&format=image&extype=PNG";
  6.         string image_Path = "D:\\1";
  7.         string retMsg = string.Empty;
  8.         DateTime dateTime = DateTime.Now;
  9.         string time = dateTime.ToString("yyyyMMdd");//当前时间格式
  10.         string downPath = Path.Combine(image_Path, time);//下载到当前的路径 //方便删除历史的记录文件
  11.         if (!Directory.Exists(downPath))
  12.         {
  13.             Directory.CreateDirectory(downPath);
  14.         }
  15.         string startdate = dateTime.AddDays(-7).ToString("yyyy-MM-dd");
  16.         string enddate = dateTime.ToString("yyyy-MM-dd");
  17.         string cSharpScript = $"string startdate= "{startdate}";string enddate= "{enddate}"; var paramlist ="&startdate="+startdate+"&"+"enddate="+enddate; return paramlist;";
  18.         string urlParam = FineReportUrlParam(cSharpScript);
  19.         string url = fineReportUrl;
  20.         if (!string.IsNullOrEmpty(urlParam))
  21.         {
  22.             url += urlParam;
  23.         }
  24.         //下载图片
  25.         bool downResult = HttpDownloadImage(url, "C02产出", downPath, out string imageFullpath);
  26.         if (downResult)
  27.         {
  28.             //发送数据到dingding
  29.             .......
  30.         }
  31.         else
  32.         {
  33.             retMsg = "执行失败!";
  34.         }
  35.     }
  36.     catch (Exception ex)
  37.     {
  38.         throw ex;
  39.     }
  40. }
  41. /// <summary>
  42. /// http下载文件
  43. /// </summary>
  44. /// <param name="url">下载文件地址</param>
  45. /// <param name="path">文件存放地址</param>
  46. /// <param name="isAddSecurity">是否标密</param>
  47. /// <param name="imageNamePrefix">保存的文件的前缀</param>
  48. /// <param name="imageFullPath">图片保存到的本地路径</param>
  49. /// <returns></returns>
  50. public bool HttpDownloadImage(string url, string imageNamePrefix, string path, out string imageFullPath)
  51. {
  52.     try
  53.     {
  54.         HttpWebRequest? request = WebRequest.Create(url) as HttpWebRequest;
  55.         //发送请求并获取相应回应数据
  56.         HttpWebResponse? response = request.GetResponse() as HttpWebResponse;
  57.         string fileName = imageNamePrefix + "_" + DateTime.Now.ToString("yyyyMMddHHmmssFFF") + ".png";
  58.         string fileFullPath = Path.Combine(path, fileName);
  59.         Stream responseStream = response.GetResponseStream();
  60.         ImageEditDomain imageEdit = new ImageEditDomain();
  61.         Bitmap imageBitmap = imageEdit.CutImageWhitePart(responseStream, 80, fileFullPath);
  62.         Bitmap imageBitmapSecret = imageBitmap;
  63.         Bitmap imageBitmapWord = imageEdit.AddWatermarkWord(imageBitmapSecret);//加有“MES系统自动发送”的图片
  64.         imageFullPath = string.Empty;
  65.         if (imageBitmapWord == null)
  66.         {
  67.             return false;
  68.         }
  69.         else
  70.         {
  71.             imageBitmapWord.Save(fileFullPath, ImageFormat.Png);
  72.             imageFullPath = fileFullPath;
  73.             return true;
  74.         }
  75.     }
  76.     catch (Exception ex)
  77.     {
  78.         imageFullPath = string.Empty;
  79.         return false;
  80.     }
  81. }
  82. /// <summary>
  83. /// 生成动态代码,方便调用
  84. /// </summary>
  85. /// <param name="argMethodCode"></param>
  86. /// <returns></returns>
  87. public string GenerateCode(string argMethodCode)
  88. {
  89.     StringBuilder sb = new StringBuilder();
  90.     sb.Append("using System;");
  91.     sb.Append(Environment.NewLine);
  92.     sb.Append("namespace FineReport");
  93.     sb.Append(Environment.NewLine);
  94.     sb.Append("{");
  95.     sb.Append(Environment.NewLine);
  96.     sb.Append("      public class UrlParam");
  97.     sb.Append(Environment.NewLine);
  98.     sb.Append("      {");
  99.     sb.Append(Environment.NewLine);
  100.     sb.Append("          public string GetParam()");
  101.     sb.Append(Environment.NewLine);
  102.     sb.Append("          {");
  103.     sb.Append(Environment.NewLine);
  104.     sb.Append(argMethodCode);
  105.     sb.Append(Environment.NewLine);
  106.     sb.Append("          }");
  107.     sb.Append(Environment.NewLine);
  108.     sb.Append("      }");
  109.     sb.Append(Environment.NewLine);
  110.     sb.Append("}");
  111.     return sb.ToString();
  112. }
  113. /// <summary>
  114. /// 获取执行动态C#代码的值
  115. /// </summary>
  116. /// <param name="argCodeStr"></param>
  117. /// <returns></returns>
  118. public string FineReportUrlParam(string argCodeStr)
  119. {
  120.     if (string.IsNullOrEmpty(argCodeStr))
  121.     {
  122.         return string.Empty;
  123.     }
  124.     string code = GenerateCode(argCodeStr);
  125.     // CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider();
  126.     //// CSharpCompilation objCSharpCodePrivoder = new CSharpCompilation();
  127.     //  CompilerParameters objCompilerParameters = new CompilerParameters();
  128.     // objCompilerParameters.ReferencedAssemblies.Add("System.dll");
  129.     // //objCompilerParameters.ReferencedAssemblies.Add("Newtonsoft.Json.dll");
  130.     // objCompilerParameters.GenerateExecutable = false;
  131.     // objCompilerParameters.GenerateInMemory = true;
  132.     // CompilerResults cresult = objCSharpCodePrivoder.CompileAssemblyFromSource(objCompilerParameters, code);
  133.     // // 通过反射,执行代码
  134.     // Assembly objAssembly = cresult.CompiledAssembly;
  135.     // object obj = objAssembly.CreateInstance("FineReport.UrlParam");
  136.     // MethodInfo objMI = obj.GetType().GetMethod("GetParam");
  137.     // return objMI.Invoke(obj, null)?.ToString();
  138.     // Parse the source code into a syntax tree.
  139.     var syntaxTree = CSharpSyntaxTree.ParseText(code);
  140.     // Define the references that your compiled code will need.
  141.     var references = new[]
  142.     {
  143.     MetadataReference.CreateFromFile(typeof(object).Assembly.Location), // mscorlib/System.Runtime.dll
  144.     MetadataReference.CreateFromFile(typeof(Console).Assembly.Location), // For example, if you use Console.WriteLine
  145.     // Add other necessary references here, for example:
  146.     // MetadataReference.CreateFromFile(typeof(Newtonsoft.Json.JsonConvert).Assembly.Location) // Newtonsoft.Json.dll
  147. };
  148.     // Create a compilation object with the parsed code and references.
  149.     var compilation = CSharpCompilation.Create(
  150.         assemblyName: Path.GetRandomFileName(),
  151.         syntaxTrees: new[] { syntaxTree },
  152.         references: references,
  153.         options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
  154.     // Emit the compiled code into a memory stream.
  155.     using (var ms = new MemoryStream())
  156.     {
  157.         var result = compilation.Emit(ms);
  158.         if (!result.Success)
  159.         {
  160.             var failures = result.Diagnostics.Where(diagnostic =>
  161.                 diagnostic.IsWarningAsError ||
  162.                 diagnostic.Severity == DiagnosticSeverity.Error);
  163.             throw new Exception("Compilation failed: " + string.Join(Environment.NewLine, failures.Select(d => d.ToString())));
  164.         }
  165.         else
  166.         {
  167.             ms.Seek(0, SeekOrigin.Begin);
  168.             // Load the compiled assembly in a separate context to avoid locking issues.
  169.             var loadContext = new AssemblyLoadContext(null, isCollectible: true);
  170.             var assembly = loadContext.LoadFromStream(ms);
  171.             // Use reflection to create an instance of the type and invoke its method.
  172.             Type? type = assembly.GetType("FineReport.UrlParam");
  173.             if (type == null)
  174.                 throw new InvalidOperationException("Type 'FineReport.UrlParam' not found.");
  175.             object obj = Activator.CreateInstance(type)!;
  176.             MethodInfo? methodInfo = type.GetMethod("GetParam");
  177.             if (methodInfo == null)
  178.                 throw new InvalidOperationException("Method 'GetParam' not found.");
  179.             return methodInfo.Invoke(obj, null)?.ToString();
  180.         }
  181.     }
  182. }
复制代码
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.Drawing.Imaging;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. namespace DingDingTest
  9. {
  10.     internal class ImageEditDomain
  11.     {
  12.         /// <summary>
  13.         /// 剪去图片空余白边
  14.         /// </summary>
  15.         /// <param name="stream">源文件</param>
  16.         /// <param name="WhiteBar">保留的白边,单位为像素</param>
  17.         /// <param name="fullPath">保存到本地的地址</param>
  18.         public Bitmap CutImageWhitePart(Stream stream, int WhiteBar, string fullPath)
  19.         {
  20.             Bitmap bmp = new Bitmap(stream);
  21.             int top = 0, left = 0;
  22.             int right = bmp.Width, bottom = bmp.Height;
  23.             Color white = Color.White;
  24.             //寻找最上面的标线,从左(0)到右,从上(0)到下
  25.             for (int i = 0; i < bmp.Height; i++)//行
  26.             {
  27.                 bool find = false;
  28.                 for (int j = 0; j < bmp.Width; j++)//列
  29.                 {
  30.                     Color c = bmp.GetPixel(j, i);
  31.                     if (IsWhite(c))
  32.                     {
  33.                         top = i;
  34.                         find = true;
  35.                         break;
  36.                     }
  37.                 }
  38.                 if (find) break;
  39.             }
  40.             //寻找最左边的标线,从上(top位)到下,从左到右
  41.             for (int i = 0; i < bmp.Width; i++)//列
  42.             {
  43.                 bool find = false;
  44.                 for (int j = top; j < bmp.Height; j++)//行
  45.                 {
  46.                     Color c = bmp.GetPixel(i, j);
  47.                     if (IsWhite(c))
  48.                     {
  49.                         left = i;
  50.                         find = true;
  51.                         break;
  52.                     }
  53.                 }
  54.                 if (find) break; ;
  55.             }
  56.             // 寻找最下边标线,从下到上,从左到右
  57.             for (int i = bmp.Height - 1; i >= 0; i--)//行
  58.             {
  59.                 bool find = false;
  60.                 for (int j = left; j < bmp.Width; j++)//列
  61.                 {
  62.                     Color c = bmp.GetPixel(j, i);
  63.                     if (IsWhite(c))
  64.                     {
  65.                         bottom = i;
  66.                         find = true;
  67.                         break;
  68.                     }
  69.                 }
  70.                 if (find) break;
  71.             }
  72.             //寻找最右边的标线,从上到下,从右往左
  73.             for (int i = bmp.Width - 1; i >= 0; i--)//列
  74.             {
  75.                 bool find = false;
  76.                 for (int j = 0; j <= bottom; j++)//行
  77.                 {
  78.                     Color c = bmp.GetPixel(i, j);
  79.                     if (IsWhite(c))
  80.                     {
  81.                         right = i;
  82.                         find = true;
  83.                         break;
  84.                     }
  85.                 }
  86.                 if (find) break;
  87.             }
  88.             int iWidth = right - left + 2;
  89.             int iHeight = bottom - top + 2;
  90.             bmp = Cut(bmp, left, top, iWidth, iHeight, WhiteBar);
  91.             return bmp;
  92.         }
  93.         /// <summary>
  94.         /// 添加文字:MES系统自动发送
  95.         /// </summary>
  96.         /// <param name="bitmap"></param>
  97.         /// <returns></returns>
  98.         public Bitmap AddWatermarkWord(Bitmap bitmap)
  99.         {
  100.             //添加MES系统标识
  101.             string text = "MES系统自动发送";
  102.             int fontSize = 10;
  103.             Graphics g = Graphics.FromImage(bitmap);
  104.             int rectWidth = text.Length * (fontSize + 10);
  105.             int rectHeight = fontSize + 15;
  106.             //声明矩形域
  107.             Rectangle rectangle = new Rectangle(bitmap.Width - 200, bitmap.Height - 25, rectWidth, rectHeight);
  108.             Font font = new Font("微软雅黑", fontSize, FontStyle.Bold); //定义字体
  109.             SolidBrush backbrush = new SolidBrush(Color.Transparent);
  110.             SolidBrush sbrushRed = new SolidBrush(Color.Red);
  111.             var stringFormat = new StringFormat();
  112.             stringFormat.Alignment = StringAlignment.Center;
  113.             g.FillRectangle(backbrush, rectangle);
  114.             g.DrawString(text, font, sbrushRed, rectangle, stringFormat);
  115.             return bitmap;
  116.         }
  117.         /// <summary>
  118.         /// 对图片进行裁剪
  119.         /// </summary>
  120.         /// <param name="b">要裁剪的图片</param>
  121.         /// <param name="StartX">裁剪的X轴</param>
  122.         /// <param name="StartY">裁剪的Y轴</param>
  123.         /// <param name="iWidth">要裁剪的宽度</param>
  124.         /// <param name="iHeight">要裁剪的高度</param>
  125.         /// <param name="WhiteBar">保留的白边</param>
  126.         /// <returns></returns>
  127.         public Bitmap Cut(Bitmap b, int StartX, int StartY, int iWidth, int iHeight, int WhiteBar)
  128.         {
  129.             Bitmap bmpOut = new Bitmap(iWidth + 2 * WhiteBar, iHeight + 2 * WhiteBar, PixelFormat.Format24bppRgb);
  130.             Graphics g = Graphics.FromImage(bmpOut);
  131.             g.FillRectangle(Brushes.White, new Rectangle(0, 0, iWidth + 2 * WhiteBar, iHeight + 2 * WhiteBar));
  132.             g.DrawImage(b, new Rectangle(WhiteBar, WhiteBar, iWidth, iHeight), new Rectangle(StartX, StartY, iWidth, iHeight), GraphicsUnit.Pixel);
  133.             g.Dispose();
  134.             return bmpOut;
  135.         }
  136.         /// <summary>
  137.         /// 判断白色与否,非纯白色
  138.         /// </summary>
  139.         /// <param name="c"></param>
  140.         /// <returns></returns>
  141.         public bool IsWhite(Color c)
  142.         {
  143.             if (c.R < 245 || c.G < 245 || c.B < 245)
  144.                 return true;
  145.             else return false;
  146.         }
  147.     }
  148. }
复制代码
<blockquote>6、总结

团体思路末了如下:
1、下载帆软报表图片到本地
2、上传图片到公网可ping通的服务器
3、钉钉机器人以markdown方式推送
踩坑如下:
1、因为之前下载帆软报表图片到本地,此中帆软的参数使用的外部脚本,以是使用了using Microsoft.CSharp包的CSharpCodeProvider,来动态执行外部C#脚本代码,来拼接查询的参数,但是因为版本的题目.Net 8 中CSharpCodeProvider被废弃了,以是换成了Microsoft.CodeAnalysis.CSharp。简单使用的话,上诉代码FineReportUrlParam方法可以直接拼接下传参
2、钉钉机器人无法上传本地图片(也不知道百度的对不对,或许有其他方式,哈哈)

3、 上传到gitee上时,HttpResponseMessage response = client.Send(request);可以看到我这边没有使用SendAsync,
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

星球的眼睛

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表