XNMS项目-业务统计-终端业务

文章目录

一、背景

XNMS(Extended Network Management System,增强型网络管理系统)是一款远程监控和管理常规中转台的软件。

中转台是系统的核心设备,所有业务都通过其进行中转。因此,只要对中转台进行监控,就能全面掌握系统的运行状况。而中转台通常部署室外,容易受到日晒雨淋等自然条件影响,造成设备损坏。为保证通讯系统正常运行,工作人员需要对中转台进行实时监控,发现中转台的异常问题,从而采取相关措施进行补救。

通过XNMS软件,工作人员可实时监控常规中转台的各项参数和告警情况,对异常问题进行排查;还可以查询或统计某时间段内中转台或终端的业务,从而全面了解常规系统的运行状况。

项目采用:Arco Design+java+mysql+springboot+vue3

二、页面

终端业务

统计和导出指定时间段内,所有终端的业务信息,包括次数、总时长及其占比。终端业务类型包括语音业务、GNSS业务、短消息业务、注册服务业务和其他业务。

  • 按表统计:选择需要统计的日期和业务类型,单击"统计",生成统计列表。该列表显示所有终端的业务信息。
  • 按图统计:生成统计列表后,单击列表中的某个终端, 报表下方左侧折线图显示该终端的语音业务信息;右侧折线图显示该终端的数据业务信息,包括GNSS业务、短消息业务、注册服务业务和其他业务。如下图所示。
    备注:您可将鼠标移动至图中某个时间点查看终端当天执行该业务的次数。



三、代码

TerminalBusiness.vue

java 复制代码
<template>
  <a-spin
      :size="80"
      :loading="uploadeLoading"
      :tip="loadingTip"
      style="width: 100%; height: 100%"
  >
    <layout_1>
      <a-scrollbar style="height: 800px; overflow: auto">
        <div class="--search-line">
          <div>
            <div class="key">{{ $t(queryButtonValue[7]) }}&nbsp;</div>
            <div class="val">
              <a-range-picker
                  style="width: 280px"
                  :allow-clear="false"
                  v-model="param.timeRange"
                  :disabled-date="disabledDate"
              >
                <template #suffix-icon>
                  <svg-loader :width="20" :height="20" name="clock"></svg-loader>
                </template>
                <template #separator>
                  <svg-loader
                      :width="16"
                      :height="16"
                      name="arrow-right"
                  ></svg-loader>
                </template>
              </a-range-picker>
            </div>
          </div>
          <div>
            <div class="key">{{ $t(queryButtonValue[10]) }}&nbsp;</div>
            <div class="val select-input">
              <!-- <a-select v-model="param.businessTypes" :multiple="true" :max-tag-count="1">
              <a-option v-for="(val, key) in statBusinessType" :label="val" :value="key" :key="key"></a-option>
            </a-select> -->
              <a-tree-select
                  class="arco-tree-select --arco-select"
                  :data="treeData"
                  v-model="param.businessTypes"
                  :tree-checkable="true"
                  :tree-check-strictly="false"
                  tree-checked-strategy="child"
                  :max-tag-count="1"
              />
            </div>
          </div>

          <a-button class="huge" @click="search" type="primary">
            <template #icon> <icon-search size="18" /> </template
            >{{ $t(queryButtonValue[1]) }}
          </a-button>
          <a-button class="huge" @click="resetSearch">
            <template #icon>
              <svg-loader
                  :width="20"
                  :height="20"
                  name="reset"
              ></svg-loader> </template
            >{{ $t(queryButtonValue[21]) }}</a-button
          >
          <a-dropdown @select="handleSelect">
            <a-button class="huge" :disabled="!tableData?.length">
              <template #icon> <icon-export size="18" /> </template>
              {{ $t(queryButtonValue[3]) }}
            </a-button>
            <template #content>
              <a-doption value="excel">Excel</a-doption>
              <a-doption value="pdf">PDF</a-doption>
              <a-doption value="png">PNG</a-doption>
              <a-doption value="jpeg">JPEG</a-doption>
            </template>
          </a-dropdown>
        </div>
        <div class="table-line" ref="exportContentRef">
          <a-table
              :data="tableData"
              :loading="tableLoading"
              :bordered="{ headerCell: true }"
              :pagination="false"
              :scroll="{ x: '100%', y: 550 }"
              @row-click="handleRowClick"
          >
            <template #empty>
              <div style="text-align: center">{{ $t("NoData") }}</div>
            </template>
            <template #columns>
              <a-table-column
                  :title="$t(queryColumnValue[1])"
                  dataIndex="index"
                  :tooltip="true"
              ></a-table-column>
              <a-table-column
                  :title="$t(queryColumnValue[79])"
                  dataIndex="terminalID"
                  :tooltip="true"
              ></a-table-column>
              <a-table-column
                  :title="$t(queryColumnValue[61])"
                  v-if="param.businessTypes.includes(1)"
              >
                <a-table-column
                    :title="$t(queryColumnValue[66])"
                    dataIndex="radioTotalCount"
                />
                <a-table-column
                    :title="$t(queryColumnValue[67])"
                    dataIndex="radioTotalTime"
                >
                  <template #cell="{ record }">
                    {{ formatTime(record.radioTotalTime) }}
                  </template>
                </a-table-column>
                <a-table-column
                    :title="$t(queryColumnValue[68])"
                    dataIndex="radioAverageTime"
                >
                  <template #cell="{ record }">
                    {{ formatTime(record.radioAverageTime) }}
                  </template>
                </a-table-column>
              </a-table-column>
              <a-table-column
                  :title="$t(queryColumnValue[62])"
                  v-if="param.businessTypes.includes(2)"
              >
                <a-table-column
                    :title="$t(queryColumnValue[80])"
                    dataIndex="messageTotalCount"
                />
                <a-table-column
                    :title="$t(queryColumnValue[81])"
                    dataIndex="messageTotalTime"
                >
                  <template #cell="{ record }">
                    {{ formatTime(record.messageTotalTime) }}
                  </template>
                </a-table-column>
              </a-table-column>
              <a-table-column
                  :title="$t(queryColumnValue[63])"
                  v-if="param.businessTypes.includes(3)"
              >
                <a-table-column
                    :title="$t(queryColumnValue[69])"
                    dataIndex="gpsTotalCount"
                />
                <a-table-column
                    :title="$t(queryColumnValue[70])"
                    dataIndex="gpsTotalTime"
                >
                  <template #cell="{ record }">
                    {{ formatTime(record.gpsTotalTime) }}
                  </template>
                </a-table-column>
              </a-table-column>
              <a-table-column
                  :title="$t(queryColumnValue[64])"
                  v-if="param.businessTypes.includes(4)"
              >
                <a-table-column
                    :title="$t(queryColumnValue[71])"
                    dataIndex="registTotalCount"
                />
                <a-table-column
                    :title="$t(queryColumnValue[72])"
                    dataIndex="registTotalTime"
                >
                  <template #cell="{ record }">
                    {{ formatTime(record.registTotalTime) }}
                  </template>
                </a-table-column>
              </a-table-column>
              <a-table-column
                  :title="$t(queryColumnValue[65])"
                  v-if="param.businessTypes.includes(5)"
              >
                <a-table-column
                    :title="$t(queryColumnValue[73])"
                    dataIndex="otherTotalCount"
                />
                <a-table-column
                    :title="$t(queryColumnValue[74])"
                    dataIndex="otherTotalTime"
                >
                  <template #cell="{ record }">
                    {{ formatTime(record.otherTotalTime) }}
                  </template>
                </a-table-column>
              </a-table-column>
              <a-table-column
                  :title="$t(queryColumnValue[78])"
                  dataIndex="proportion"
                  :tooltip="true"
              >
                <template #cell="{ record }">
                  {{ `${(record.proportion * 100).toFixed(2)}%` }}
                </template>
              </a-table-column>
            </template>
          </a-table>
          <div class="--table-pager">
            <a-config-provider :locale="arcoLang">
              <a-pagination
                  :total="page.total"
                  v-model:current="page.currentPage"
                  v-model:page-size="page.pageSize"
                  @change="getTableData"
                  @page-size-change="getTableData"
                  show-total
                  show-page-size
                  show-jumper
              /></a-config-provider>
          </div>
          <div v-if="showRecordChart" class="chart_box">
            <div class="title">{{ detailTitle }}</div>
            <div class="content" id="terminal_business_content_box">
              <div class="content-item box-1 inline">
                <div class="content-item-title">
                  {{ $t(queryColumnValue[116]) }}
                </div>
                <div
                    :id="`voice_business_by_date_${recordInfo.terminalID}`"
                    class="chart-1"
                ></div>
              </div>
              <div class="content-item box-2 inline">
                <div class="content-item-title">
                  {{ $t(queryColumnValue[117]) }}
                </div>
                <div
                    :id="`data_business_by_date_${recordInfo.terminalID}`"
                    class="chart-2"
                ></div>
                <div class="content-item-icon">
                  <div
                      v-for="(item, index) in filteredbizTypesTitlesItems"
                      :key="index"
                      :id="`content-item-icon-container${index}`"
                      :class="{
                    'content-item-icon-container': true,
                    'content-item-icon-greyed-out-0': index === 0 && g0,
                    'content-item-icon-greyed-out-1': index === 1 && g1,
                    'content-item-icon-greyed-out-2': index === 2 && g2,
                    'content-item-icon-greyed-out-3': index === 3 && g3,
                  }"
                      @click="
                    toggleLine($t(queryColumnValue[item.enumValueIndex]), index)
                  "
                  >
                    <div
                        class="content-item-icon-container-inline"
                        :style="{ backgroundColor: item.color }"
                    ></div>
                    <div class="content-item-icon-container-text">
                      {{ $t(queryColumnValue[item.enumValueIndex]) }}
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </a-scrollbar>
    </layout_1>
  </a-spin>
</template>

<script setup>
import {
  commonResponse,
  formatDateToYyyyMMddHHmmss,
  formatTime,
} from "@/views/pages/_common";
import {
  queryButtonValue,
  queryColumnValue,
  queryName,
  statBusinessType,
} from "@/views/pages/_common/enum";
import Layout_1 from "@/views/pages/_common/layout_1.vue";
import {
  exportTerminalBusinessDetailCountByDayBizType,
  exportTerminalBusinessStat,
  getTerminalBusinessStatList,
} from "@/views/pages/business/_request";
import * as echarts from "echarts";
import html2canvas from "html2canvas";
import JsPDF from "jspdf";
import * as moment from "moment";
import { computed, inject, reactive, ref, watch } from "vue";
const arcoLang = inject("arcoLang");
const t = inject("t");
const exportType = ref('');
const uploadeLoading = ref(false);
const param = reactive({
  timeRange: [null, null],
  businessTypes: [],
});
let reqParam = {
  startTime: null,
  endTime: null,
  businessTypes: [],
};
const page = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});

const treeData = [
  {
    title: t(statBusinessType[0]), // 根节点 (全选项)
    value: 0,
    key: 0,
    children: Object.keys(statBusinessType)
      .slice(1)
      .map((key) => ({
        title: t(statBusinessType[key]),
        value: Number(key),
        key: Number(key),
      })),
  },
];

// 监听 param.businessTypes 的变化
watch(
  () => param.businessTypes,
  (newValue) => {
    if (newValue.includes(0)) {
      // 如果用户选择了全选(即选择了 0),将其转换为 1, 2, 3, 4, 5 的数组
      param.businessTypes = [1, 2, 3, 4, 5];
    }
  }
);

const showRecordChart = ref(false);
const tableLoading = ref(false);
const exportContentRef = ref(null);
const tableData = ref([]);
const getTableData = () => {
  tableLoading.value = true;
  getTerminalBusinessStatList({
    ...reqParam,
    currentPage: page.currentPage,
    pageSize: page.pageSize,
  })
    .then((response) => {
      tableLoading.value = false;
      commonResponse({
        response,
        onSuccess: () => {
          tableData.value = response.data;
          page.total = response.pagination.totalRecords;
        },
      });
    })
    .catch((e) => (tableLoading.value = true));
};

const disabledDate = (date) => {
  return date.getTime() > moment().format("x");
};

const search = () => {
  reqParam = {
    ...reqParam,
    ...param,
  };
  reqParam.startTime = moment(reqParam.timeRange[0]).format(
    "YYYY-MM-DDT00:00:00.000"
  );
  reqParam.endTime = moment(reqParam.timeRange[1]).format(
    "YYYY-MM-DDT23:59:59.999"
  );
  delete reqParam.timeRange;
  getTableData();
};

const resetSearch = () => {
  param.timeRange = [moment().add(-9, "days"), moment()];
  param.businessTypes = [];
  page.currentPage = 1;
  page.total = 0;
  tableData.value = [];
  showRecordChart.value = false;
};

const loadingTip = computed(() => {
  if (!exportType.value) return t('ExportStatus_Exporting');
  return `${exportType.value} ${t('Outputting')}`;
});

const handleSelect = (t) => {
  exportType.value = t;
  uploadeLoading.value = true;
  exportFormat(t);
};

// 导出功能
const exportTable = async () => {
  tableLoading.value = true;
  try {
    const response = await exportTerminalBusinessStat({
      ...reqParam,
    });
    tableLoading.value = false;
    // 创建 Blob 对象
    const blob = new Blob([response], {
      type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    });
    const url = window.URL.createObjectURL(blob);
    // 创建一个临时的 <a> 标签来触发下载
    const link = document.createElement("a");
    link.href = url;
    link.download =
        t(queryName["RadioService"]) + formatDateToYyyyMMddHHmmss(new Date()); // 设置文件保存名称
    link.click();
    // 清理 URL 对象
    window.URL.revokeObjectURL(url);
  } catch (error) {
    tableLoading.value = true;
    console.error("导出.excel图片失败:", error);
  } finally {
    setTimeout(() => uploadeLoading.value = false, 3000);
  }
};

// 处理不同格式的导出请求
const exportFormat = (format) => {
  const title =
    t(queryName["RadioService"]) + formatDateToYyyyMMddHHmmss(new Date());
  if (format === "excel") {
    exportTable();
  } else if (format === "pdf") {
    exportPdf(exportContentRef.value, title);
  } else if (format === "png") {
    exportPng(exportContentRef.value, title);
  } else if (format === "jpeg") {
    exportJpeg(exportContentRef.value, title);
  }
};

// 导出 PDF
const exportPdf = async (el, title) => {
  // 获取元素的实际高度
  const contentHeight = el.scrollHeight;
  const contentWidth = el.scrollWidth;
  const getDynamicScale = (element) => {
    try {
      if (!element || !element.getBoundingClientRect) {
        console.warn('getDynamicScale: 元素无效或不存在');
        return 1;
      }
      
      const rect = element.getBoundingClientRect();
      const area = rect.width * rect.height;
    
      if (isNaN(area) || area <= 0) {
        console.warn('getDynamicScale: 计算出的面积无效', { width: rect.width, height: rect.height, area });
        return 1;
      }
      console.log('area', area, 'width:', rect.width, 'height:', rect.height);
      const areaMB = area / 1000000;
      if (!isFinite(areaMB)) {
        console.warn('getDynamicScale: areaMB无效', areaMB);
        return 1;
      }
      if (areaMB > 20) {
        return 0.5;
      } else if (areaMB > 15) {
        return 0.75;
      } else if (areaMB > 13) {
        return 0.8;
      } else if (areaMB > 10) {
        return 0.9;
      } else if (areaMB > 5) {
        return 0.95;
      } else if (areaMB > 4) {
        return 1;
      } else if (areaMB > 3) {
        return 1.25;
      } else if (areaMB > 2.5) {
        return 1.5;
      } else if (areaMB > 2) {
        return 1.5;
      } else {
        return 2;
      }
    } catch (error) {
      console.error('getDynamicScale 发生错误:', error);
      return 1;
    }
  };
  const scale = getDynamicScale(el);

  try {
    const canvas = await html2canvas(el, {
      allowTaint: true,
      taintTest: false,
      useCORS: true,
      logging: false,
      imageTimeout: 0,
      scale: scale,
      scrollX: 0,
      scrollY: 0,
      width: contentWidth,
      height: contentHeight,
    });
    let pageHeight = (contentWidth / 592.28) * 841.89;
    let leftHeight = contentHeight;
    let position = 0;
    let imgWidth = 592.28;
    let imgHeight = (592.28 / contentWidth) * contentHeight;
    let pageData = canvas.toDataURL("image/jpeg", 1.0);
    let PDF = new JsPDF("", "pt", "a4");
    if (leftHeight < pageHeight) {
      PDF.addImage(pageData, "JPEG", 0, 0, imgWidth, imgHeight);
    } else {
      while (leftHeight > 0) {
        PDF.addImage(pageData, "JPEG", 0, position, imgWidth, imgHeight);
        leftHeight -= pageHeight;
        position -= imgHeight;
        if (leftHeight > 0) {
          PDF.addPage();
        }
      }
    }
    PDF.save(title + ".pdf");
  } catch (error) {
    console.error("导出.pdf图片失败:", error);
  } finally {
    setTimeout(() => uploadeLoading.value = false, 3000);
  }
};

// 导出 PNG
const exportPng = async (el, title) => {
  const contentHeight = el.scrollHeight;
  const contentWidth = el.scrollWidth;
  try {
    const canvas = await html2canvas(el, {
      allowTaint: true,
      taintTest: false,
      useCORS: true,
      logging: false,
      imageTimeout: 0,
      scale: 2,
      scrollX: 0,
      scrollY: 0,
      width: contentWidth,
      height: contentHeight,
    });
    let url = canvas.toDataURL("image/png");
    let a = document.createElement("a");
    a.href = url;
    a.download = title + ".png";
    a.click();
    setTimeout(() => URL.revokeObjectURL(url), 1000);
  } catch (error) {
    console.error("导出.png图片失败:", error);
  } finally {
    setTimeout(() => uploadeLoading.value = false, 3000);
  }
};

// 导出 JPEG
const exportJpeg = async (el, title) => {
  const contentHeight = el.scrollHeight;
  const contentWidth = el.scrollWidth;
  try {
    const canvas = await html2canvas(el, {
      allowTaint: true,
      taintTest: false,
      useCORS: true,
      logging: false,
      imageTimeout: 0,
      scale: 2,
      scrollX: 0,
      scrollY: 0,
      width: contentWidth,
      height: contentHeight,
    });
    let url = canvas.toDataURL("image/jpeg", 0.9);
    let a = document.createElement("a");
    a.href = url;
    a.download = title + ".jpg";
    a.click();
    setTimeout(() => URL.revokeObjectURL(url), 1000);
  } catch (error) {
    console.error("导出.jpg图片失败:", error);
  } finally {
    setTimeout(() => uploadeLoading.value = false, 3000);
  }
};

let detailCountParam = {
  businessTypes: [],
  startTime: null,
  endTime: null,
  radioIDs: [],
};

let detailCountData = {};
const getDetailCountByDayBizType = (record) => {
  exportTerminalBusinessDetailCountByDayBizType({
    ...detailCountParam,
    currentPage: page.currentPage,
    pageSize: page.pageSize,
  }).then((response) => {
    commonResponse({
      response,
      onSuccess: () => {
        detailCountData = response.data;
        renderChartVoice(detailCountData, record);
        renderChartOtherData(detailCountData, record);
      },
    });
  });
};

const detailTitle = ref("");
const recordInfo = ref({});

//终端业务列标题枚举的索引
const bizTypesTitles = [
  { bizTypeValue: 1, enumValueIndex: 61, color: "#2FA5FB" },
  { bizTypeValue: 3, enumValueIndex: 63, color: "#7D51BC" },
  { bizTypeValue: 2, enumValueIndex: 62, color: "#2987B6" },
  { bizTypeValue: 4, enumValueIndex: 64, color: "#7A7CED" },
  { bizTypeValue: 5, enumValueIndex: 65, color: "#435088" },
];

const filteredbizTypesTitlesItems = computed(() => bizTypesTitles.slice(1));

const handleRowClick = (record) => {
  showRecordChart.value = true;
  detailTitle.value = t(queryColumnValue[115]) + `(Radio-${record.terminalID})`;
  recordInfo.value = record;
  detailCountParam.businessTypes = reqParam.businessTypes;
  detailCountParam.startTime = reqParam.startTime;
  detailCountParam.endTime = reqParam.endTime;
  detailCountParam.radioIDs = [record.terminalID];
  getDetailCountByDayBizType(record);
};
const renderChartVoice = (detailCountData, record) => {
  const x = Object.keys(detailCountData).sort();
  const y_1 = [];
  x.forEach((key) => {
    let nestedMap = detailCountData[key];
    if (
      nestedMap[bizTypesTitles[0].bizTypeValue] !== undefined &&
      nestedMap[bizTypesTitles[0].bizTypeValue] !== null
    ) {
      y_1.push(nestedMap[bizTypesTitles[0].bizTypeValue]);
    }
  });
  const chartDomVoice = document.querySelector(
    `#voice_business_by_date_${record.terminalID}`
  );
  const chart = echarts.init(chartDomVoice);
  const option = {
    color: ["#2FA5FB"],
    tooltip: {
      trigger: "axis",
      axisPointer: {
        type: "cross", // 鼠标滑过时显示交叉线
      },
      formatter: function (params) {
        // 获取日期(x 轴的值)
        const date = params[0].axisValue;
        // 获取 y 轴的次数(即每个系列的值)
        const count = params[0].value;
        // 返回格式化后的内容
        return `${date}<br>次数: ${count}`;
      },
    },
    legend: {
      itemGap: 20,
      textStyle: {
        color: "#202B40",
        fontFamily: "PingFang SC",
        fontSize: 13,
        fontWeight: 400,
        lineHeight: 22,
      },
    },
    grid: {
      left: "3%",
      right: "10%",
      bottom: "6%",
      containLabel: true,
    },
    toolbox: {
      feature: {
        saveAsImage: {},
      },
    },

    xAxis: {
      type: "category",
      boundaryGap: false,
      data: x,
      triggerEvent: true,
    },
    yAxis: {
      type: "value",
      name: t(queryColumnValue[118]),
      nameTextStyle: {
        color: "#778091",
        fontFamily: "PingFang SC",
        fontSize: 12,
        fontWeight: 400,
        lineHeight: 12,
        align: "left",
      },
      minInterval: 1,
    },
    series: [
      {
        type: "line",
        data: y_1,
        symbol: "circle",
        symbolSize: 5,
        connectNulls: true,
      },
    ],
  };
  option && chart.setOption(option);
};
let globalChartDomOtherData;
const renderChartOtherData = (detailCountData, record) => {
  const x = Object.keys(detailCountData).sort();
  const y_1 = [];
  for (let i = 1; i < bizTypesTitles.length; i++) {
    const y_2 = [];
    x.forEach((key) => {
      let nestedMap = detailCountData[key];
      y_2.push(nestedMap[bizTypesTitles[i].bizTypeValue]);
    });
    y_1.push({ y_2: y_2, bizTypesTitles: bizTypesTitles[i] });
  }

  const chartDomOtherData = document.querySelector(
    `#data_business_by_date_${record.terminalID}`
  );
  const chart = echarts.init(chartDomOtherData);
  globalChartDomOtherData = chart;
  const series = [];
  for (let i = 0; i < y_1.length; i++) {
    series.push({
      name: t(queryColumnValue[y_1[i].bizTypesTitles.enumValueIndex]),
      type: "line",
      areaStyle: {},
      data: y_1[i].y_2,
      symbol: "circle",
      symbolSize: 5,
      connectNulls: true,
      lineStyle: {
        opacity: 0.7, // 透明度
        width: 2,
        color: y_1[i].bizTypesTitles.color,
        emphasis: {
          color: y_1[i].bizTypesTitles.color,
        },
      },
    });
  }
  const option = {
    color: ["#7D51BC", "#2987B6", "#7A7CED", "#435088"],
    tooltip: {
      trigger: "axis",
      axisPointer: {
        type: "cross", // 鼠标滑过时显示交叉线
      },
    },
    legend: {
      show: false,
      itemGap: 20,
      textStyle: {
        color: "#202B40",
        fontFamily: "PingFang SC",
        fontSize: 13,
        fontWeight: 400,
        lineHeight: 22,
      },
    },
    grid: {
      left: "6%",
      right: "10%",
      bottom: "8%",
      containLabel: true,
    },
    toolbox: {
      feature: {
        saveAsImage: {},
      },
    },

    xAxis: {
      type: "category",
      boundaryGap: false,
      data: x,
      triggerEvent: true,
    },
    yAxis: {
      type: "value",
      name: t(queryColumnValue[118]),
      nameTextStyle: {
        color: "#778091",
        fontFamily: "PingFang SC",
        fontSize: 12,
        fontWeight: 400,
        lineHeight: 12,
        align: "left",
      },
      minInterval: 1,
    },
    series: series,
  };
  option && chart.setOption(option);
};
const g0 = ref(false);
const g1 = ref(false);
const g2 = ref(false);
const g3 = ref(false);
const toggleLine = (name, index) => {
  if (index == 0) {
    g0.value = !g0.value;
  }
  if (index == 1) {
    g1.value = !g1.value;
  }
  if (index == 2) {
    g2.value = !g2.value;
  }
  if (index == 3) {
    g3.value = !g3.value;
  }
  const option = globalChartDomOtherData.getOption();

  if (Object.prototype.hasOwnProperty.call(option.legend[0].selected, name)) {
    option.legend[0].selected[name] = !option.legend[0].selected[name];
  } else {
    option.legend[0].selected[name] = false;
  }
  globalChartDomOtherData.setOption(option, true);
};

const init = () => {
  resetSearch();
};

init();
</script>

<style scoped lang="less">
.table-line {
  box-sizing: border-box;
  margin-top: 20px;
  height: calc(100% - 60px);
}

.select-input {
  width: 160px;
}
.title {
  margin-top: 20px;
  margin-left: 40px;
  margin-top: 10px;
  padding-left: 10px;
}
.content {
  width: 100%;
  margin-top: 20px;
  &-item {
    position: relative;
    box-sizing: border-box;
    border: 1px solid #e5e7ec;
    border-radius: 10px;
    padding: 20px;
    &.box-1 {
      width: 45%;
      margin-left: 40px;
      margin-right: 40px;
      height: 420px;
      .chart-1 {
        height: 345px;
      }
    }
    &.box-2 {
      margin-left: 30px;
      width: 45%;
      height: 420px;
      .chart-2 {
        height: 345px;
      }
    }
    &-title {
      color: #192840;
      font-family: "PingFang SC";
      font-size: 18px;
      font-style: normal;
      font-weight: 600;
      line-height: 28px; /* 155.556% */
      text-align: center;
    }
    &-icon {
      display: flex;
      justify-content: center;
      &-greyed-out-0 {
        opacity: 0.5; /* 置灰效果 */
      }
      &-greyed-out-1 {
        opacity: 0.5; /* 置灰效果 */
      }
      &-greyed-out-2 {
        opacity: 0.5; /* 置灰效果 */
      }
      &-greyed-out-3 {
        opacity: 0.5; /* 置灰效果 */
      }
      &-container {
        display: flex;
        cursor: pointer;
        /*  中间的所有子元素 */
        &:not(:first-child) {
          margin-left: 40px;
        }
        &-inline {
          width: 28px;
          height: 15px;
          border-radius: 5px;
          margin-right: 5px;
        }
        &-text {
          color: #192840;
          font-family: "PingFang SC";
          font-size: 12px;
          font-style: normal;
          line-height: 15px;
          text-align: center;
        }
      }
    }
  }
}
</style>

RadioStatisticController

java 复制代码
package com.xnms.client.service.controller.data.statistic;

import com.xnms.client.service.controller.common.ResponseModel;
import com.xnms.client.service.view.naviagation.page.data.statistice.RadioStatisticUserControl;
import com.xnms.client.service.view.naviagation.page.data.statistice.StatisticSelectUserControl;
import com.xnms.data.contract.MultiResponse;
import com.xnms.data.contract.client.RadioTeleTrafficStatisticTable;
import com.xnms.data.contract.database.db.Pagination;
import com.xnms.data.contract.database.db.QueryConditionBase;
import com.xnms.data.contract.database.db.StatUserTraffic;
import io.swagger.annotations.Api;
import io.swagger.v3.oas.annotations.Operation;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;

@Api(tags = "业务统计 - 终端业务")
@RestController
@RequestMapping("/api/radio_statistic")
public class RadioStatisticController implements InitializingBean {

    private static final Logger logger = LoggerFactory.getLogger(RadioStatisticController.class);

    @Autowired
    private StatisticSelectUserControl statisticSelectUserControl;

    @Autowired
    private RadioStatisticUserControl radioStatisticUserControl;

    @Override
    public void afterPropertiesSet() throws Exception {

    }


    @Operation(summary = "统计列表")
    @PostMapping
    public ResponseModel<List<RadioTeleTrafficStatisticTable>> statisticRadio(@RequestBody QueryConditionBase queryConditionBase) {
        logger.info("RadioStatisticController,statisticRadio:queryConditionBase = [{}]", queryConditionBase);
        String msg = statisticSelectUserControl.verifyStatisticQueryCondition(queryConditionBase, "Query_Operate_Exception");
        if (!StringUtils.isBlank(msg)) {
            return ResponseModel.ofError(msg);
        }
        MultiResponse<RadioTeleTrafficStatisticTable> radioTeleTrafficStatisticTableMultiResponse = radioStatisticUserControl.statisticRadio(queryConditionBase);
        return ResponseModel.ofSuccess(radioTeleTrafficStatisticTableMultiResponse.getData(), Pagination.of(queryConditionBase.getCurrentPage(), queryConditionBase.getPageSize(), radioTeleTrafficStatisticTableMultiResponse.getTotalCount()));
    }


    @Operation(summary = "导出")
    @PostMapping(value = "/excel")
    public ResponseModel<String> exportExcelFile(@RequestBody QueryConditionBase queryConditionBase) {
        logger.info("RadioStatisticController,exportExcelFile:queryConditionBase = [{}]", queryConditionBase);
        String filePath = radioStatisticUserControl.doFile(queryConditionBase, radioStatisticUserControl.exportToExcel(queryConditionBase));
        return ResponseModel.ofSuccess(filePath);
    }

    @Operation(summary = "导出,返回流")
    @PostMapping(value = "/excel_stream")
    public void exportExcelStream(@RequestBody QueryConditionBase queryConditionBase, HttpServletResponse httpServletResponse) throws IOException {
        logger.info("RadioStatisticController,exportExcelStream:queryConditionBase = [{}]", queryConditionBase);
        radioStatisticUserControl.doStream(queryConditionBase, radioStatisticUserControl.exportToExcel(queryConditionBase), httpServletResponse);
    }


    @Operation(summary = "详情")
    @PostMapping("/detail")
    public ResponseModel<List<StatUserTraffic>> statisticRadioDetail(@RequestBody QueryConditionBase queryConditionBase) {
        logger.info("RadioStatisticController,statisticRadioDetail:queryConditionBase = [{}]", queryConditionBase);
        List<StatUserTraffic> radioTeleTrafficStatisticTableMultiResponse = radioStatisticUserControl.statisticRadioDetail(queryConditionBase);
        return ResponseModel.ofSuccess(radioTeleTrafficStatisticTableMultiResponse, Pagination.of(queryConditionBase.getCurrentPage(), queryConditionBase.getPageSize(), radioTeleTrafficStatisticTableMultiResponse.size()));
    }


    @Operation(summary = "详情分业务统计次数")
    @PostMapping("/detail_count")
    public ResponseModel<Map<String, Map<Integer, Long>>> statisticRadioDetailCountByDayBizType(@RequestBody QueryConditionBase queryConditionBase) {
        logger.info("RadioStatisticController,statisticRadioDetail:queryConditionBase = [{}]", queryConditionBase);
        List<StatUserTraffic> radioTeleTrafficStatisticTableMultiResponse = radioStatisticUserControl.statisticRadioDetail(queryConditionBase);
        Map<String, Map<Integer, Long>> stringMapMap = radioStatisticUserControl.calculateCountByBizType(radioTeleTrafficStatisticTableMultiResponse,queryConditionBase);
        return ResponseModel.ofSuccess(stringMapMap, Pagination.of(queryConditionBase.getCurrentPage(), queryConditionBase.getPageSize(), radioTeleTrafficStatisticTableMultiResponse.size()));
    }

}

StacUserTrafficDaoImpl

java 复制代码
package com.xnms.data.service.dao.mysql.impl;

import com.xnms.data.contract.client.RadioTeleTrafficStatisticTable;
import com.xnms.data.contract.database.db.QueryConditionBase;
import com.xnms.data.contract.database.db.StatUserTraffic;
import com.xnms.data.service.dao.StacUserTrafficDao;
import com.xnms.data.service.entity.StacUserTrafficEntity;
import com.xnms.data.service.util.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Repository
public class StacUserTrafficDaoImpl implements StacUserTrafficDao {


    private static final Logger logger = LoggerFactory.getLogger(StacUserTrafficDaoImpl.class);

    private static final SimpleDateFormat yyyyMMdd = new SimpleDateFormat("yyyy-MM-dd");

    private static final SimpleDateFormat YMDHMS = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @PersistenceContext
    private EntityManager entityManager;

    private String getSql(String sqlBuilder) {
        String sql = sqlBuilder;
        if (sql.endsWith(";")) {
            sql = sql.substring(0, sql.length() - 1);
        }
        sql = "SELECT UUID() as uuid, rbres.* from ( " + sql + " ) rbres";
        return sql;
    }

    @Override
    public List<StacUserTrafficEntity> getStacUserTrafficEntityList(Date fromTime, Date endTime) {
        List<StacUserTrafficEntity> stacUserTrafficEntityList = new ArrayList<>();
        String sql = "SELECT * FROM stac_usertraffic_day sp WHERE sp.Date >= :fromTime AND sp.Date <= :endTime";
        try {
            Query query = entityManager.createNativeQuery(getSql(sql), StacUserTrafficEntity.class);
            query.setParameter("fromTime", yyyyMMdd.format(fromTime));
            query.setParameter("endTime", yyyyMMdd.format(endTime));

            List<StacUserTrafficEntity> resultList = query.getResultList();
            stacUserTrafficEntityList.addAll(resultList);
        } catch (Exception ex) {
            // 日志记录
            logger.error("<StacUserTrafficDaoImpl getStacUserTrafficEntityList> Error: {}, SQL: {}", ex.getMessage(), sql, ex);
        }

        return stacUserTrafficEntityList;
    }

    @Override
    public List<StacUserTrafficEntity> getStacUserTrafficEntityList() {
        return null;
    }

    @Override
    public void insertOrUpdateStacUserTrafficEntityList(List<String> sqlList) {

    }

    // 计算日期方法
    private Date getDate(int daysOffset) {
        return new Date(System.currentTimeMillis() + (daysOffset * 24L * 60L * 60L * 1000L));
    }

    @Transactional
    @Override
    public Boolean doStacUserTraffic() {
        List<String> sqlList = new ArrayList<>();

        try {
            for (int i = 0; i < 2; i++) {
                String date = yyyyMMdd.format(getDate(i - 1));
                String tableName = DateUtil.convertStartTimeToTableName(getDate(i - 1));

                // DELETE SQL
                String deleteSql = String.format("DELETE FROM stac_usertraffic_day WHERE date='%s';", date);
                sqlList.add(deleteSql);

                // Calling Insert SQL
                String callingInsertSql = String.format(
                        "INSERT INTO stac_usertraffic_day (radioId, date, count, durationTime, biztype, calling, groupId, calltype) " +
                                "SELECT senderid AS radioId, " +
                                "DATE_FORMAT(starttime, '%%Y-%%m-%%d') AS date, " +
                                "COUNT(*) AS count, " +
                                "SUM(duration) AS durationTime, " +
                                "type AS bizType, " +
                                "1 AS calling, -1 AS groupId, " +
                                "calltype AS calltype " +
                                "FROM %s " +
                                "WHERE (status = 0 OR status = 6 OR status = 11) " +
                                "AND DATE_FORMAT(starttime, '%%Y-%%m-%%d') = '%s' " +
                                "GROUP BY senderid, DATE_FORMAT(starttime, '%%Y-%%m-%%d'), type, calltype;",
                        tableName, date
                );
                sqlList.add(callingInsertSql);

                // Called Insert SQL
                String calledInsertSql = String.format(
                        "INSERT INTO stac_usertraffic_day (radioId, date, count, durationTime, biztype, calling, groupId, calltype) " +
                                "SELECT " +
                                "(CASE WHEN calltype IN (1, 2, 3, 5, 7, 12) THEN -1 ELSE targetid END) AS radioId, " +
                                "DATE_FORMAT(starttime, '%%Y-%%m-%%d') AS date, " +
                                "COUNT(*) AS count, " +
                                "SUM(duration) AS durationTime, " +
                                "type AS bizType, " +
                                "0 AS calling, " +
                                "(CASE WHEN calltype IN (1, 2, 3, 5, 7, 12) THEN targetid ELSE -1 END) AS groupId, " +
                                "calltype AS calltype " +
                                "FROM %s " +
                                "WHERE (status = 0 OR status = 6 OR status = 11) " +
                                "AND (type != 3 AND type != 4) " +
                                "AND DATE_FORMAT(starttime, '%%Y-%%m-%%d') = '%s' " +
                                "GROUP BY targetid, DATE_FORMAT(starttime, '%%Y-%%m-%%d'), type, calltype;",
                        tableName, date
                );
                sqlList.add(calledInsertSql);
            }

            // 批量执行 SQL
            if (!sqlList.isEmpty()) {
                for (String sql : sqlList) {
                    Query query = entityManager.createNativeQuery(sql);
                    query.executeUpdate();
                }
            }

            return true;
        } catch (Exception ex) {
            // 错误日志记录
            logger.error("StacUserTrafficDaoImpl类,doStacUserTraffic方法:SQL:{}", sqlList.toString(), ex);
            return false;
        }
    }

    @Override
    public List<StatUserTraffic> getRadioIdFromPrivateCallStatistics() {
        return null;
    }

    @Override
    public List<StatUserTraffic> obtainRadioTeleTrafficDataDetail(QueryConditionBase queryCondition) {
        List<StatUserTraffic> radioTeleTrafficStatisticRecords = new ArrayList<>();

        // 检查查询条件是否有效
        if (queryCondition == null || queryCondition.getStartTime() == null || queryCondition.getEndTime() == null ||
                queryCondition.getBusinessTypes() == null || queryCondition.getBusinessTypes().isEmpty() ||
                queryCondition.getRadioIDs() == null || queryCondition.getRadioIDs().isEmpty()) {
            logger.debug("[StacUserTrafficDao] obtainRadioTeleTrafficDataDetail: QueryCondition Invalid.");
            return radioTeleTrafficStatisticRecords;
        }

        // 处理业务类型(如果业务类型是 5,则加上 6 和 7)
        List<Integer> bizTypes = new ArrayList<>();
        for (Integer businessType : queryCondition.getBusinessTypes()) {
            if (businessType != 5) {
                bizTypes.add(businessType);
            } else {
                bizTypes.add(5);
                bizTypes.add(6);
                bizTypes.add(7);
            }
        }

        // 构建 SQL 查询语句
        try {
            String sql = "SELECT radioId, date, count, durationTime durationtime, biztype, calling " +
                    "FROM stac_usertraffic_day " +
                    "WHERE biztype IN (:bizTypes) " +
                    "AND radioId IN (:radioIds) " +
                    "AND date >= :startDate " +
                    "AND date <= :endDate " +
                    "AND radioId > 0";

            // 创建原生 SQL 查询并设置参数
            Query query = entityManager.createNativeQuery(getSql(sql), StatUserTraffic.class);
            query.setParameter("bizTypes", bizTypes);
            query.setParameter("radioIds", queryCondition.getRadioIDs());
            query.setParameter("startDate", yyyyMMdd.format(queryCondition.getStartTime()));
            query.setParameter("endDate", yyyyMMdd.format(queryCondition.getEndTime()));

            // 执行查询
            List<StatUserTraffic> resultList = query.getResultList();

            // 将查询结果加入到返回列表中
            if (resultList != null) {
                radioTeleTrafficStatisticRecords.addAll(resultList);
            }

        } catch (Exception ex) {
            logger.error("[StacUserTrafficDao] obtainRadioTeleTrafficDataDetail: " + ex.getMessage(), ex);
        }

        return radioTeleTrafficStatisticRecords;
    }

    @Override
    public int obtainRadioTeleTrafficCount(QueryConditionBase queryCondition) {
        int totalCount = 0;

        // Validate the input
        if (queryCondition == null || queryCondition.getStartTime() == null || queryCondition.getEndTime() == null || queryCondition.getBusinessTypes() == null || queryCondition.getBusinessTypes().isEmpty()) {
            logger.debug("[StacUserTrafficDaoImpl] obtainRadioTeleTrafficCount : QueryCondition Invalid.");
            return totalCount;
        }

        List<Integer> bizTypes = new ArrayList<>();
        for (Integer businessType : queryCondition.getBusinessTypes()) {
            if (businessType != 5) {
                bizTypes.add(businessType);
            } else {
                bizTypes.add(5);
                bizTypes.add(6);
                bizTypes.add(7);
            }
        }
        try {
            String startDateStr = yyyyMMdd.format(queryCondition.getStartTime());
            String endDateStr = yyyyMMdd.format(queryCondition.getEndTime());

            // Construct the SQL query
            String dmlSql = "SELECT count(1) cnt FROM " +
                    "( SELECT radioId FROM stac_usertraffic_day " +
                    "WHERE biztype IN (:bizTypes) AND radioid > 0 " +
                    "AND `DATE` >= :startDate AND `DATE` <= :endDate " +
                    "GROUP BY radioId ) a";

            // Create the native query with parameters
            Query query = entityManager.createNativeQuery(dmlSql);
            query.setParameter("bizTypes", bizTypes);
            query.setParameter("startDate", startDateStr);
            query.setParameter("endDate", endDateStr);

            // Execute the query and retrieve the result
            Object returnValue = query.getSingleResult();
            if (returnValue != null) {
                totalCount = Integer.parseInt(returnValue.toString());
            }
        } catch (Exception ex) {
            logger.error("[StacUserTrafficDaoImpl] obtainRadioTeleTrafficCount : " + ex.getMessage(), ex);
        }

        return totalCount;
    }

    @Override
    public List<RadioTeleTrafficStatisticTable> obtainRadioTeleTrafficData(QueryConditionBase queryCondition, int pageIndex, int pageSize) {
        List<RadioTeleTrafficStatisticTable> radioTeleTrafficStatisticRecords = new ArrayList<>();

        // Validate input
        if (queryCondition == null || queryCondition.getStartTime() == null || queryCondition.getEndTime() == null || queryCondition.getBusinessTypes() == null || queryCondition.getBusinessTypes().isEmpty()) {
            logger.debug("[StacUserTrafficDaoImpl] obtainRadioTeleTrafficData: QueryCondition Invalid.");
            return radioTeleTrafficStatisticRecords;
        }

        List<Integer> bizTypes = new ArrayList<>();
        for (Integer businessType : queryCondition.getBusinessTypes()) {
            if (businessType != 5) {
                bizTypes.add(businessType);
            } else {
                bizTypes.add(5);
                bizTypes.add(6);
                bizTypes.add(7);
            }
        }

        try {
            // Calculate offset
            int offset = (pageIndex - 1) * pageSize;
            ;
            // Prepare date format
            String startDateStr = yyyyMMdd.format(queryCondition.getStartTime());
            String endDateStr = yyyyMMdd.format(queryCondition.getEndTime());

            // Construct SQL query
            String dmlSql = "SELECT IFNULL(voice_duration_time / voice_count, 0) voice_average_time, a.* FROM (" +
                    "SELECT radioId radio_id, SUM(durationTime) totaltime, " +
                    "SUM(CASE biztype WHEN '1' THEN count ELSE 0 END) voice_count, " +
                    "SUM(CASE biztype WHEN '1' THEN durationTime ELSE 0 END) voice_duration_time, " +
                    "SUM(CASE biztype WHEN '2' THEN count ELSE 0 END) message_count, " +
                    "SUM(CASE biztype WHEN '2' THEN durationTime ELSE 0 END) message_duration_time, " +
                    "SUM(CASE biztype WHEN '3' THEN count ELSE 0 END) gps_count, " +
                    "SUM(CASE biztype WHEN '3' THEN durationTime ELSE 0 END) gps_duration_time, " +
                    "SUM(CASE biztype WHEN '4' THEN count ELSE 0 END) register_count, " +
                    "SUM(CASE biztype WHEN '4' THEN durationTime ELSE 0 END) register_duration_time, " +
                    "SUM(CASE WHEN biztype IN ('5', '6', '7') THEN count ELSE 0 END) other_count, " +
                    "SUM(CASE WHEN biztype IN ('5', '6', '7') THEN durationTime ELSE 0 END) other_duration_time " +
                    "FROM stac_usertraffic_day " +
                    "WHERE biztype IN (:bizTypes) AND radioid > 0 AND `DATE` >= :startDate AND `DATE` <= :endDate " +
                    "GROUP BY radioId) a " +
                    "ORDER BY a.totaltime DESC " +
                    "LIMIT :offset, :size";

            // Create native query with parameters
            Query query = entityManager.createNativeQuery(getSql(dmlSql), RadioTeleTrafficStatisticTable.class);
            query.setParameter("bizTypes", bizTypes);
            query.setParameter("startDate", startDateStr);
            query.setParameter("endDate", endDateStr);
            query.setParameter("offset", offset);
            query.setParameter("size", pageSize);

            // Execute query and get result
            radioTeleTrafficStatisticRecords = query.getResultList();
            BigDecimal terminalsTotalTime = obtainRadioTeleTrafficTotalTime(queryCondition);
            int startIndex = (pageIndex - 1) * pageSize;
            int i = 0;
            for (RadioTeleTrafficStatisticTable item : radioTeleTrafficStatisticRecords) {
                item.setIndex(startIndex + i + 1);
                item.setProportion(item.getTotalTime().divide(terminalsTotalTime, 4, BigDecimal.ROUND_HALF_UP));
                i++;
            }

        } catch (Exception ex) {
            logger.error("[StacUserTrafficDaoImpl] obtainRadioTeleTrafficData: " + ex.getMessage(), ex);
        }

        return radioTeleTrafficStatisticRecords;
    }

    public BigDecimal obtainRadioTeleTrafficTotalTime(QueryConditionBase queryCondition) {
        BigDecimal totalTime = BigDecimal.valueOf(Long.MAX_VALUE);  // 默认返回最大值

        // 参数校验
        if (queryCondition == null || queryCondition.getStartTime() == null || queryCondition.getEndTime() == null
                || queryCondition.getBusinessTypes() == null || queryCondition.getBusinessTypes().isEmpty()) {
            logger.debug("[StacUserTrafficDaoImpl] obtainRadioTeleTrafficTotalTime: QueryCondition Invalid.");
            return totalTime;
        }

        try {
            String startDateStr = yyyyMMdd.format(queryCondition.getStartTime());
            String endDateStr = yyyyMMdd.format(queryCondition.getEndTime());

            // 构建 SQL 查询语句
            String sql = "SELECT IFNULL(SUM(durationTime), 0) AS totalTime FROM stac_usertraffic_day " +
                    "WHERE biztype IN (:bizTypes) AND radioid > 0 AND DATE >= :startDate AND DATE <= :endDate";

            // 创建查询对象
            Query query = entityManager.createNativeQuery(sql);
            query.setParameter("bizTypes", queryCondition.getBusinessTypes());
            query.setParameter("startDate", startDateStr);
            query.setParameter("endDate", endDateStr);

            // 执行查询并获取结果
            Object result = query.getSingleResult();

            // 获取查询结果中的 totalTime
            if (result != null) {
                totalTime = new BigDecimal(result.toString());
            }

            // 如果 totalTime 为零,设置为最大值
            if (totalTime.compareTo(BigDecimal.ZERO) == 0) {
                totalTime = BigDecimal.valueOf(Long.MAX_VALUE);
            }
        } catch (Exception ex) {
            logger.error("[StacUserTrafficDaoImpl] obtainRadioTeleTrafficTotalTime: " + ex.getMessage(), ex);
        }

        return totalTime;
    }

    @Transactional
    @Override
    public Boolean doStacUserTraffic(Date stacDate, Date todayStart, Date tomorrowStart) {
        List<String> sqlList = new ArrayList<>();

        try {
            String date = yyyyMMdd.format(stacDate);
            String tableName = DateUtil.convertStartTimeToTableName(stacDate);

            // DELETE SQL
            String deleteSql = String.format("DELETE FROM stac_usertraffic_day WHERE date='%s';", date);
            sqlList.add(deleteSql);

            // Calling Insert SQL
            String callingInsertSql = String.format(
                    "INSERT INTO stac_usertraffic_day (radioId, date, count, durationTime, biztype, calling, groupId, calltype) " +
                            "SELECT senderid AS radioId, " +
                            "DATE_FORMAT(starttime, '%%Y-%%m-%%d') AS date, " +
                            "COUNT(*) AS count, " +
                            "SUM(duration) AS durationTime, " +
                            "type AS bizType, " +
                            "1 AS calling, -1 AS groupId, " +
                            "calltype AS calltype " +
                            "FROM %s " +
                            "WHERE status in (0,6,11) " +
                            "AND STARTTIME between '%s' and '%s' " +
                            "GROUP BY senderid, date, type, calltype;",
                    tableName, YMDHMS.format(todayStart), YMDHMS.format(tomorrowStart)
            );
            sqlList.add(callingInsertSql);

            // Called Insert SQL
            String calledInsertSql = String.format(
                    "INSERT INTO stac_usertraffic_day (radioId, date, count, durationTime, biztype, calling, groupId, calltype) " +
                            "SELECT " +
                            "(CASE WHEN calltype IN (1, 2, 3, 5, 7, 12) THEN -1 ELSE targetid END) AS radioId, " +
                            "DATE_FORMAT(starttime, '%%Y-%%m-%%d') AS date, " +
                            "COUNT(*) AS count, " +
                            "SUM(duration) AS durationTime, " +
                            "type AS bizType, " +
                            "0 AS calling, " +
                            "(CASE WHEN calltype IN (1, 2, 3, 5, 7, 12) THEN targetid ELSE -1 END) AS groupId, " +
                            "calltype AS calltype " +
                            "FROM %s " +
                            "WHERE status in (0,6,11) " +
                            "AND type not in (3,4) " +
                            "AND STARTTIME between '%s' and '%s' " +
                            "GROUP BY targetid, date, type, calltype;",
                    tableName, YMDHMS.format(todayStart), YMDHMS.format(tomorrowStart)
            );
            sqlList.add(calledInsertSql);

            // 批量执行 SQL
            if (!sqlList.isEmpty()) {
                for (String sql : sqlList) {
                    Query query = entityManager.createNativeQuery(sql);
                    query.executeUpdate();
                }
            }

            return true;
        } catch (Exception ex) {
            // 错误日志记录
            logger.error("StacUserTrafficDaoImpl类,doStacUserTraffic方法:SQL:{}",sqlList.toString(),ex);
            return false;
        }
    }
}

四、说明

五、本人其他相关文章链接

相关推荐
刘大猫.3 天前
XNMS项目-业务统计-中转业务
业务统计·中转业务·中转个呼·中转组呼·终端业务·终端组业务·自定义报表
刘大猫.22 天前
XNMS项目-mysql数据库同步
数据库·统计·同步·数据同步·数据统计·数据库同步·业务统计