|
|
@@ -0,0 +1,665 @@
|
|
|
+package shop.alien.store.util;
|
|
|
+
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import shop.alien.entity.store.vo.StoreOperationalStatisticsComparisonVo;
|
|
|
+
|
|
|
+import javax.imageio.ImageIO;
|
|
|
+import java.awt.*;
|
|
|
+import java.awt.image.BufferedImage;
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.text.DecimalFormat;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 经营统计数据对比图片生成工具类
|
|
|
+ * 将 StoreOperationalStatisticsComparisonVo 数据转换为图片
|
|
|
+ *
|
|
|
+ * @author system
|
|
|
+ * @since 2026-01-05
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+public class StatisticsComparisonImageUtil {
|
|
|
+
|
|
|
+ // 颜色定义
|
|
|
+ private static final Color BACKGROUND_COLOR = Color.WHITE;
|
|
|
+ private static final Color TEXT_COLOR = new Color(51, 51, 51); // #333333
|
|
|
+ private static final Color HEADER_BG_COLOR = new Color(245, 245, 245); // #F5F5F5
|
|
|
+ private static final Color POSITIVE_COLOR = new Color(76, 175, 80); // 绿色 #4CAF50
|
|
|
+ private static final Color NEGATIVE_COLOR = new Color(244, 67, 54); // 红色 #F44336
|
|
|
+ private static final Color SECTION_TITLE_COLOR = new Color(33, 33, 33); // #212121
|
|
|
+ private static final Color BORDER_COLOR = new Color(224, 224, 224); // #E0E0E0
|
|
|
+
|
|
|
+ // 字体定义
|
|
|
+ private static final String FONT_NAME = "Microsoft YaHei"; // 微软雅黑,如果系统没有则使用默认字体
|
|
|
+ private static final int TITLE_FONT_SIZE = 24;
|
|
|
+ private static final int SECTION_TITLE_FONT_SIZE = 18;
|
|
|
+ private static final int DATA_FONT_SIZE = 14;
|
|
|
+ private static final int LABEL_FONT_SIZE = 12;
|
|
|
+
|
|
|
+ // 尺寸定义
|
|
|
+ private static final int IMAGE_WIDTH = 800;
|
|
|
+ private static final int PADDING = 20;
|
|
|
+ private static final int SECTION_SPACING = 30;
|
|
|
+ private static final int ROW_HEIGHT = 35;
|
|
|
+ private static final int HEADER_HEIGHT = 50;
|
|
|
+
|
|
|
+ private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,##0.00");
|
|
|
+ private static final DecimalFormat PERCENT_FORMAT = new DecimalFormat("#,##0.00%");
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将统计数据对比转换为图片字节数组
|
|
|
+ *
|
|
|
+ * @param comparison 统计数据对比对象
|
|
|
+ * @return 图片字节数组
|
|
|
+ */
|
|
|
+ public static byte[] generateImage(StoreOperationalStatisticsComparisonVo comparison) {
|
|
|
+ if (comparison == null) {
|
|
|
+ throw new IllegalArgumentException("统计数据对比对象不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 计算图片高度
|
|
|
+ int totalHeight = calculateImageHeight(comparison);
|
|
|
+
|
|
|
+ // 创建图片
|
|
|
+ BufferedImage image = new BufferedImage(IMAGE_WIDTH, totalHeight, BufferedImage.TYPE_INT_RGB);
|
|
|
+ Graphics2D g2d = image.createGraphics();
|
|
|
+
|
|
|
+ // 设置抗锯齿
|
|
|
+ g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
|
+ g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
|
|
+
|
|
|
+ // 填充背景
|
|
|
+ g2d.setColor(BACKGROUND_COLOR);
|
|
|
+ g2d.fillRect(0, 0, IMAGE_WIDTH, totalHeight);
|
|
|
+
|
|
|
+ int currentY = PADDING;
|
|
|
+
|
|
|
+ // 绘制标题
|
|
|
+ currentY = drawTitle(g2d, currentY, comparison);
|
|
|
+
|
|
|
+ // 绘制日期范围
|
|
|
+ currentY = drawDateRange(g2d, currentY, comparison);
|
|
|
+
|
|
|
+ currentY += SECTION_SPACING;
|
|
|
+
|
|
|
+ // 绘制流量数据
|
|
|
+ if (comparison.getTrafficData() != null) {
|
|
|
+ currentY = drawSection(g2d, currentY, "流量数据",
|
|
|
+ buildTrafficDataRows(comparison.getTrafficData()));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 绘制互动数据
|
|
|
+ if (comparison.getInteractionData() != null) {
|
|
|
+ currentY = drawSection(g2d, currentY, "互动数据",
|
|
|
+ buildInteractionDataRows(comparison.getInteractionData()));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 绘制优惠券数据
|
|
|
+ if (comparison.getCouponData() != null) {
|
|
|
+ currentY = drawSection(g2d, currentY, "优惠券",
|
|
|
+ buildCouponDataRows(comparison.getCouponData()));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 绘制代金券数据
|
|
|
+ if (comparison.getVoucherData() != null) {
|
|
|
+ currentY = drawSection(g2d, currentY, "代金券",
|
|
|
+ buildVoucherDataRows(comparison.getVoucherData()));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 绘制服务质量数据
|
|
|
+ if (comparison.getServiceQualityData() != null) {
|
|
|
+ currentY = drawSection(g2d, currentY, "服务质量",
|
|
|
+ buildServiceQualityDataRows(comparison.getServiceQualityData()));
|
|
|
+ }
|
|
|
+
|
|
|
+ g2d.dispose();
|
|
|
+
|
|
|
+ // 转换为字节数组
|
|
|
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
|
+ ImageIO.write(image, "PNG", baos);
|
|
|
+ return baos.toByteArray();
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("生成统计数据对比图片失败", e);
|
|
|
+ throw new RuntimeException("生成图片失败: " + e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算图片高度
|
|
|
+ */
|
|
|
+ private static int calculateImageHeight(StoreOperationalStatisticsComparisonVo comparison) {
|
|
|
+ int height = PADDING * 2;
|
|
|
+ height += HEADER_HEIGHT; // 标题
|
|
|
+ height += 40; // 日期范围
|
|
|
+ height += SECTION_SPACING;
|
|
|
+
|
|
|
+ int rowCount = 0;
|
|
|
+ if (comparison.getTrafficData() != null) {
|
|
|
+ rowCount += 6; // 流量数据行数(6个字段)
|
|
|
+ }
|
|
|
+ if (comparison.getInteractionData() != null) {
|
|
|
+ rowCount += 13; // 互动数据行数(13个字段)
|
|
|
+ }
|
|
|
+ if (comparison.getCouponData() != null) {
|
|
|
+ rowCount += 10; // 优惠券数据行数(10个字段)
|
|
|
+ }
|
|
|
+ if (comparison.getVoucherData() != null) {
|
|
|
+ rowCount += 10; // 代金券数据行数(10个字段)
|
|
|
+ }
|
|
|
+ if (comparison.getServiceQualityData() != null) {
|
|
|
+ rowCount += 12; // 服务质量数据行数(12个字段)
|
|
|
+ }
|
|
|
+
|
|
|
+ height += rowCount * ROW_HEIGHT;
|
|
|
+ height += (rowCount / 6 + 4) * SECTION_SPACING; // 区块间距
|
|
|
+
|
|
|
+ return height;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 绘制标题
|
|
|
+ */
|
|
|
+ private static int drawTitle(Graphics2D g2d, int y, StoreOperationalStatisticsComparisonVo comparison) {
|
|
|
+ Font titleFont = new Font(FONT_NAME, Font.BOLD, TITLE_FONT_SIZE);
|
|
|
+ g2d.setFont(titleFont);
|
|
|
+ g2d.setColor(SECTION_TITLE_COLOR);
|
|
|
+
|
|
|
+ String title = "经营数据";
|
|
|
+ FontMetrics fm = g2d.getFontMetrics();
|
|
|
+ int titleWidth = fm.stringWidth(title);
|
|
|
+ int titleX = (IMAGE_WIDTH - titleWidth) / 2;
|
|
|
+
|
|
|
+ g2d.drawString(title, titleX, y + TITLE_FONT_SIZE);
|
|
|
+ return y + HEADER_HEIGHT;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 绘制日期范围
|
|
|
+ */
|
|
|
+ private static int drawDateRange(Graphics2D g2d, int y, StoreOperationalStatisticsComparisonVo comparison) {
|
|
|
+ Font dateFont = new Font(FONT_NAME, Font.PLAIN, DATA_FONT_SIZE);
|
|
|
+ g2d.setFont(dateFont);
|
|
|
+ g2d.setColor(TEXT_COLOR);
|
|
|
+
|
|
|
+ String currentDate = formatDate(comparison.getCurrentStartTime()) + "-" + formatDate(comparison.getCurrentEndTime());
|
|
|
+ String previousDate = formatDate(comparison.getPreviousStartTime()) + "-" + formatDate(comparison.getPreviousEndTime());
|
|
|
+
|
|
|
+ int dateX = PADDING;
|
|
|
+ g2d.drawString(currentDate, dateX, y);
|
|
|
+ g2d.drawString(previousDate, dateX, y + 20);
|
|
|
+
|
|
|
+ return y + 40;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 格式化日期
|
|
|
+ */
|
|
|
+ private static String formatDate(String date) {
|
|
|
+ if (date == null || date.length() < 10) {
|
|
|
+ return date;
|
|
|
+ }
|
|
|
+ // 将 yyyy-MM-dd 转换为 yyyy/MM/dd
|
|
|
+ return date.replace("-", "/");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 绘制数据区块
|
|
|
+ */
|
|
|
+ private static int drawSection(Graphics2D g2d, int y, String sectionTitle, List<DataRow> rows) {
|
|
|
+ if (rows == null || rows.isEmpty()) {
|
|
|
+ return y;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 绘制区块标题
|
|
|
+ Font sectionFont = new Font(FONT_NAME, Font.BOLD, SECTION_TITLE_FONT_SIZE);
|
|
|
+ g2d.setFont(sectionFont);
|
|
|
+ g2d.setColor(SECTION_TITLE_COLOR);
|
|
|
+ g2d.drawString(sectionTitle, PADDING, y);
|
|
|
+ y += 30;
|
|
|
+
|
|
|
+ // 绘制表头
|
|
|
+ y = drawTableHeader(g2d, y);
|
|
|
+
|
|
|
+ // 绘制数据行
|
|
|
+ Font dataFont = new Font(FONT_NAME, Font.PLAIN, DATA_FONT_SIZE);
|
|
|
+ Font labelFont = new Font(FONT_NAME, Font.PLAIN, LABEL_FONT_SIZE);
|
|
|
+
|
|
|
+ for (DataRow row : rows) {
|
|
|
+ y = drawDataRow(g2d, y, row, dataFont, labelFont);
|
|
|
+ }
|
|
|
+
|
|
|
+ return y + SECTION_SPACING;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 绘制表头
|
|
|
+ */
|
|
|
+ private static int drawTableHeader(Graphics2D g2d, int y) {
|
|
|
+ // 绘制表头背景
|
|
|
+ g2d.setColor(HEADER_BG_COLOR);
|
|
|
+ g2d.fillRect(PADDING, y, IMAGE_WIDTH - PADDING * 2, ROW_HEIGHT);
|
|
|
+
|
|
|
+ // 绘制表头文字
|
|
|
+ Font headerFont = new Font(FONT_NAME, Font.BOLD, LABEL_FONT_SIZE);
|
|
|
+ g2d.setFont(headerFont);
|
|
|
+ g2d.setColor(TEXT_COLOR);
|
|
|
+
|
|
|
+ int col1X = PADDING + 10;
|
|
|
+ int col2X = IMAGE_WIDTH / 2 - 80;
|
|
|
+ int col3X = IMAGE_WIDTH / 2 + 20;
|
|
|
+ int col4X = IMAGE_WIDTH - PADDING - 100;
|
|
|
+
|
|
|
+ g2d.drawString("指标", col1X, y + 20);
|
|
|
+ g2d.drawString("当期", col2X, y + 20);
|
|
|
+ g2d.drawString("上期", col3X, y + 20);
|
|
|
+ g2d.drawString("变化率", col4X, y + 20);
|
|
|
+
|
|
|
+ // 绘制边框
|
|
|
+ g2d.setColor(BORDER_COLOR);
|
|
|
+ g2d.drawRect(PADDING, y, IMAGE_WIDTH - PADDING * 2, ROW_HEIGHT);
|
|
|
+
|
|
|
+ return y + ROW_HEIGHT;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 绘制数据行
|
|
|
+ */
|
|
|
+ private static int drawDataRow(Graphics2D g2d, int y, DataRow row, Font dataFont, Font labelFont) {
|
|
|
+ // 绘制行背景(交替颜色)
|
|
|
+ if ((y / ROW_HEIGHT) % 2 == 0) {
|
|
|
+ g2d.setColor(new Color(250, 250, 250));
|
|
|
+ g2d.fillRect(PADDING, y, IMAGE_WIDTH - PADDING * 2, ROW_HEIGHT);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 绘制指标名称
|
|
|
+ g2d.setFont(labelFont);
|
|
|
+ g2d.setColor(TEXT_COLOR);
|
|
|
+ g2d.drawString(row.getLabel(), PADDING + 10, y + 22);
|
|
|
+
|
|
|
+ // 绘制当期值
|
|
|
+ g2d.setFont(dataFont);
|
|
|
+ String currentValue = formatValue(row.getCurrent());
|
|
|
+ g2d.drawString(currentValue, IMAGE_WIDTH / 2 - 80, y + 22);
|
|
|
+
|
|
|
+ // 绘制上期值
|
|
|
+ String previousValue = formatValue(row.getPrevious());
|
|
|
+ g2d.drawString(previousValue, IMAGE_WIDTH / 2 + 20, y + 22);
|
|
|
+
|
|
|
+ // 绘制变化率(带颜色)
|
|
|
+ String changeRate = formatChangeRate(row.getChangeRate());
|
|
|
+ Color changeColor = row.getChangeRate() != null && row.getChangeRate().compareTo(BigDecimal.ZERO) >= 0
|
|
|
+ ? POSITIVE_COLOR : NEGATIVE_COLOR;
|
|
|
+ g2d.setColor(changeColor);
|
|
|
+ g2d.drawString(changeRate, IMAGE_WIDTH - PADDING - 100, y + 22);
|
|
|
+
|
|
|
+ // 绘制边框
|
|
|
+ g2d.setColor(BORDER_COLOR);
|
|
|
+ g2d.drawLine(PADDING, y + ROW_HEIGHT, IMAGE_WIDTH - PADDING, y + ROW_HEIGHT);
|
|
|
+
|
|
|
+ return y + ROW_HEIGHT;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 格式化数值
|
|
|
+ */
|
|
|
+ private static String formatValue(Object value) {
|
|
|
+ if (value == null) {
|
|
|
+ return "0";
|
|
|
+ }
|
|
|
+ if (value instanceof BigDecimal) {
|
|
|
+ BigDecimal bd = (BigDecimal) value;
|
|
|
+ if (bd.scale() > 0) {
|
|
|
+ return DECIMAL_FORMAT.format(bd);
|
|
|
+ } else {
|
|
|
+ return String.valueOf(bd.longValue());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (value instanceof Number) {
|
|
|
+ return DECIMAL_FORMAT.format(((Number) value).doubleValue());
|
|
|
+ }
|
|
|
+ return String.valueOf(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 格式化变化率
|
|
|
+ */
|
|
|
+ private static String formatChangeRate(BigDecimal changeRate) {
|
|
|
+ if (changeRate == null) {
|
|
|
+ return "0.00%";
|
|
|
+ }
|
|
|
+ String sign = changeRate.compareTo(BigDecimal.ZERO) >= 0 ? "+" : "";
|
|
|
+ return sign + PERCENT_FORMAT.format(changeRate.divide(new BigDecimal(100), 4, BigDecimal.ROUND_HALF_UP));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建流量数据行(按照实体类字段顺序)
|
|
|
+ */
|
|
|
+ private static List<DataRow> buildTrafficDataRows(StoreOperationalStatisticsComparisonVo.TrafficDataComparison data) {
|
|
|
+ List<DataRow> rows = new ArrayList<>();
|
|
|
+ // 1. 店铺搜索量对比
|
|
|
+ if (data.getStoreSearchVolume() != null) {
|
|
|
+ rows.add(new DataRow("店铺搜索量对比", data.getStoreSearchVolume().getCurrent(),
|
|
|
+ data.getStoreSearchVolume().getPrevious(), data.getStoreSearchVolume().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 2. 浏览量对比
|
|
|
+ if (data.getPageViews() != null) {
|
|
|
+ rows.add(new DataRow("浏览量对比", data.getPageViews().getCurrent(),
|
|
|
+ data.getPageViews().getPrevious(), data.getPageViews().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 3. 访客数对比
|
|
|
+ if (data.getVisitors() != null) {
|
|
|
+ rows.add(new DataRow("访客数对比", data.getVisitors().getCurrent(),
|
|
|
+ data.getVisitors().getPrevious(), data.getVisitors().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 4. 新增访客数对比
|
|
|
+ if (data.getNewVisitors() != null) {
|
|
|
+ rows.add(new DataRow("新增访客数对比", data.getNewVisitors().getCurrent(),
|
|
|
+ data.getNewVisitors().getPrevious(), data.getNewVisitors().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 5. 访问时长对比
|
|
|
+ if (data.getVisitDuration() != null) {
|
|
|
+ rows.add(new DataRow("访问时长对比", data.getVisitDuration().getCurrent(),
|
|
|
+ data.getVisitDuration().getPrevious(), data.getVisitDuration().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 6. 平均访问时长对比
|
|
|
+ if (data.getAvgVisitDuration() != null) {
|
|
|
+ rows.add(new DataRow("平均访问时长对比", data.getAvgVisitDuration().getCurrent(),
|
|
|
+ data.getAvgVisitDuration().getPrevious(), data.getAvgVisitDuration().getChangeRate()));
|
|
|
+ }
|
|
|
+ return rows;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建互动数据行(按照实体类字段顺序)
|
|
|
+ */
|
|
|
+ private static List<DataRow> buildInteractionDataRows(StoreOperationalStatisticsComparisonVo.InteractionDataComparison data) {
|
|
|
+ List<DataRow> rows = new ArrayList<>();
|
|
|
+ // 1. 店铺收藏次数对比
|
|
|
+ if (data.getStoreCollectionCount() != null) {
|
|
|
+ rows.add(new DataRow("店铺收藏次数对比", data.getStoreCollectionCount().getCurrent(),
|
|
|
+ data.getStoreCollectionCount().getPrevious(), data.getStoreCollectionCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 2. 店铺分享次数对比
|
|
|
+ if (data.getStoreShareCount() != null) {
|
|
|
+ rows.add(new DataRow("店铺分享次数对比", data.getStoreShareCount().getCurrent(),
|
|
|
+ data.getStoreShareCount().getPrevious(), data.getStoreShareCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 3. 店铺打卡次数对比
|
|
|
+ if (data.getStoreCheckInCount() != null) {
|
|
|
+ rows.add(new DataRow("店铺打卡次数对比", data.getStoreCheckInCount().getCurrent(),
|
|
|
+ data.getStoreCheckInCount().getPrevious(), data.getStoreCheckInCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 4. 咨询商家次数对比
|
|
|
+ if (data.getConsultMerchantCount() != null) {
|
|
|
+ rows.add(new DataRow("咨询商家次数对比", data.getConsultMerchantCount().getCurrent(),
|
|
|
+ data.getConsultMerchantCount().getPrevious(), data.getConsultMerchantCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 5. 好友数量对比
|
|
|
+ if (data.getFriendsCount() != null) {
|
|
|
+ rows.add(new DataRow("好友数量对比", data.getFriendsCount().getCurrent(),
|
|
|
+ data.getFriendsCount().getPrevious(), data.getFriendsCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 6. 关注数量对比
|
|
|
+ if (data.getFollowCount() != null) {
|
|
|
+ rows.add(new DataRow("关注数量对比", data.getFollowCount().getCurrent(),
|
|
|
+ data.getFollowCount().getPrevious(), data.getFollowCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 7. 粉丝数量对比
|
|
|
+ if (data.getFansCount() != null) {
|
|
|
+ rows.add(new DataRow("粉丝数量对比", data.getFansCount().getCurrent(),
|
|
|
+ data.getFansCount().getPrevious(), data.getFansCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 8. 发布动态数量对比
|
|
|
+ if (data.getPostsPublishedCount() != null) {
|
|
|
+ rows.add(new DataRow("发布动态数量对比", data.getPostsPublishedCount().getCurrent(),
|
|
|
+ data.getPostsPublishedCount().getPrevious(), data.getPostsPublishedCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 9. 动态点赞数量对比
|
|
|
+ if (data.getPostLikesCount() != null) {
|
|
|
+ rows.add(new DataRow("动态点赞数量对比", data.getPostLikesCount().getCurrent(),
|
|
|
+ data.getPostLikesCount().getPrevious(), data.getPostLikesCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 10. 动态评论数量对比
|
|
|
+ if (data.getPostCommentsCount() != null) {
|
|
|
+ rows.add(new DataRow("动态评论数量对比", data.getPostCommentsCount().getCurrent(),
|
|
|
+ data.getPostCommentsCount().getPrevious(), data.getPostCommentsCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 11. 动态转发数量对比
|
|
|
+ if (data.getPostSharesCount() != null) {
|
|
|
+ rows.add(new DataRow("动态转发数量对比", data.getPostSharesCount().getCurrent(),
|
|
|
+ data.getPostSharesCount().getPrevious(), data.getPostSharesCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 12. 被举报次数对比
|
|
|
+ if (data.getReportedCount() != null) {
|
|
|
+ rows.add(new DataRow("被举报次数对比", data.getReportedCount().getCurrent(),
|
|
|
+ data.getReportedCount().getPrevious(), data.getReportedCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 13. 被拉黑次数对比
|
|
|
+ if (data.getBlockedCount() != null) {
|
|
|
+ rows.add(new DataRow("被拉黑次数对比", data.getBlockedCount().getCurrent(),
|
|
|
+ data.getBlockedCount().getPrevious(), data.getBlockedCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ return rows;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建优惠券数据行(按照实体类字段顺序)
|
|
|
+ */
|
|
|
+ private static List<DataRow> buildCouponDataRows(StoreOperationalStatisticsComparisonVo.CouponDataComparison data) {
|
|
|
+ List<DataRow> rows = new ArrayList<>();
|
|
|
+ // 1. 赠送好友数量对比
|
|
|
+ if (data.getGiftToFriendsCount() != null) {
|
|
|
+ rows.add(new DataRow("赠送好友数量对比", data.getGiftToFriendsCount().getCurrent(),
|
|
|
+ data.getGiftToFriendsCount().getPrevious(), data.getGiftToFriendsCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 2. 赠送好友金额合计对比
|
|
|
+ if (data.getGiftToFriendsAmount() != null) {
|
|
|
+ rows.add(new DataRow("赠送好友金额合计对比", data.getGiftToFriendsAmount().getCurrent(),
|
|
|
+ data.getGiftToFriendsAmount().getPrevious(), data.getGiftToFriendsAmount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 3. 赠送好友使用数量对比
|
|
|
+ if (data.getGiftToFriendsUsedCount() != null) {
|
|
|
+ rows.add(new DataRow("赠送好友使用数量对比", data.getGiftToFriendsUsedCount().getCurrent(),
|
|
|
+ data.getGiftToFriendsUsedCount().getPrevious(), data.getGiftToFriendsUsedCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 4. 赠送好友使用金额合计对比
|
|
|
+ if (data.getGiftToFriendsUsedAmount() != null) {
|
|
|
+ rows.add(new DataRow("赠送好友使用金额合计对比", data.getGiftToFriendsUsedAmount().getCurrent(),
|
|
|
+ data.getGiftToFriendsUsedAmount().getPrevious(), data.getGiftToFriendsUsedAmount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 5. 赠送好友使用金额占比对比
|
|
|
+ if (data.getGiftToFriendsUsedAmountRatio() != null) {
|
|
|
+ rows.add(new DataRow("赠送好友使用金额占比对比", data.getGiftToFriendsUsedAmountRatio().getCurrent(),
|
|
|
+ data.getGiftToFriendsUsedAmountRatio().getPrevious(), data.getGiftToFriendsUsedAmountRatio().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 6. 好友赠送数量对比
|
|
|
+ if (data.getFriendsGiftCount() != null) {
|
|
|
+ rows.add(new DataRow("好友赠送数量对比", data.getFriendsGiftCount().getCurrent(),
|
|
|
+ data.getFriendsGiftCount().getPrevious(), data.getFriendsGiftCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 7. 好友赠送金额合计对比
|
|
|
+ if (data.getFriendsGiftAmount() != null) {
|
|
|
+ rows.add(new DataRow("好友赠送金额合计对比", data.getFriendsGiftAmount().getCurrent(),
|
|
|
+ data.getFriendsGiftAmount().getPrevious(), data.getFriendsGiftAmount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 8. 好友赠送使用数量对比
|
|
|
+ if (data.getFriendsGiftUsedCount() != null) {
|
|
|
+ rows.add(new DataRow("好友赠送使用数量对比", data.getFriendsGiftUsedCount().getCurrent(),
|
|
|
+ data.getFriendsGiftUsedCount().getPrevious(), data.getFriendsGiftUsedCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 9. 好友赠送使用金额合计对比
|
|
|
+ if (data.getFriendsGiftUsedAmount() != null) {
|
|
|
+ rows.add(new DataRow("好友赠送使用金额合计对比", data.getFriendsGiftUsedAmount().getCurrent(),
|
|
|
+ data.getFriendsGiftUsedAmount().getPrevious(), data.getFriendsGiftUsedAmount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 10. 好友赠送使用金额占比对比
|
|
|
+ if (data.getFriendsGiftUsedAmountRatio() != null) {
|
|
|
+ rows.add(new DataRow("好友赠送使用金额占比对比", data.getFriendsGiftUsedAmountRatio().getCurrent(),
|
|
|
+ data.getFriendsGiftUsedAmountRatio().getPrevious(), data.getFriendsGiftUsedAmountRatio().getChangeRate()));
|
|
|
+ }
|
|
|
+ return rows;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建代金券数据行(按照实体类字段顺序)
|
|
|
+ */
|
|
|
+ private static List<DataRow> buildVoucherDataRows(StoreOperationalStatisticsComparisonVo.VoucherDataComparison data) {
|
|
|
+ List<DataRow> rows = new ArrayList<>();
|
|
|
+ // 1. 赠送好友数量对比
|
|
|
+ if (data.getGiftToFriendsCount() != null) {
|
|
|
+ rows.add(new DataRow("赠送好友数量对比", data.getGiftToFriendsCount().getCurrent(),
|
|
|
+ data.getGiftToFriendsCount().getPrevious(), data.getGiftToFriendsCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 2. 赠送好友金额合计对比
|
|
|
+ if (data.getGiftToFriendsAmount() != null) {
|
|
|
+ rows.add(new DataRow("赠送好友金额合计对比", data.getGiftToFriendsAmount().getCurrent(),
|
|
|
+ data.getGiftToFriendsAmount().getPrevious(), data.getGiftToFriendsAmount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 3. 赠送好友使用数量对比
|
|
|
+ if (data.getGiftToFriendsUsedCount() != null) {
|
|
|
+ rows.add(new DataRow("赠送好友使用数量对比", data.getGiftToFriendsUsedCount().getCurrent(),
|
|
|
+ data.getGiftToFriendsUsedCount().getPrevious(), data.getGiftToFriendsUsedCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 4. 赠送好友使用金额合计对比
|
|
|
+ if (data.getGiftToFriendsUsedAmount() != null) {
|
|
|
+ rows.add(new DataRow("赠送好友使用金额合计对比", data.getGiftToFriendsUsedAmount().getCurrent(),
|
|
|
+ data.getGiftToFriendsUsedAmount().getPrevious(), data.getGiftToFriendsUsedAmount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 5. 赠送好友使用金额占比对比
|
|
|
+ if (data.getGiftToFriendsUsedAmountRatio() != null) {
|
|
|
+ rows.add(new DataRow("赠送好友使用金额占比对比", data.getGiftToFriendsUsedAmountRatio().getCurrent(),
|
|
|
+ data.getGiftToFriendsUsedAmountRatio().getPrevious(), data.getGiftToFriendsUsedAmountRatio().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 6. 好友赠送数量对比
|
|
|
+ if (data.getFriendsGiftCount() != null) {
|
|
|
+ rows.add(new DataRow("好友赠送数量对比", data.getFriendsGiftCount().getCurrent(),
|
|
|
+ data.getFriendsGiftCount().getPrevious(), data.getFriendsGiftCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 7. 好友赠送金额合计对比
|
|
|
+ if (data.getFriendsGiftAmount() != null) {
|
|
|
+ rows.add(new DataRow("好友赠送金额合计对比", data.getFriendsGiftAmount().getCurrent(),
|
|
|
+ data.getFriendsGiftAmount().getPrevious(), data.getFriendsGiftAmount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 8. 好友赠送使用数量对比
|
|
|
+ if (data.getFriendsGiftUsedCount() != null) {
|
|
|
+ rows.add(new DataRow("好友赠送使用数量对比", data.getFriendsGiftUsedCount().getCurrent(),
|
|
|
+ data.getFriendsGiftUsedCount().getPrevious(), data.getFriendsGiftUsedCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 9. 好友赠送使用金额合计对比
|
|
|
+ if (data.getFriendsGiftUsedAmount() != null) {
|
|
|
+ rows.add(new DataRow("好友赠送使用金额合计对比", data.getFriendsGiftUsedAmount().getCurrent(),
|
|
|
+ data.getFriendsGiftUsedAmount().getPrevious(), data.getFriendsGiftUsedAmount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 10. 好友赠送使用金额占比对比
|
|
|
+ if (data.getFriendsGiftUsedAmountRatio() != null) {
|
|
|
+ rows.add(new DataRow("好友赠送使用金额占比对比", data.getFriendsGiftUsedAmountRatio().getCurrent(),
|
|
|
+ data.getFriendsGiftUsedAmountRatio().getPrevious(), data.getFriendsGiftUsedAmountRatio().getChangeRate()));
|
|
|
+ }
|
|
|
+ return rows;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建服务质量数据行(按照实体类字段顺序)
|
|
|
+ */
|
|
|
+ private static List<DataRow> buildServiceQualityDataRows(StoreOperationalStatisticsComparisonVo.ServiceQualityDataComparison data) {
|
|
|
+ List<DataRow> rows = new ArrayList<>();
|
|
|
+ // 1. 店铺评分对比
|
|
|
+ if (data.getStoreRating() != null) {
|
|
|
+ rows.add(new DataRow("店铺评分对比", data.getStoreRating().getCurrent(),
|
|
|
+ data.getStoreRating().getPrevious(), data.getStoreRating().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 2. 口味评分对比
|
|
|
+ if (data.getTasteRating() != null) {
|
|
|
+ rows.add(new DataRow("口味评分对比", data.getTasteRating().getCurrent(),
|
|
|
+ data.getTasteRating().getPrevious(), data.getTasteRating().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 3. 环境评分对比
|
|
|
+ if (data.getEnvironmentRating() != null) {
|
|
|
+ rows.add(new DataRow("环境评分对比", data.getEnvironmentRating().getCurrent(),
|
|
|
+ data.getEnvironmentRating().getPrevious(), data.getEnvironmentRating().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 4. 服务评分对比
|
|
|
+ if (data.getServiceRating() != null) {
|
|
|
+ rows.add(new DataRow("服务评分对比", data.getServiceRating().getCurrent(),
|
|
|
+ data.getServiceRating().getPrevious(), data.getServiceRating().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 5. 评价数量对比
|
|
|
+ if (data.getTotalReviews() != null) {
|
|
|
+ rows.add(new DataRow("评价数量对比", data.getTotalReviews().getCurrent(),
|
|
|
+ data.getTotalReviews().getPrevious(), data.getTotalReviews().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 6. 好评数量对比
|
|
|
+ if (data.getPositiveReviews() != null) {
|
|
|
+ rows.add(new DataRow("好评数量对比", data.getPositiveReviews().getCurrent(),
|
|
|
+ data.getPositiveReviews().getPrevious(), data.getPositiveReviews().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 7. 中评数量对比
|
|
|
+ if (data.getNeutralReviews() != null) {
|
|
|
+ rows.add(new DataRow("中评数量对比", data.getNeutralReviews().getCurrent(),
|
|
|
+ data.getNeutralReviews().getPrevious(), data.getNeutralReviews().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 8. 差评数量对比
|
|
|
+ if (data.getNegativeReviews() != null) {
|
|
|
+ rows.add(new DataRow("差评数量对比", data.getNegativeReviews().getCurrent(),
|
|
|
+ data.getNegativeReviews().getPrevious(), data.getNegativeReviews().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 9. 差评占比对比
|
|
|
+ if (data.getNegativeReviewRatio() != null) {
|
|
|
+ rows.add(new DataRow("差评占比对比", data.getNegativeReviewRatio().getCurrent(),
|
|
|
+ data.getNegativeReviewRatio().getPrevious(), data.getNegativeReviewRatio().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 10. 差评申诉次数对比
|
|
|
+ if (data.getNegativeReviewAppealsCount() != null) {
|
|
|
+ rows.add(new DataRow("差评申诉次数对比", data.getNegativeReviewAppealsCount().getCurrent(),
|
|
|
+ data.getNegativeReviewAppealsCount().getPrevious(), data.getNegativeReviewAppealsCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 11. 差评申诉成功次数对比
|
|
|
+ if (data.getNegativeReviewAppealsSuccessCount() != null) {
|
|
|
+ rows.add(new DataRow("差评申诉成功次数对比", data.getNegativeReviewAppealsSuccessCount().getCurrent(),
|
|
|
+ data.getNegativeReviewAppealsSuccessCount().getPrevious(), data.getNegativeReviewAppealsSuccessCount().getChangeRate()));
|
|
|
+ }
|
|
|
+ // 12. 差评申诉成功占比对比
|
|
|
+ if (data.getNegativeReviewAppealsSuccessRatio() != null) {
|
|
|
+ rows.add(new DataRow("差评申诉成功占比对比", data.getNegativeReviewAppealsSuccessRatio().getCurrent(),
|
|
|
+ data.getNegativeReviewAppealsSuccessRatio().getPrevious(), data.getNegativeReviewAppealsSuccessRatio().getChangeRate()));
|
|
|
+ }
|
|
|
+ return rows;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 数据行内部类
|
|
|
+ */
|
|
|
+ private static class DataRow {
|
|
|
+ private String label;
|
|
|
+ private Object current;
|
|
|
+ private Object previous;
|
|
|
+ private BigDecimal changeRate;
|
|
|
+
|
|
|
+ public DataRow(String label, Object current, Object previous, BigDecimal changeRate) {
|
|
|
+ this.label = label;
|
|
|
+ this.current = current;
|
|
|
+ this.previous = previous;
|
|
|
+ this.changeRate = changeRate;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getLabel() {
|
|
|
+ return label;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Object getCurrent() {
|
|
|
+ return current;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Object getPrevious() {
|
|
|
+ return previous;
|
|
|
+ }
|
|
|
+
|
|
|
+ public BigDecimal getChangeRate() {
|
|
|
+ return changeRate;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|