泰州网站制作方案/新东方烹饪学校学费一年多少钱
需求:
后端根据数据自动生成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: truename: aliossaccessKey: xxxsecretKey: xxxbucketName: xxxendpoint: xxxpathPrefix: xxx #生成图片所在的文件夹
#钉钉机器人
sys:pe:dingtalk:robotMsgUrl: xxxurlsecret: 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,shapeSequencePaint[] 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); // 去除阴影效果// 设置轴样式jfCategoryAxis 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 + "×tamp=" + timestamp);OapiRobotSendRequest request = new OapiRobotSendRequest();request.setMsgtype("markdown");OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();markdown.setTitle("测试图片");
// markdown.setText(" @" + phone + " \n " +
// "");markdown.setText("");request.setMarkdown(markdown);//@全体,如果要@单独几个人再去看钉钉机器人推送相关文档OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
// at.setAtUserIds(Arrays.asList(userId));
// isAtAll类型如果不为Boolean,请升级至最新SDKat.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 {@Autowiredprivate DingTalkConfig dingTalkConfig;@Autowiredprivate OssConfig ossConfig;@Testpublic void testCreateImage() throws Exception {String fileName1 = UUID.randomUUID().toString();//创建image文件夹String dirPath = createFolder("image") + File.separator + fileName1 + ".png";System.out.println("dirPath = " + dirPath);//获取创建的图片地址1createImage(dirPath);//获取创建的图片地址2String dirPath2 = createFolder("image") + File.separator + fileName1 + ".png";createImage(dirPath2);//合并图片1和图片2String dirPath3 = createFolder("image") + File.separator + fileName1 + ".png";ImageConnectUtil.connectImageWidthVertical(dirPath, dirPath2, dirPath3);//sso上传图片,获得公网下的图片urlString 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 folderString 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 18chart.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();}}}}
相关文章:

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

如何用J-Link仿真PY32F003系列芯片
在用国产ARM芯片,仿真和烧录是必须的,但KEIL MDK也支持国产芯片在线仿真和下载。相信大家和我一样,苦于不会设置J-Link走了很多弯路。不管你用盗版的,还是正版的,都支持在线仿真和下载,只要是ARM核…...

# Go学习-Day10
Go学习-Day10 个人博客:CSDN博客 反射 编写函数适配器,序列化和反序列话可以用到 反射可以在运行时,动态获取变量的各种信息,例如类型,结构体本身的信息,修改变量的值,调用关联的方法 反射是…...

vue3:5、组合式API-reactive和ref函数
<script setup> /* reactive接收一个对象类型的数据,返回一个响应式的对象 *//*** ref:接收简单类型或复杂类型,返回一个响应式对象* 本质:是在原有传入数据的基础上,外层报了一层对象,包成了复杂类型* 底层&…...

Unity Inspector面板上显示Api
serializeField】——将私有类型和保护类型可视化到面板上【System.serializeField】——将自定义类型可视化到面板上【HideIninspector】——将公共变量隐藏【Header(“分组说明”)】——将可视化变量进行分组【Tooltip(“内容说明”&#x…...

Redis功能实战篇之附近商户
在互联网的app当中,特别是像美团,饿了么等app。经常会看到附件美食或者商家, 当我们点击美食之后,会出现一系列的商家,商家中可以按照多种排序方式,我们此时关注的是距离,这个地方就需要使用到我…...

selenium 自动化测试——元素定位
WebDriver 提供了8种元素的定位方法,分别是: id 定位:find_element(By.ID, "kw") name 定位: find_element(By.NAME, "") tag 定位: find_element(By.TAG, "") class 定位: find_element(By.CLASS_NAME, &quo…...

【JMeter】 二次开发插件开发 Dubbo 接口测试插件浅析
概述 在一些企业中,各类业务系统非常丰富,相互之间或对外提供很多的服务或接口这些服务或接口中,有很多是需要强契约约束的,服务的提供方、服务的使用方必须遵守相同契约这类服务最典型的就是RPC,其中应用广泛的有Dub…...

手机SSL证书认证失败是什么意思?
手机SSL证书认证失败是指在使用手机设备浏览网站时,由于SSL证书的认证问题,导致无法建立安全的加密连接。本文将详细介绍手机SSL证书认证失败的含义、可能的原因以及解决方法,帮助用户了解并解决该问题,以确保手机端浏览的数据传输…...

PXE网络批量装机(centos7)
目录 前言 一、实验拓扑图 二、PXE的组件 三、配置PXE装机服务器 1、设置防火墙、selinux 2.安装、启动vsftp 3、拷贝系统文件到/var/ftp用于装机 4、配置tftp 5、准备pxelinx.0文件、引导文件、内核文件 6、配置本机IP 7、配置DHCP服务 8、创建default文件 四、配…...

P1104 生日
题目描述 cjf 君想调查学校 OI 组每个同学的生日,并按照年龄从大到小的顺序排序。但 cjf 君最近作业很多,没有时间,所以请你帮她排序。 输入格式 输入共有 n 1 n 1 n1 行, 第 1 1 1 行为 OI 组总人数 n n n; …...

计算机网络复习大纲
第一章 计算机网络概述 一,网络发展的形态 了解:当前网络的组成形态: 二,计算机网络的定义 掌握 网络的物理组成实体 网络的工作方式 网络组建的目的 三,通过网络定义 我们如何学习网络 物理实体如何构成&…...

Linux:进程(概念)
学习目标 1.认识冯诺依曼系统 2.认识操作系统概念与定位 (系统调用接口) 3.理解进程的概念(PCB) 4.理解进程的状态(fork创建进程,僵尸进程及孤儿进程) 5.了解进程的调度(优先级,竞争性ÿ…...

智能机器人:打造自动化未来的关键技术
文章目录 1. 智能机器人的基本概念2. 智能机器人的关键技术2.1 机器视觉2.2 机器学习与深度学习2.3 传感器技术 3. 智能机器人的应用领域3.1 制造业3.2 医疗保健3.3 农业3.4 服务业 4. 智能机器人的未来趋势4.1 自主决策能力的提升4.2 协作与互操作性4.3 个性化定制4.4 环境感知…...

大数据(七):Pandas的基础应用详解(四)
专栏介绍 结合自身经验和内部资料总结的Python教程,每天3-5章,最短1个月就能全方位的完成Python的学习并进行实战开发,学完了定能成为大佬!加油吧!卷起来! 全部文章请访问专栏:《Python全栈教程(0基础)》 再推荐一下最近热更的:《大厂测试高频面试题详解》 该专栏对…...

【1day】万户协同办公平台 ezoffice未授权访问漏洞学习
注:该文章来自作者日常学习笔记,请勿利用文章内的相关技术从事非法测试,如因此产生的一切不良后果与作者无关。 目录...

适配器模式:如何让不兼容的接口变得兼容
在软件开发中,我们经常会遇到这样的情况:我们需要使用一个现有的类或者接口,但它与我们系统的目标接口不兼容,而我们又不能修改它。这时候,我们该怎么办呢?大多数情况下我们都可以使用适配器模式来解决这个…...

sentinel熔断报java.lang.reflect.UndeclaredThrowableException
背景:内部要进行应用jdk&springboot升级,因此也需要将Spring Cloud Hystrix 替换成alibaba sentinel。 依赖 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</a…...

工业4G路由器的户外组网与无人值守场景应用
工业4G路由器是专为不便电缆布线的工业或日晒雨淋网络不畅的户外环境所设计的网络设备。它能够在没有光纤宽带的情况下使用插卡的方式提供4G或无线WiFi的网络支持。具备工业级防水功能,能够在户外环境下进行网络部署,并实现无人值守运行。工业4G路由器还…...

中移粤港澳大湾区创新研究院、南湖研究院类脑实验室面试(部分)
中移粤港澳大湾区创新研究院 reids热key的高并发量,导致此redis节点的cpu使用率爆满,有什么优化方案?高并发情况下为了保证平台正常运行,怎么设置平台的监控和告警 南湖研究院类脑实验室 笔试通过后,面试无后续...

API 自动化测试难点总结与分享
API自动化测试的难点包括: 接口的参数组合较多,需要覆盖各种可能的情况。接口的状态和数据关联较多,需要验证返回结果是否符合预期。接口的并发访问和性能测试较为复杂,需要合理规划和调度测试策略。接口的安全性和权限控制较为重…...

【每日一题】补档 ABC309F - Box in Box | 三维偏序 | 树状数组 | 中等
题目内容 原题链接 给定 n n n 个箱子,问是否存在一个箱子 x x x 是否可以放到另一个箱子 y y y 里。 需要满足 h x < h y , w x < w y , d x < d y h_x<h_y,w_x<w_y,d_x<d_y hx<hy,wx<wy,dx<dy。 箱子可以随意翻转。 …...

异步编程 - 13 高性能线程间消息传递库 Disruptor
文章目录 Disruptor概述Disruptor中的核心术语Disruptor 流程图 Disruptor的特性详解基于Disruptor实现异步编程 Disruptor概述 Disruptor是一个高性能的线程间消息传递库,它源于LMAX对并发性、性能和非阻塞算法的研究,如今构成了其Exchange基础架构的核…...

(DXE_DRIVER)PciHostBridge
UEFI-PciHostBridge 1、PciHostBridge简介 PciHostBridge: 提供PCI配置空间,IO,MEM空间访问接口以及统一维护平台相关的PCI资源,提供gEfiPciHostBridgeResourceAllocationProtocolGuid,创建RootBridge等为PciBusDxe提供服务; 2、PciHostBridge 配置空间 PCI桥可管理其下PCI子…...

SpringMVC的增删改查的案例
目录 前言: 1.总体思路: 2.前期准备 3.前台页面 前言: 我们今天来学习研究SpringMVC的增删改查,希望这篇博客能够帮助正在学习,工作的你们!!! 1.总体思路: 首先我们得…...

golang入门笔记——nginx
文章目录 Nginx介绍Nginx的安装Nginx文件Nginx反向代理负载均衡nginx动静分离URLRewrite防盗链nginx高可用配置安全性Nginx限流Nginx缓存集成Lua脚本OpenRestry Nginx介绍 Nginx是一个高性能的HTTP和反向代理服务器,特点是占用内存少,并发能力强&#x…...

最新报告!TikTok 市场小家电大商机,GMV破亿的爆款如何复制?
近期,新锐小家电品牌Gaabor空气炸锅在东南亚卖爆了,单款商品GMV短时间内突破两亿,在印尼、泰国、马来西亚、菲律宾、越南均开设本土TikTok 小店,增长势头还在持续。 但Gaabor并不是个例。 整个东南亚家电市场规模增长迅速&#…...

功能定义-紧急制动系统
功能简介 紧急制动系统的触发过程如上图所示: 安全距离报警:当两车距离较近时,会给予驾驶员相应提示 预报警:当两车存在碰撞风险但风险较低【Danger Level1】时,会给予驾驶员提示【提示相比之前更为明显】 制动预填充&…...

Map与Set的区别
map与set是一种进行搜索的数据结构。 一 Map map存储的是key-value的键值对。 1 map中的常见方法 方法作用put(key,value)向map中存放key-value键值对get(key)根据key值得到value值getOrDefault(key,value)获取值为key的value,若不存在,则将key值对应…...

基于uwb和IMU融合的三维空间定位算法matlab仿真
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ..........................................................................kkk 0; for E…...