jframe生成柱状图片+图片垂直合并+钉钉机器人推送

news2024/12/23 18:40:21

需求:

        后端根据数据自动生成2个图片,然后把两张图片合并成一张图片,再发到钉钉群里,涉及到定时生成和推送,当时我们测试同事说他们写定时脚本放到服务器上,然后让我提供生成图片的方法和钉钉机器人的逻辑

天下文字一大抄,集各位大佬精华,最后成果如下

 

 

依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>imagedemo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--柱状图制作引入-->
        <dependency>
            <groupId>org.jfree</groupId>
            <artifactId>jfreechart</artifactId>
            <version>1.0.19</version>
        </dependency>

        <!--oss引入-->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.15.1</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>

        <!--钉钉机器人引入-->
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>alibaba-dingtalk-service-sdk</artifactId>
            <version>2.0.0</version>
        </dependency>

        <!--常用工具类-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.20</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

配置  

 

server:
  port: 9111

#oss配置
aliyun:
  oss:
    enable: true
    name: alioss
    accessKey: xxx
    secretKey: xxx
    bucketName: xxx
    endpoint: xxx
    pathPrefix: xxx                  #生成图片所在的文件夹
#钉钉机器人
sys:
  pe:
    dingtalk:
      robotMsgUrl: xxxurl
      secret: xxx

配置类 

package com.example.demo.config;


import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties(prefix = "sys.pe.dingtalk")
public class DingTalkConfig {
    private String robotMsgUrl;  //系统预警机器人推送群地址
    private String secret;  //系统预警机器人推送群地址
}

 

package com.example.demo.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
public class OssConfig {

    private String endpoint;

    private String accessKey;

    private String secretKey;

    private String bucketName;  //桶名称

    private String pathPrefix;   //生成文件路径前缀

}

module【除serie外,另外一个根据自己需求自行确定是否要用】 

 

package com.example.demo.model;

import java.io.Serializable;
import java.util.Vector;

/**
 * @author zsl0
 * created on 2023/7/6 17:51
 */
public class Serie implements Serializable {

    private static final long serialVersionUID = 1L;
    private String name;// 名字
    private Vector<Object> data;// 数据值

    public Serie() {

    }

    /**
     *
     * @param name
     *            名称(线条名称)
     * @param data
     *            数据(线条上的所有数据值)
     */
    public Serie(String name, Vector<Object> data) {

        this.name = name;
        this.data = data;
    }

    /**
     *
     * @param name
     *            名称(线条名称)
     * @param array
     *            数据(线条上的所有数据值)
     */
    public Serie(String name, Object[] array) {
        this.name = name;
        if (array != null) {
            data = new Vector<Object>(array.length);
            for (int i = 0; i < array.length; i++) {
                data.add(array[i]);
            }
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Vector<Object> getData() {
        return data;
    }

    public void setData(Vector<Object> data) {
        this.data = data;
    }

}

package com.example.demo.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class SystemApiCount {
    private String systemName;
    private Integer count;
}

 生成图片util

package com.example.demo.utils;

import com.example.demo.model.Serie;
import lombok.extern.slf4j.Slf4j;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.StandardChartTheme;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.DefaultDrawingSupplier;
import org.jfree.chart.plot.PieLabelLinkStyle;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.StandardBarPainter;
import org.jfree.chart.renderer.xy.StandardXYBarPainter;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.ui.RectangleInsets;

import java.awt.*;
import java.util.Vector;

/**
 * Jfreechart工具类
 * <p>
 * 解决中午乱码问题<br>
 * 用来创建类别图表数据集、创建饼图数据集、时间序列图数据集<br>
 * 用来对柱状图、折线图、饼图、堆积柱状图、时间序列图的样式进行渲染<br>
 * 设置X-Y坐标轴样式
 * <p>
 *
 * @author chenchangwen
 * @since:2014-2-18
 */
@Slf4j
public class ChartUtil {
    private static String NO_DATA_MSG = "数据加载失败";
    private static Font FONT = new Font("宋体", Font.PLAIN, 12);
//    public static Color[] CHART_COLORS = {
//            new Color(22, 102, 149), new Color(92, 92, 97), new Color(144, 237, 125), new Color(255, 188, 117),
//            new Color(60, 142, 116), new Color(255, 117, 153), new Color(253, 236, 109), new Color(128, 133, 232),
//            new Color(43, 66, 147), new Color(255, 204, 102)};// 颜色

    public static Color[] CHART_COLORS = {
            new Color(12, 250, 194, 195), new Color(16, 161, 246), new Color(22, 102, 149)};// 颜色

    static {
        setChartTheme();
    }

    public ChartUtil() {
    }

    /**
     * 中文主题样式 解决乱码
     */
    private static void setChartTheme() {
        // 设置中文主题样式 解决乱码
        StandardChartTheme chartTheme = new StandardChartTheme("CN");
        // 设置标题字体
        chartTheme.setExtraLargeFont(FONT);
        // 设置图例的字体
        chartTheme.setRegularFont(FONT);
        // 设置轴向的字体
        chartTheme.setLargeFont(FONT);
        chartTheme.setSmallFont(FONT);
        chartTheme.setTitlePaint(new Color(51, 51, 51));
        chartTheme.setSubtitlePaint(new Color(85, 85, 85));

        chartTheme.setLegendBackgroundPaint(Color.WHITE);// 设置标注
        chartTheme.setLegendItemPaint(Color.BLACK);//
        chartTheme.setChartBackgroundPaint(Color.WHITE);
        // 绘制颜色绘制颜色.轮廓供应商
        // paintSequence,outlinePaintSequence,strokeSequence,outlineStrokeSequence,shapeSequence

        Paint[] OUTLINE_PAINT_SEQUENCE = new Paint[]{Color.WHITE};
        // 绘制器颜色源
        DefaultDrawingSupplier drawingSupplier = new DefaultDrawingSupplier(CHART_COLORS, CHART_COLORS, OUTLINE_PAINT_SEQUENCE,
                DefaultDrawingSupplier.DEFAULT_STROKE_SEQUENCE, DefaultDrawingSupplier.DEFAULT_OUTLINE_STROKE_SEQUENCE,
                DefaultDrawingSupplier.DEFAULT_SHAPE_SEQUENCE);
        chartTheme.setDrawingSupplier(drawingSupplier);

        chartTheme.setPlotBackgroundPaint(Color.WHITE);// 绘制区域
        chartTheme.setPlotOutlinePaint(Color.WHITE);// 绘制区域外边框
        chartTheme.setLabelLinkPaint(new Color(8, 55, 114));// 链接标签颜色
        chartTheme.setLabelLinkStyle(PieLabelLinkStyle.CUBIC_CURVE);

        chartTheme.setAxisOffset(new RectangleInsets(5, 12, 5, 12));
        chartTheme.setDomainGridlinePaint(new Color(192, 208, 224));// X坐标轴垂直网格颜色
        chartTheme.setRangeGridlinePaint(new Color(192, 192, 192));// Y坐标轴水平网格颜色

        chartTheme.setBaselinePaint(Color.WHITE);
        chartTheme.setCrosshairPaint(Color.BLUE);// 不确定含义
        chartTheme.setAxisLabelPaint(new Color(51, 51, 51));// 坐标轴标题文字颜色
        chartTheme.setTickLabelPaint(new Color(67, 67, 72));// 刻度数字
        chartTheme.setBarPainter(new StandardBarPainter());// 设置柱状图渲染
        chartTheme.setXYBarPainter(new StandardXYBarPainter());// XYBar 渲染

        chartTheme.setItemLabelPaint(Color.black);
        chartTheme.setThermometerPaint(Color.white);// 温度计

        ChartFactory.setChartTheme(chartTheme);
    }

    /**
     * 创建类别数据集合
     */
    public static DefaultCategoryDataset createDefaultCategoryDataset(Vector<Serie> series, String[] categories) {
        DefaultCategoryDataset dataset = new DefaultCategoryDataset();

        for (Serie serie : series) {
            String name = serie.getName();
            Vector<Object> data = serie.getData();
            if (data != null && categories != null && data.size() == categories.length) {
                for (int index = 0; index < data.size(); index++) {
                    String value = data.get(index) == null ? "" : data.get(index).toString();
                    if (isPercent(value)) {
                        value = value.substring(0, value.length() - 1);
                    }
                    if (isNumber(value)) {
                        dataset.setValue(Double.parseDouble(value), name, categories[index]);
                    }
                }
            }

        }
        return dataset;
    }

    /**
     * 设置柱状图渲染
     *
     * @param plot             绘图
     * @param isShowDataLabels
     */
    public static void setBarRenderer(CategoryPlot plot, boolean isShowDataLabels) {

        plot.setNoDataMessage(NO_DATA_MSG);
        plot.setInsets(new RectangleInsets(10, 20, 5, 20));

        // 设置柱状图样式
        BarRenderer renderer = (BarRenderer) plot.getRenderer();
        renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator());
        //设置每个柱子之间的距离【柱子之间的距离和柱子的最大宽度以及你生成的图片的宽度,这三个参数应该挺有关系会影响展示】
        renderer.setItemMargin(-3);
        renderer.setMaximumBarWidth(0.015);// 设置柱子最大宽度
        for (int i = 0; i < CHART_COLORS.length; i++) {
            // 设置柱子颜色
            renderer.setSeriesPaint(i, CHART_COLORS[i]);
        }

        renderer.setShadowVisible(false); // 去除阴影效果

        // 设置轴样式jf
        CategoryAxis domainAxis = plot.getDomainAxis();
        domainAxis.setTickLabelFont(new Font("宋体", Font.PLAIN, 14)); // 设置X轴上提示文字样式
        domainAxis.setLabelFont(new Font("宋体", Font.PLAIN, 15)); // 设置X轴下的标签文字
        domainAxis.setTickLabelInsets(new RectangleInsets(10, 20, 5, 20));
//        domainAxis.setTickMarksVisible(false);  // 坐标轴标尺不显示
//        domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_45);  //X轴刻度倾斜45度

        NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
        rangeAxis.setTickLabelFont(new Font("宋体", Font.PLAIN, 14));  // 设置Y轴的提示文字样式
        rangeAxis.setLabelFont(new Font("宋体", Font.PLAIN, 15));
        rangeAxis.setTickLabelsVisible(false);   //y轴数值不显示

        plot.setRangeGridlinesVisible(false);   //数据轴网格不显示
//        rangeAxis.setLowerMargin(0.35); // 设置最低的一个 Item 与图片底端的距离
//        rangeAxis.setUpperMargin(0.45); // 设置最高的一个 Item 与图片顶端的距离
//        rangeAxis.setUpperBound(8000.0);  // 设置Y轴的最大值


        if (isShowDataLabels) {
            renderer.setBaseItemLabelsVisible(true);
        }

        setXAixs(plot);
        setYAixs(plot);
    }

    /**
     * 设置类别图表(CategoryPlot) X坐标轴线条颜色和样式
     *
     * @param plot 绘图
     */
    private static void setXAixs(CategoryPlot plot) {
        Color lineColor = new Color(31, 121, 170);
        plot.getDomainAxis().setAxisLinePaint(lineColor);// X坐标轴颜色
        plot.getDomainAxis().setTickMarkPaint(lineColor);// X坐标轴标记|竖线颜色

    }

    /**
     * 设置类别图表(CategoryPlot) Y坐标轴线条颜色和样式 同时防止数据无法显示
     *
     * @param plot 绘图
     */
    private static void setYAixs(CategoryPlot plot) {
        Color lineColor = new Color(192, 208, 224);
        ValueAxis axis = plot.getRangeAxis();
        axis.setAxisLinePaint(lineColor);// Y坐标轴颜色
        axis.setTickMarkPaint(lineColor);// Y坐标轴标记|竖线颜色
        // 隐藏Y刻度
        axis.setAxisLineVisible(false);
        axis.setTickMarksVisible(false);
        // Y轴网格线条
        plot.setRangeGridlinePaint(new Color(192, 192, 192));
        plot.setRangeGridlineStroke(new BasicStroke(1));

        plot.getRangeAxis().setUpperMargin(0.1);// 设置顶部Y坐标轴间距,防止数据无法显示
        plot.getRangeAxis().setLowerMargin(0.1);// 设置底部Y坐标轴间距

    }

    /**
     * 是不是一个%形式的百分比
     *
     * @param str
     * @return
     */
    private static boolean isPercent(String str) {
        return str != null ? str.endsWith("%") && isNumber(str.substring(0, str.length() - 1)) : false;
    }

    /**
     * 是不是一个数字
     *
     * @param str
     * @return
     */
    private static boolean isNumber(String str) {
        return str != null ? str.matches("^[-+]?(([0-9]+)((([.]{0})([0-9]*))|(([.]{1})([0-9]+))))$") : false;
    }


}

阿里云oss上传util 

package com.example.demo.utils;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.example.demo.config.OssConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
import java.util.UUID;

@Slf4j
@Configuration
public class OssUploadUtil {


    public static String uploadImage(String filPath, OssConfig ossConfig) throws IOException {
        OSS ossClient = null;
        InputStream inputStream = null;
        try {
            // 创建OSSClient实例。
            ossClient = new OSSClientBuilder().build(ossConfig.getEndpoint(), ossConfig.getAccessKey(), ossConfig.getSecretKey());
            //生成任意文件名称
            UUID uuid = UUID.randomUUID();
            String imagePath = ossConfig.getPathPrefix() + "/" + DateUtil.format(new Date(System.currentTimeMillis()), DatePattern.NORM_DATE_PATTERN) + "/" + uuid + ".png";
            // 数据流
            inputStream = new FileInputStream(filPath);
            // 填写Bucket名称和Object完整路径。Object完整路径中不能包含Bucket名称。
            ossClient.putObject(ossConfig.getBucketName(), imagePath, inputStream);
            String key = imagePath;
            System.out.println("imagePath = " + imagePath);
            //因为申请公司内的阿里云oss桶的时候,oss生成的图片所在的文件夹权限继承于桶,那么生成这个链接之后就不能被公网访问,当时公司要求文件夹权限或图片权限不能改成公共读,所以就采用了生成的url链接拼接有效期的形式
            Date expiration = new Date(new Date().getTime() + 1000 * 60 * 60 * 24 * 365);  //设置从此刻开始一年有效
            URL url = ossClient.generatePresignedUrl(ossConfig.getBucketName(), key, expiration);
            return url.toString();
        } catch (Exception e) {
            log.error("sso upload error", e);
        } finally {
            // 关闭OSSClient和数据流【一定要关闭,否则会失败】
            if (ossClient != null) ossClient.shutdown();
            if (inputStream != null) inputStream.close();
        }
        return "";
    }

}

钉钉机器人uitl 

package com.example.demo.utils;

import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiRobotSendRequest;
import com.dingtalk.api.response.OapiRobotSendResponse;
import com.example.demo.config.DingTalkConfig;
import com.taobao.api.ApiException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;

@Slf4j
public class DingTalkUtil {

    /**
     * 图片语法
     *
     * @param phone
     */
    public static void sendRobotMessage(String phone, String imageUrl, DingTalkConfig dingTalkConfig) throws Exception {
        try {
            Long timestamp = System.currentTimeMillis();
            String sign = getSign(dingTalkConfig.getSecret(), timestamp);

            DingTalkClient client = new DefaultDingTalkClient(dingTalkConfig.getRobotMsgUrl() + "&sign=" + sign + "&timestamp=" + timestamp);

            OapiRobotSendRequest request = new OapiRobotSendRequest();
            request.setMsgtype("markdown");
            OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
            markdown.setTitle("测试图片");
//            markdown.setText(" @" + phone + "  \n  " +
//                    "![这是一张图片](" + imageUrl + ")");
            markdown.setText("![这是一张图片](" + imageUrl + ")");
            request.setMarkdown(markdown);
            //@全体,如果要@单独几个人再去看钉钉机器人推送相关文档
            OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
//            at.setAtUserIds(Arrays.asList(userId));
//          isAtAll类型如果不为Boolean,请升级至最新SDK
            at.setIsAtAll(Boolean.TRUE);
            request.setAt(at);
            OapiRobotSendResponse response = client.execute(request);
            System.out.println(response.getBody());
        } catch (ApiException e) {
            e.printStackTrace();
        }
    }

   //钉钉机器人推送设置加签方式推送
    public static String getSign(String secret, Long timestamp) throws Exception {
        String stringToSign = timestamp + "\n" + secret;
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
        byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
        String sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");
        return sign;
    }
}

 图片合并util

package com.example.demo.utils;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class ImageConnectUtil {

    //图片垂直合并
    public static void connectImageWidthVertical(String topImagePath, String bottomImagePath, String targetImagePath) throws IOException {
        BufferedImage topBufImage = ImageIO.read(new File(topImagePath));
        BufferedImage bottomBufImage = ImageIO.read(new File(bottomImagePath));

        int connImageWidth = Math.max(topBufImage.getWidth(), bottomBufImage.getHeight()); //目标图片宽度
        int connImageHeight = topBufImage.getHeight() + bottomBufImage.getHeight() + 40;   //目标图片高度=第一个图片高度+第二个图片高度+两个图片之间间距

        BufferedImage connImage = new BufferedImage(connImageWidth, connImageHeight, BufferedImage.TYPE_INT_RGB);
        Graphics connGraphics = connImage.getGraphics();
        connGraphics.fillRect(0, 0, connImageWidth, connImageHeight);  //设置目标图片底部为白色
        connGraphics.setColor(Color.white);
        //第一张图左上角坐标为(0, 0)
        connGraphics.drawImage(topBufImage, 0, 0, null);
        connGraphics.drawImage(bottomBufImage, 0, topBufImage.getHeight() + 40, null);  //第二张图片在第一章图片40px高的位置下边

        String targetFileName = targetImagePath.split("\\.")[1];
        ImageIO.write(connImage, targetFileName, new File(targetImagePath));
    }

}

 测试类

package com.example.demo;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.example.demo.config.DingTalkConfig;
import com.example.demo.config.OssConfig;
import com.example.demo.model.Serie;
import com.example.demo.model.SystemApiCount;
import com.example.demo.utils.ChartUtil;
import com.example.demo.utils.DingTalkUtil;
import com.example.demo.utils.ImageConnectUtil;
import com.example.demo.utils.OssUploadUtil;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.block.BlockBorder;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.RectangleInsets;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.awt.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;


@SpringBootTest
public class TestImageJF {

    @Autowired
    private DingTalkConfig dingTalkConfig;

    @Autowired
    private OssConfig ossConfig;

    @Test
    public void testCreateImage() throws Exception {
        String fileName1 = UUID.randomUUID().toString();
        //创建image文件夹
        String dirPath = createFolder("image") + File.separator + fileName1 + ".png";
        System.out.println("dirPath = " + dirPath);
        //获取创建的图片地址1
        createImage(dirPath);
        //获取创建的图片地址2
        String dirPath2 = createFolder("image") + File.separator + fileName1 + ".png";
        createImage(dirPath2);
        //合并图片1和图片2
        String dirPath3 = createFolder("image") + File.separator + fileName1 + ".png";
        ImageConnectUtil.connectImageWidthVertical(dirPath, dirPath2, dirPath3);
        //sso上传图片,获得公网下的图片url
        String uploadUrl = OssUploadUtil.uploadImage(dirPath3, ossConfig);
        System.out.println("uploadUrl = " + uploadUrl);
        //钉钉上传图片

        //钉钉机器人发送图片消息
        String phone = "19890909090";
        DingTalkUtil.sendRobotMessage(phone, uploadUrl, dingTalkConfig);
        //删除在项目中生成的图片
        deleteFile(dirPath);
        deleteFile(dirPath2);
        deleteFile(dirPath3);
    }


    //生成image文件夹
    public String createFolder(String folderName) {
        String path = System.getProperty("user.dir");
        //create folder
        String dirPath = path + File.separator + folderName;
        File dir = new File(dirPath);
        dir.mkdirs();
        return dirPath;
    }

    //删除图片
    private void deleteFile(String filePath) {
        File file = new File(filePath);
        if (file.isFile() && file.exists()) {
            boolean delete = file.delete();
            System.out.println("delete = " + delete);
        }
    }


    public void createImage(String fileLocation) throws IOException {
        FileOutputStream fileOutputStream = null;
        try {
            //获取所有系统的接口数量
            List<SystemApiCount> systemApiCounts = new ArrayList<>();
            int count = 900;
            String prefix = "xx中心";
            for (int i = 0; i < 16; i++) {
                systemApiCounts.add(SystemApiCount.builder().systemName(prefix + i).count(count).build());
                count -= 50;
            }


            //获取所有系统的执行用例数
            List<SystemApiCount> systemUseCaseExecCounts = new ArrayList<>();
            int useCaseCount = 900;
            for (int i = 0; i < 16; i++) {
                systemUseCaseExecCounts.add(SystemApiCount.builder().systemName(prefix + i).count(useCaseCount).build());
                useCaseCount -= 50;

            }

            //获取所有系统的失败用例数
            List<SystemApiCount> failUseCaseExecCounts = new ArrayList<>();
            int failedUseCaseCount = 900;
            for (int i = 0; i < 16; i++) {
                failUseCaseExecCounts.add(SystemApiCount.builder().systemName(prefix + i).count(failedUseCaseCount).build());
                failedUseCaseCount -= 50;
            }

//        // 创建数据系列
            List<String> systemName = systemApiCounts.stream().map(SystemApiCount::getSystemName).filter(Objects::nonNull).distinct().collect(Collectors.toList());

            //数据分别根据中心排序
            List<Integer> apiCounts = systemApiCounts.stream()
                    .sorted(Comparator.comparing(item -> systemName.indexOf(item.getSystemName()))).map(SystemApiCount::getCount).collect(Collectors.toList());
            List<Integer> useCaseExecCounts = systemUseCaseExecCounts.stream()
                    .sorted(Comparator.comparing(item -> systemName.indexOf(item.getSystemName()))).map(SystemApiCount::getCount).collect(Collectors.toList());
            List<Integer> failUseCaseExecCountList = failUseCaseExecCounts.stream()
                    .sorted(Comparator.comparing(item -> systemName.indexOf(item.getSystemName()))).map(SystemApiCount::getCount).collect(Collectors.toList());

            Serie serie1 = new Serie("调用接口数", new Vector<>(apiCounts));
            Serie serie2 = new Serie("超时接口数", new Vector<>(useCaseExecCounts));
            Serie serie3 = new Serie("失败接口数", new Vector<>(failUseCaseExecCountList));

            // 创建数据集
            DefaultCategoryDataset dataset = ChartUtil.createDefaultCategoryDataset(new Vector<>(Arrays.asList(serie1, serie2, serie3)),
                    systemName.toArray(new String[systemName.size()]));
            // 创建柱状图
            String title = DateUtil.format(new Date(System.currentTimeMillis()), DatePattern.CHINESE_DATE_PATTERN) + "系统接入接口监控情况";
            JFreeChart chart = ChartFactory.createBarChart(
                    title,     // 图表标题
                    "",              // X轴标题
                    "",                 // Y轴标题
                    dataset,                 // 数据集
                    PlotOrientation.VERTICAL,// 图表方向
//                PlotOrientation.HORIZONTAL,// 图表方向
                    true,                    // 是否显示图例
                    true,                    // 是否生成工具提示
                    false                    // 是否生成URL链接
            );

            chart.setTitle(new TextTitle(title, new Font("宋体", Font.BOLD, 20)));
            chart.getTitle().setMargin(18, 0, 0, 0);
            chart.getTitle().setPaint(Color.GREEN);
            chart.getLegend().setFrame(new BlockBorder(Color.WHITE));
            chart.getLegend().setPosition(RectangleEdge.TOP);//设置图例在顶部
            chart.getLegend().setItemFont(new Font("宋体", Font.PLAIN, 14)); //设置图例大小
            chart.getLegend().setItemLabelPadding(new RectangleInsets(2, 2, 2, 2));  //设置图例位置
            chart.getLegend().setMargin(18, 0, 0, 0); //这样就只是距离右边有距离 margin 18
            chart.getRenderingHints().put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
            chart.setBorderVisible(true);  //设置图片边框显示
            chart.setBorderPaint(Color.BLACK); //图片边框颜色

//            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);


            // 对柱状图进行样式渲染
            CategoryPlot plot = chart.getCategoryPlot();
            ChartUtil.setBarRenderer(plot, true);
            fileOutputStream = new FileOutputStream(fileLocation);
            //如果后续有其他中心接入,数据增多的情况下可把宽高设置大点
            ChartUtilities.writeChartAsJPEG(fileOutputStream, 1.0f, chart,
                    1850, 650, null);// 输出图表
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fileOutputStream != null) {
                fileOutputStream.close();
            }
        }

    }

}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/989767.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

rhcsa5(日志、维护准确时间)

分析和存储日志 许多系统都以文本文件的方式记录事件日志&#xff0c;而这些文件保存在/var/log目录中。 在红帽中有systemd-journald和rsyslog服务管理日志进程。systemd-journald服务是操作系统事件日志架构的核心&#xff0c;包括内核、引导过程早期阶段的输出、守护进程启动…

第16章_多版本并发控制MVCC

1. 什么是MVCC MVCC &#xff08; Multiversion Concurrency Control &#xff09;&#xff0c;多版本并发控制。顾名思义&#xff0c; MVCC 是通过数据行的多个版本管理来实现数据库的 并发控制 。这项技术使得在 InnoDB 的事务隔离级别下执行 一致性读 操作有了保证。换…

计算机毕业设计 基于SSM的问卷调查管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

孙哥Spring源码第20集

第20集 refresh()-invokeBeanFactoryPostProcessor 四-处理Configuration下的Bean生成代理对象 【视频来源于&#xff1a;B站up主孙帅suns Spring源码视频】【微信号&#xff1a;suns45】 1、二行InvokeBeanFactoryPostProcessors的作用 registryProcessors&#xff1a;处理的…

c语言逻辑思维

c语言逻辑思维 1.如何问问题? 有甲、乙两人&#xff0c;其中&#xff0c;甲只说假话&#xff0c;而不说真话;乙则是只说真话&#xff0c;不说假话。但是&#xff0c;他们两个人在回答别人的问题时&#xff0c;只通过点头与摇头来表示&#xff0c;不讲话。有一天&#xff0c;一…

怎么压缩pdf文件大小?详细压缩步骤

怎么压缩pdf文件大小&#xff1f;在日常的工作和学习中&#xff0c;我们频繁地处理PDF文件。然而&#xff0c;有时候这些文件的大小可能会非常庞大&#xff0c;这给我们带来了一系列的问题。首先&#xff0c;它们占用了大量的存储空间&#xff0c;使得我们的设备变得拥挤不堪。…

const int* , const int * const, int * const之间的区别

const int * p、int * const p 和 const int * const p 是指针声明中的三种常见形式&#xff0c;它们之间有以下区别&#xff1a; const int * p: 这表示 p 是一个指向常量整数的指针。指针 p 是可变的&#xff08;mutable pointer&#xff09;&#xff0c;可以改变它所指向的对…

小白备战大厂算法笔试(四)——哈希表

文章目录 哈希表常用操作简单实现冲突与扩容链式地址开放寻址线性探测多次哈希 哈希表 哈希表&#xff0c;又称散列表&#xff0c;其通过建立键 key 与值 value 之间的映射&#xff0c;实现高效的元素查询。具体而言&#xff0c;我们向哈希表输入一个键 key &#xff0c;则可以…

基于Java+SpringBoot+Vue摄影分享网站的设计与实现 前后端分离【Java毕业设计·文档报告·代码讲解·安装调试】

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

JAVA怎么进行内存管理? - 易智编译EaseEditing

Java使用自动内存管理系统&#xff0c;主要通过垃圾回收器&#xff08;Garbage Collector&#xff09;来进行内存管理。这意味着开发人员不需要手动分配或释放内存&#xff0c;而是让垃圾回收器来处理不再使用的对象的内存释放。以下是关于Java内存管理的一些重要概念和建议&am…

SpringCloud Alibaba(2021.0.1版本)微服务-OpenFeign以及相关组件使用(保姆级教程)

&#x1f4bb;目录 前言一、简绍二、代码实现1、搭建服务模块1.1、建立父包1.2、建立两个子包&#xff08;service-order、service-product&#xff09;1.3、添加util 工具类 2、添加maven依赖和yml配置文件2.1、springcloud-test父包配置2.2、服务模块配置2.2.1、service-orde…

Tailwind 练手项目

Tailwind 练手项目 用到的技巧 Tailwind CSS 速成 应该都提过了&#xff0c;我不记得这里有什么特别新的知识 整体完成图大概这样&#xff1a; 一个纯静态页面&#xff0c;没有做 JS 之类的特效&#xff0c;不过做了移动端适配&#xff0c;说实话我写到一半的时候改了不少………

【PTA】浙软2020年上机题目自测

个人学习记录&#xff0c;代码难免不尽人意。 在PTA买了浙软2020年的保研上机真题时光机做了做&#xff0c;20年的明显要比19年的难一些&#xff0c;我用了差不多2小时多一点做完了&#xff0c;最后得分90分&#xff0c;在当年排名26左右。下面是4道题和我的做法 7-1 Standard…

YOLO-NAS | YOLO新高度,引入NAS,出于YOLOv8而优于YOLOv8

编辑 | Happy 首发 | AIWalker 链接 | https://mp.weixin.qq.com/s/FsWSRguAn2WZKtmPhMbc6g 亮点在哪里&#xff1f; 参考QARepVGG&#xff0c;该方案引入了QSP与QCI模块以同时利用重参数与8-bit量化的优化&#xff1b; 该方案采用 AutoNAC搜索最优尺寸、每个stage的结构&#…

CK_Label-V23货架标签(电池版本)接口文档

查询标签信息接口 接口类型&#xff1a;POST, 参数格式&#xff1a;json array 链接形式&#xff1a; http://localhost/wms/associate/getTagsMsg 代码形式&#xff1a; { url : http://localhost/wms/associate/getTagsMsg, requestMethed : GET, requestParameter :…

第二证券:光刻胶概念再度活跃,蓝英装备近3日大涨56%

光刻胶概念8日再度走强&#xff0c;截至发稿&#xff0c;蓝英配备“20cm”涨停&#xff0c;腾景科技、&#xff0c;容大感光涨约15%&#xff0c;国风新材、奥普光电、张江高科、百川股份等涨停&#xff0c;炬光科技涨超9%。 蓝英配备近3日累计大涨56%&#xff0c;其近来在互动…

代码随想录 - Day35 - 回溯:重新安排行程,棋盘问题

代码随想录 - Day35 - 回溯&#xff1a;重新安排行程&#xff0c;棋盘问题 332. 重新安排行程 输入&#xff1a;tickets [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],[&qu…

为什么客户跟踪对客户成功很重要?如何正确做到这一点?

如果您想以客户为中心&#xff0c;了解您的客户就非常重要。您可以利用客户沟通管理平台&#xff0c;例如SaleSmartly&#xff08;SS客服&#xff09;查看各种指标来了解客户对您的实际体验以及他们对您的期望。您需要长时间跟踪它们&#xff0c;注意它们的变化并找出原因&…

JavaEE初阶(1)(冯诺依曼体系、CPU、CPU基本原理、如何衡量CPU的好坏?指令、操作系统、操作系统“内核”)

目录 冯诺依曼体系&#xff08;Von Neumann Architecture&#xff09; CPU CPU基本原理&#xff1a; 如何衡量CPU的好坏&#xff1f; 1、主频&#xff08;时钟速度&#xff09;&#xff1a; 2、核心数&#xff1a; 指令 操作系统 操作系统“内核” 冯诺依曼体系&#x…

微信小程序 房贷计算器 js代码终极版

这里写目录标题 展示图1.在utils 中创建文件calculateMortgage.ts2. 在需要使用的地方引入并传参 展示图 1.在utils 中创建文件calculateMortgage.ts /** 假设房贷本金是60万&#xff0c;贷款年利率为4.8%即月利率为0.4%&#xff0c;分20年即240期偿还&#xff0c;等额本金还款…