From b7b10f78abc7ba462e5c602ba7e70d4739e316b8 Mon Sep 17 00:00:00 2001
From: baoshiwei <baoshiwei@shlanbao.cn>
Date: 星期二, 24 六月 2025 09:00:39 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/main'

---
 eims-ui/apps/web-antd/src/api/eims/report/index.ts                                            |   28 +
 eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/ReportReqBo.java          |   19 +
 eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/ReportController.java    |   50 ++
 eims-ui/apps/web-antd/src/views/eims/insp-report/data.tsx                                     |   53 ++
 eims-ui/apps/web-antd/src/views/eims/maint-report/data.tsx                                    |   46 ++
 eims-ui/apps/web-antd/src/api/eims/report/model.d.ts                                          |   97 +++++
 eims-ui/apps/web-antd/src/views/eims/maint-report/index.vue                                   |  213 +++++++++++
 eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/ReportServiceImpl.java |  310 ++++++++++++++++
 eims-ui/apps/web-antd/src/views/eims/insp-report/index.vue                                    |  235 ++++++++++++
 eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IReportService.java         |   33 +
 eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsInspectReportVo.java  |   38 ++
 11 files changed, 1,122 insertions(+), 0 deletions(-)

diff --git a/eims-ui/apps/web-antd/src/api/eims/report/index.ts b/eims-ui/apps/web-antd/src/api/eims/report/index.ts
new file mode 100644
index 0000000..84ae85c
--- /dev/null
+++ b/eims-ui/apps/web-antd/src/api/eims/report/index.ts
@@ -0,0 +1,28 @@
+import { requestClient } from '#/api/request';
+
+enum Api {
+  insp = '/eims/report/insp',
+  maint = '/eims/report/maint',
+}
+
+/**
+ * 鏌ヨ鐐规璁板綍鍒楄〃
+ * @param query
+ * @returns {*}
+ */
+
+export function insp(params?: any) {
+  return requestClient.get<any[]>(Api.insp, { params });
+}
+
+
+/**
+ * 鏌ヨ淇濆吇璁板綍鍒楄〃
+ * @param query
+ * @returns {*}
+ */
+
+export function maint(params?: any) {
+  return requestClient.get<any[]>(Api.maint, { params });
+}
+
diff --git a/eims-ui/apps/web-antd/src/api/eims/report/model.d.ts b/eims-ui/apps/web-antd/src/api/eims/report/model.d.ts
new file mode 100644
index 0000000..688c76f
--- /dev/null
+++ b/eims-ui/apps/web-antd/src/api/eims/report/model.d.ts
@@ -0,0 +1,97 @@
+export interface InspectRecordVO {
+  /**
+   * id
+   */
+  id: string | number;
+
+  /**
+   * 璁惧di
+   */
+  equId: string | number;
+  /**
+   * 璁惧鍚嶇О
+
+   */
+  equName: string;
+  /**
+   * 璧勪骇缂栧彿
+
+   */
+  assteNo: string;
+
+  /**
+   * 鐐规鍚嶇О
+   */
+  inspName: string;
+
+  /**
+   * 鐐规鎻忚堪
+   */
+  inspDesc: string;
+
+  /**
+   * 鐘舵��
+   */
+  status: string;
+
+  /**
+   * 鐐规缂栫爜
+   */
+  inspCode: string;
+
+  /**
+   * 鍊艰褰曟柟寮忥紙瀛楀吀锛�
+   */
+  recordMode: string;
+
+  /**
+   * 鍙傝�冨��
+   */
+  referenceValue: string;
+
+  /**
+   * 涓婇檺
+   */
+  upperLimit: string;
+
+  /**
+   * 涓嬮檺
+   */
+  lowLimit: string;
+
+  /**
+   * 妫�鏌ュ��
+   */
+  checkValue: string;
+
+  /**
+   * 鐐规缁撴灉锛堝瓧鍏革級
+   */
+  inspResult: string;
+
+  /**
+   * 鐐规鏃堕棿
+   */
+  inspTime: string;
+
+  /**
+   * 璁″垝鐐规鏃ユ湡
+   */
+  planTime: string;
+
+  /**
+   * 楠岃瘉浜�
+   */
+  verifyUser: number;
+
+  /**
+   * 璁″垝id
+   */
+  planId: string | number;
+
+  /**
+   * 澶囨敞
+   */
+  remark: string;
+
+}
diff --git a/eims-ui/apps/web-antd/src/views/eims/insp-report/data.tsx b/eims-ui/apps/web-antd/src/views/eims/insp-report/data.tsx
new file mode 100644
index 0000000..64b9585
--- /dev/null
+++ b/eims-ui/apps/web-antd/src/views/eims/insp-report/data.tsx
@@ -0,0 +1,53 @@
+import type { VxeGridProps } from '#/adapter/vxe-table';
+
+import { ref } from 'vue';
+
+import dayjs from 'dayjs';
+
+import { type FormSchemaGetter } from '#/adapter/form';
+
+export const querySchema: FormSchemaGetter = () => [
+  /*  {
+    component: 'Select',
+    componentProps: {
+      showSearch: true,
+      allowClear: true,
+      getPopupContainer
+    },
+    fieldName: 'equId',
+    label: '璁惧鍚嶇О'
+  },*/
+  {
+    component: 'Input',
+    fieldName: 'keyword',
+    componentProps: {
+      placeholder: '璇疯緭鍏ヨ澶囧悕绉版垨璧勪骇缂栧彿'
+    },
+    label: '鍏抽敭瀛�'
+  },
+  {
+    component: 'DatePicker',
+    componentProps: {
+      picker: 'month',
+      format: 'YYYY-MM',
+      valueFormat: 'YYYY-MM',
+      placeholder: '璇烽�夋嫨鏈堜唤',
+      allowClear: true,
+      defaultValue: dayjs().format('YYYY-MM'),
+      getPopupContainer: (triggerNode: HTMLElement) => triggerNode.parentNode as HTMLElement
+    },
+    fieldName: 'selectMonth',
+    defaultValue: dayjs().format('YYYY-MM'),
+    label: '鏈堜唤閫夋嫨'
+  }
+];
+
+export const columns = ref<VxeGridProps['columns']>([
+  { type: 'checkbox', width: 60, fixed: 'left' },
+  {
+    title: '璁惧鍚嶇О',
+    field: 'equName',
+    minWidth: 200,
+    fixed: 'left'
+  }
+]);
diff --git a/eims-ui/apps/web-antd/src/views/eims/insp-report/index.vue b/eims-ui/apps/web-antd/src/views/eims/insp-report/index.vue
new file mode 100644
index 0000000..bc56f0e
--- /dev/null
+++ b/eims-ui/apps/web-antd/src/views/eims/insp-report/index.vue
@@ -0,0 +1,235 @@
+<script setup lang="ts">
+import { h, onMounted, ref, toRaw } from 'vue';
+
+import { Page, type VbenFormProps } from '@vben/common-ui';
+
+import { Space } from 'ant-design-vue';
+import dayjs from 'dayjs';
+
+import { useVbenVxeGrid, type VxeGridProps, vxeSortEvent } from '#/adapter/vxe-table';
+import { insp } from '#/api/eims/report';
+
+import { columns as dynamicColumns, querySchema } from './data';
+// 鏁版嵁鏄惁鍒濆鍖�
+const initFlag = ref(false);
+const baseColumns: VxeGridProps['columns'] = [
+  {
+    title: '璁惧鍚嶇О',
+    field: 'equName',
+    minWidth: 120,
+    fixed: 'left'
+  },
+  {
+    title: '璧勪骇缂栧彿 - 鏃ユ湡',
+    field: 'assetNo',
+    minWidth: 120,
+    fixed: 'left'
+  }
+];
+initColumns();
+
+const formOptions: VbenFormProps = {
+  commonConfig: {
+    labelWidth: 80,
+    componentProps: {
+      allowClear: true
+    }
+  },
+  schema: querySchema(),
+  wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
+  handleReset: async () => {
+    // eslint-disable-next-line no-use-before-define
+    const { formApi, reload } = tableApi;
+    await formApi.resetForm();
+    const formValues = formApi.form.values;
+    formApi.setLatestSubmissionValues(formValues);
+    await reload(formValues);
+    initFlag.value = false;
+    initColumns(dayjs().format('YYYY-MM'), true);
+  },
+  // 鏃ユ湡閫夋嫨鏍煎紡鍖�
+  fieldMappingTime: [['changeDate', ['params[beginTime]', 'params[endTime]'], ['YYYY-MM-DD 00:00:00', 'YYYY-MM-DD 23:59:59']]]
+};
+
+const gridOptions: VxeGridProps = {
+  checkboxConfig: {
+    // 楂樹寒
+    highlight: true,
+    // 缈婚〉鏃朵繚鐣欓�変腑鐘舵��
+    reserve: true
+    // 鐐瑰嚮琛岄�変腑
+    // trigger: 'row'
+  },
+  border: true,
+  size: 'mini',
+  columns: dynamicColumns.value,
+  height: 'auto',
+  keepSource: true,
+  pagerConfig: {
+    // 榛樿鏉℃暟
+    pageSize: 20,
+    // 鍒嗛〉鍙�夋潯鏁�
+    pageSizes: [10, 20, 30, 50, 100, 200, 300, 500]
+  },
+  proxyConfig: {
+    ajax: {
+      query: async ({ page }, formValues = {}) => {
+        // TODO 鏆傛椂瑙e喅鍔ㄦ�佷慨鏀硅〃澶村悗鍙傛暟涓㈠け闂
+        // eslint-disable-next-line no-use-before-define
+        const p = await tableApi.formApi.getValues();
+        const params = toRaw(p);
+
+        return await insp({
+          pageNum: page.currentPage,
+          pageSize: page.pageSize,
+          ...params
+        });;
+      }
+    }
+  },
+  rowConfig: {
+    isHover: true,
+    keyField: 'inspReport'
+  },
+  sortConfig: {
+    // 杩滅▼鎺掑簭
+    remote: true,
+    // 鏀寔澶氬瓧娈垫帓搴� 榛樿鍏抽棴
+    multiple: true
+  },
+  id: 'eims-report-index'
+};
+
+const [BasicTable, tableApi] = useVbenVxeGrid({
+  formOptions,
+  gridOptions,
+  gridEvents: {
+    sortChange: (sortParams) => vxeSortEvent(tableApi, sortParams)
+  }
+});
+
+onMounted(async () => {
+  initListener();
+  // await setupEquSelect();
+});
+const selMonth = ref('');
+
+function initListener() {
+  tableApi.formApi.updateSchema([
+    {
+      component: 'DatePicker',
+      componentProps: {
+        onChange: (sMonth: string) => {
+          selMonth.value = sMonth;
+          dynamicColumns.value = [];
+          initFlag.value = false;
+          initColumns(sMonth, true);
+        }
+      },
+      fieldName: 'selectMonth'
+    }
+  ]);
+}
+
+function initColumns(selectMonth: string = dayjs().format('YYYY-MM'), isUpdate: boolean = false) {
+  if (initFlag.value) {
+    return false;
+  }
+  const dayNumbers = getDayNumbers(selectMonth);
+  const newColumns: VxeGridProps['columns'] = dayNumbers.map((day: number) => ({
+    title: `${day}`,
+    field: `day_${day}`,
+    width: 40,
+    align: 'center',
+    slots: {
+      default: ({ row }) => {
+        const value = row[`day_${day}`];
+        let className = '';
+
+        switch (value) {
+          case '宸插畬鎴�': {
+            className = 'dot-green';
+            break;
+          }
+          case '寰呴獙璇�': {
+            className = 'dot-orange';
+            break;
+          }
+          case '鏈偣妫�': {
+            className = 'dot-gray';
+            break;
+          }
+          default: {
+            return '-';
+          }
+        }
+
+        return h('span', { class: className });
+      }
+    }
+    // formatter: ({ row }: { row: any }) => {
+    //   return row[`day_${day}`] || '-';
+    // }
+  }));
+  dynamicColumns.value = [...(baseColumns || []), ...newColumns];
+  initFlag.value = true;
+  if (isUpdate) {
+    tableApi.setGridOptions({
+      ...gridOptions,
+      columns: dynamicColumns.value
+    });
+    tableApi.query();
+  }
+}
+
+function getDayNumbers(yearMonth: string): number[] {
+  const days = dayjs(yearMonth).daysInMonth();
+  return Array.from({ length: days }).map((_, i) => i + 1);
+}
+</script>
+
+<template>
+  <Page :auto-content-height="true">
+    <div class="flex h-full gap-[8px]">
+      <BasicTable class="flex-1 overflow-hidden" table-title="璇曚骇鍒楄〃">
+        <template #toolbar-tools>
+          <Space>
+            <span class="ml-4 mr-2">-</span>鏈敓鎴愯鍒� <span class="dot-gray ml-4"></span>鏈偣妫� <span class="dot-orange ml-4"></span>寰呴獙璇�
+            <span class="dot-green ml-4"></span>宸茬偣妫�
+          </Space>
+        </template>
+
+        <template #insp="{ row }">
+          <Space>
+            <span>{{ row.equName }}</span>
+          </Space>
+        </template>
+      </BasicTable>
+    </div>
+  </Page>
+</template>
+<style lang="less" scoped>
+:deep(.dot-green) {
+  display: inline-block;
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  background-color: #52c41a;
+}
+
+:deep(.dot-orange) {
+  display: inline-block;
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  background-color: #faad14;
+}
+
+:deep(.dot-gray) {
+  display: inline-block;
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  background-color: #d9d9d9;
+}
+</style>
diff --git a/eims-ui/apps/web-antd/src/views/eims/maint-report/data.tsx b/eims-ui/apps/web-antd/src/views/eims/maint-report/data.tsx
new file mode 100644
index 0000000..af86655
--- /dev/null
+++ b/eims-ui/apps/web-antd/src/views/eims/maint-report/data.tsx
@@ -0,0 +1,46 @@
+import type { VxeGridProps } from '#/adapter/vxe-table';
+
+import { ref } from 'vue';
+
+import dayjs from 'dayjs';
+
+import { type FormSchemaGetter } from '#/adapter/form';
+
+export const querySchema: FormSchemaGetter = () => [
+  /*  {
+    component: 'Select',
+    componentProps: {
+      showSearch: true,
+      allowClear: true,
+      getPopupContainer
+    },
+    fieldName: 'equId',
+    label: '璁惧鍚嶇О'
+  },*/
+  {
+    component: 'Input',
+    fieldName: 'keyword',
+    componentProps: {
+      placeholder: '璇疯緭鍏ヨ澶囧悕绉版垨璧勪骇缂栧彿'
+    },
+    label: '鍏抽敭瀛�'
+  },
+  {
+    component: 'DatePicker',
+    componentProps: {
+      picker: 'year',
+      format: 'YYYY',
+      valueFormat: 'YYYY',
+      placeholder: '璇烽�夋嫨鏈堜唤',
+      allowClear: true,
+      defaultValue: dayjs().format('YYYY'),
+      getPopupContainer: (triggerNode: HTMLElement) => triggerNode.parentNode as HTMLElement
+    },
+    fieldName: 'selectYear',
+    defaultValue: dayjs().format('YYYY'),
+    label: '骞翠唤閫夋嫨'
+  }
+];
+
+export const columns = ref<VxeGridProps['columns']>([
+]);
diff --git a/eims-ui/apps/web-antd/src/views/eims/maint-report/index.vue b/eims-ui/apps/web-antd/src/views/eims/maint-report/index.vue
new file mode 100644
index 0000000..ee92d5e
--- /dev/null
+++ b/eims-ui/apps/web-antd/src/views/eims/maint-report/index.vue
@@ -0,0 +1,213 @@
+<script setup lang="ts">
+import { h, onMounted, ref, toRaw } from 'vue';
+
+import { Page, type VbenFormProps } from '@vben/common-ui';
+
+import { Space } from 'ant-design-vue';
+
+import { useVbenVxeGrid, type VxeGridProps, vxeSortEvent } from '#/adapter/vxe-table';
+import { maint } from '#/api/eims/report';
+
+import { columns as dynamicColumns, querySchema } from './data';
+// 鏁版嵁鏄惁鍒濆鍖�
+const initFlag = ref(false);
+const baseColumns: VxeGridProps['columns'] = [
+  { type: 'checkbox', width: 60, fixed: 'left' },
+  {
+    title: '璁惧鍚嶇О',
+    field: 'equName',
+    width: 200,
+    fixed: 'left'
+  },
+  {
+    title: '璧勪骇缂栧彿',
+    field: 'assetNo',
+    width: 200,
+    fixed: 'left'
+  }
+];
+initColumns();
+
+const formOptions: VbenFormProps = {
+  commonConfig: {
+    labelWidth: 80,
+    componentProps: {
+      allowClear: true
+    }
+  },
+  schema: querySchema(),
+  wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
+  handleReset: async () => {
+    // eslint-disable-next-line no-use-before-define
+    const { formApi, reload } = tableApi;
+    await formApi.resetForm();
+    const formValues = formApi.form.values;
+    formApi.setLatestSubmissionValues(formValues);
+    await reload(formValues);
+  }
+};
+
+const gridOptions: VxeGridProps = {
+  checkboxConfig: {
+    // 楂樹寒
+    highlight: true,
+    // 缈婚〉鏃朵繚鐣欓�変腑鐘舵��
+    reserve: true
+    // 鐐瑰嚮琛岄�変腑
+    // trigger: 'row'
+  },
+  border: true,
+  size: 'mini',
+  columns: dynamicColumns.value,
+  height: 'auto',
+  keepSource: true,
+  pagerConfig: {
+    // 榛樿鏉℃暟
+    pageSize: 20,
+    // 鍒嗛〉鍙�夋潯鏁�
+    pageSizes: [10, 20, 30, 50, 100, 200, 300, 500]
+  },
+  proxyConfig: {
+    ajax: {
+      query: async ({ page }, formValues = {}) => {
+        // TODO 鏆傛椂瑙e喅鍔ㄦ�佷慨鏀硅〃澶村悗鍙傛暟涓㈠け闂
+        // eslint-disable-next-line no-use-before-define
+        const p = await tableApi.formApi.getValues();
+        const params = toRaw(p);
+
+        return await maint({
+          pageNum: page.currentPage,
+          pageSize: page.pageSize,
+          ...params
+        });
+      }
+    }
+  },
+  rowConfig: {
+    isHover: true,
+    keyField: 'maintReport'
+  },
+  sortConfig: {
+    // 杩滅▼鎺掑簭
+    remote: true,
+    // 鏀寔澶氬瓧娈垫帓搴� 榛樿鍏抽棴
+    multiple: true
+  },
+  id: 'mainit-report-index'
+};
+
+const [BasicTable, tableApi] = useVbenVxeGrid({
+  formOptions,
+  gridOptions,
+  gridEvents: {
+    sortChange: (sortParams) => vxeSortEvent(tableApi, sortParams)
+  }
+});
+
+onMounted(async () => {
+  initListener();
+  // await setupEquSelect();
+});
+const selYear = ref('');
+
+function initListener() {
+  tableApi.formApi.updateSchema([
+    {
+      component: 'DatePicker',
+      componentProps: {
+        onChange: (sYear: string) => {
+          selYear.value = sYear;
+        }
+      },
+      fieldName: 'selectYear'
+    }
+  ]);
+}
+
+function initColumns() {
+  if (initFlag.value) {
+    return false;
+  }
+  const dayNumbers = getMonthNumbers();
+  const newColumns: VxeGridProps['columns'] = dayNumbers.map((month: number) => ({
+    title: `${month}鏈坄,
+    field: `month_${month}`,
+    align: 'center',
+    slots: {
+      default: ({ row }) => {
+        const value = row[`month_${month}`];
+        let className = '';
+
+        switch (value) {
+          case '宸插畬鎴�': {
+            className = 'dot-green';
+            break;
+          }
+          case '鏈畬鎴�': {
+            className = 'dot-orange';
+            break;
+          }
+          default: {
+            return '-';
+          }
+        }
+
+        return h('span', { class: className });
+      }
+    }
+  }));
+  dynamicColumns.value = [...(baseColumns || []), ...newColumns];
+  initFlag.value = true;
+}
+
+function getMonthNumbers(): number[] {
+  return Array.from({ length: 12 }, (_, i) => i + 1);
+}
+</script>
+
+<template>
+  <Page :auto-content-height="true">
+    <div class="flex h-full gap-[8px]">
+      <BasicTable class="flex-1 overflow-hidden" table-title="璇曚骇鍒楄〃">
+        <template #toolbar-tools>
+          <Space>
+            <span class="ml-4 mr-2">-</span>鏈敓鎴愯鍒� <span class="dot-orange ml-4"></span>鏈畬鎴� <span class="dot-green ml-4"></span>宸插畬鎴�
+          </Space>
+        </template>
+      </BasicTable>
+    </div>
+  </Page>
+</template>
+<style lang="less" scoped>
+:deep(.dot-green) {
+  display: inline-block;
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  background-color: #52c41a;
+}
+
+:deep(.dot-orange) {
+  display: inline-block;
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  background-color: #faad14;
+}
+
+:deep(.dot-blue) {
+  display: inline-block;
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  background-color: #2a5dd8;
+}
+
+:deep(.dot-purple) {
+  display: inline-block;
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  background-color: #8a2ad8;
+}
+</style>
diff --git a/eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/ReportController.java b/eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/ReportController.java
new file mode 100644
index 0000000..7e71d80
--- /dev/null
+++ b/eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/ReportController.java
@@ -0,0 +1,50 @@
+package org.dromara.eims.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.eims.service.IReportService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Map;
+
+/**
+ * 鐐规鎶ヨ〃
+ *
+ * @author zhuguifei
+ * @date
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/eims/report")
+public class ReportController extends BaseController {
+
+    private final IReportService reportService;
+
+    /**
+     * 鏌ヨ鐐规璁板綍鍒楄〃
+     */
+    @SaCheckPermission("eims:inspReport:list")
+    @GetMapping("/insp")
+    public TableDataInfo<Map<String,Object>> list(@RequestParam Map<String, Object> queryParams,  PageQuery pageQuery) {
+        return reportService.queryPageListCustom(queryParams, pageQuery);
+    }
+
+
+    /**
+     * 鏌ヨ淇濆吇璁板綍鍒楄〃
+     */
+    @SaCheckPermission("eims:maintReport:list")
+    @GetMapping("/maint")
+    public TableDataInfo<Map<String,Object>> maint(@RequestParam Map<String, Object> queryParams,  PageQuery pageQuery) {
+        return reportService.queryMaintList(queryParams, pageQuery);
+    }
+
+}
diff --git a/eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/ReportReqBo.java b/eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/ReportReqBo.java
new file mode 100644
index 0000000..1462533
--- /dev/null
+++ b/eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/ReportReqBo.java
@@ -0,0 +1,19 @@
+package org.dromara.eims.domain.bo;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+
+/**
+ * 鐐规鎶ヨ〃璇锋眰鍙傛暟
+ *
+ * @author zhuguifei
+ * @date
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ReportReqBo extends BaseEntity {
+
+
+
+}
diff --git a/eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsInspectReportVo.java b/eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsInspectReportVo.java
new file mode 100644
index 0000000..132ecbb
--- /dev/null
+++ b/eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsInspectReportVo.java
@@ -0,0 +1,38 @@
+package org.dromara.eims.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.translation.annotation.Translation;
+import org.dromara.common.translation.constant.TransConstant;
+import org.dromara.eims.domain.EimsInspectRecord;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 鐐规鎶ヨ〃瑙嗗浘瀵硅薄
+ *
+ * @author zhuguifei
+ * @date
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class EimsInspectReportVo extends BaseEntity implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    private Integer year;
+    private Integer month;
+
+
+}
diff --git a/eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IReportService.java b/eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IReportService.java
new file mode 100644
index 0000000..3036927
--- /dev/null
+++ b/eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IReportService.java
@@ -0,0 +1,33 @@
+package org.dromara.eims.service;
+
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+import java.util.Map;
+
+/**
+ * 鐐规鎶ヨ〃Service鎺ュ彛
+ *
+ * @author zhuguifei
+ * @date
+ */
+public interface IReportService {
+
+
+    /**
+     * 鍒嗛〉鏌ヨ鐐规璁板綍鍒楄〃-澶氳〃鏌ヨ
+     *
+     * @param queryParams        鏌ヨ鏉′欢
+     * @param pageQuery 鍒嗛〉鍙傛暟
+     * @return 鐐规璁板綍鍒嗛〉鍒楄〃
+     */
+    TableDataInfo<Map<String,Object>> queryPageListCustom(Map<String, Object> queryParams, PageQuery pageQuery);
+
+    /**
+     * 鏌ヨ淇濆吇璁板綍
+     * @param queryParams
+     * @param pageQuery
+     * @return
+     */
+    TableDataInfo<Map<String, Object>> queryMaintList(Map<String, Object> queryParams, PageQuery pageQuery);
+}
diff --git a/eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/ReportServiceImpl.java b/eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/ReportServiceImpl.java
new file mode 100644
index 0000000..ce7d9a2
--- /dev/null
+++ b/eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/ReportServiceImpl.java
@@ -0,0 +1,310 @@
+package org.dromara.eims.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.eims.domain.*;
+import org.dromara.eims.domain.vo.EimsEquVo;
+import org.dromara.eims.mapper.*;
+import org.dromara.eims.service.IReportService;
+import org.dromara.system.mapper.SysDeptMapper;
+import org.springframework.stereotype.Service;
+
+import java.time.*;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@RequiredArgsConstructor
+@Service
+public class ReportServiceImpl implements IReportService {
+
+    private static final String UNCHECKED = "鏈偣妫�";
+    private static final String PENDING_VERIFICATION = "寰呴獙璇�";
+    private static final String COMPLETED = "宸插畬鎴�";
+    private static final String NOT_APPLICABLE = "/";
+
+
+    private static final String MAINT_0 = "寰呬繚鍏�";
+    private static final String MAINT_1 = "淇濆吇涓�";
+    private static final String MAINT_2 = "寰呴獙璇�";
+    private static final String MAINT_3 = "宸插畬鎴�";
+
+    private final EimsInspectRecordMapper baseMapper;
+    private final EimsInspectStMapper inspectStMapper;
+    private final EimsInspectPlanMapper inspectPlanMapper;
+    private final SysDeptMapper sysDeptMapper;
+    private final EimsEquMapper equMapper;
+    private final EimsMaintOrderMapper maintOrderMapper;
+    private final EimsMaintPlanMapper maintPlanMapper;
+
+    @Override
+    public TableDataInfo<Map<String, Object>> queryPageListCustom(Map<String, Object> queryParams, PageQuery pageQuery) {
+        LambdaQueryWrapper<EimsEqu> equWrapper = Wrappers.lambdaQuery();
+
+        if (queryParams.containsKey("keyword")) {
+            String keyword = (String) queryParams.get("keyword");
+            equWrapper.and(StringUtils.isNotBlank(keyword),
+                wrapper -> wrapper
+                    .like(EimsEqu::getEquName, keyword)
+                    .or()
+                    .like(EimsEqu::getAssetNo, keyword)
+            );
+        }
+
+        Page<EimsEquVo> equList = equMapper.selectVoPage(pageQuery.build(), equWrapper);
+        return TableDataInfo.build(fitInspData(equList, queryParams));
+    }
+
+    @Override
+    public TableDataInfo<Map<String, Object>> queryMaintList(Map<String, Object> queryParams, PageQuery pageQuery) {
+        LambdaQueryWrapper<EimsEqu> equWrapper = Wrappers.lambdaQuery();
+
+        if (queryParams.containsKey("keyword")) {
+            String keyword = (String) queryParams.get("keyword");
+            equWrapper.and(StringUtils.isNotBlank(keyword),
+                wrapper -> wrapper
+                    .like(EimsEqu::getEquName, keyword)
+                    .or()
+                    .like(EimsEqu::getAssetNo, keyword)
+            );
+        }
+
+        Page<EimsEquVo> equList = equMapper.selectVoPage(pageQuery.build(), equWrapper);
+        return TableDataInfo.build(fitMaintData(equList, queryParams));
+    }
+
+    private  Page<Map<String, Object>> fitMaintData(Page<EimsEquVo> equPageList, Map<String, Object> queryParams) {
+        Integer selectYear = getYearFromParams(queryParams);
+        LocalDate firstDay = Year.of(selectYear).atDay(1); // 绗竴澶�
+        LocalDate lastDay = Year.of(selectYear).atDay(Year.of(selectYear).length());
+        Page<Map<String, Object>> page = new Page<>(
+            equPageList.getCurrent(),
+            equPageList.getSize(),
+            equPageList.getTotal(),
+            equPageList.searchCount()
+        );
+        if (equPageList.getRecords().isEmpty()) {
+            return page;
+        }
+
+        List<Long> equIds = equPageList.getRecords().stream()
+            .map(EimsEquVo::getEquId)
+            .collect(Collectors.toList());
+
+        Map<Long, List<EimsMaintOrder>> maintOrderMap = getMaintOrderMap(equIds, firstDay, lastDay);
+        Map<Long, List<EimsMaintPlan>> maintPlanMap = getMaintPlanMap(equIds,selectYear);
+
+        List<Map<String, Object>> resultList = equPageList.getRecords().stream()
+            .map(equ -> buildMaintReport(equ, maintOrderMap, maintPlanMap))
+            .collect(Collectors.toList());
+
+        page.setRecords(resultList);
+
+        return page;
+    }
+
+    private Page<Map<String, Object>> fitInspData(Page<EimsEquVo> equPageList, Map<String, Object> queryParams) {
+        YearMonth yearMonth = getYearMonthFromParams(queryParams);
+        LocalDate firstDay = yearMonth.atDay(1);
+        LocalDate lastDay = yearMonth.atEndOfMonth();
+
+        Page<Map<String, Object>> page = new Page<>(
+            equPageList.getCurrent(),
+            equPageList.getSize(),
+            equPageList.getTotal(),
+            equPageList.searchCount()
+        );
+
+        if (equPageList.getRecords().isEmpty()) {
+            return page;
+        }
+
+        List<Long> equIds = equPageList.getRecords().stream()
+            .map(EimsEquVo::getEquId)
+            .collect(Collectors.toList());
+
+        Map<Long, List<EimsInspectSt>> inspectStMap = getInspectStMap(equIds, firstDay, lastDay);
+        Map<Long, List<EimsInspectPlan>> inspectPlanMap = getInspectPlanMap(equIds, yearMonth);
+
+        List<Map<String, Object>> resultList = equPageList.getRecords().stream()
+            .map(equ -> buildEquipmentReport(equ, inspectStMap, inspectPlanMap, yearMonth))
+            .collect(Collectors.toList());
+
+        page.setRecords(resultList);
+        return page;
+    }
+
+    private YearMonth getYearMonthFromParams(Map<String, Object> queryParams) {
+        if (queryParams.containsKey("selectMonth")) {
+            String[] parts = queryParams.get("selectMonth").toString().split("-");
+            return YearMonth.of(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]));
+        }
+        return YearMonth.now();
+    }
+
+    private Map<Long, List<EimsInspectSt>> getInspectStMap(List<Long> equIds, LocalDate firstDay, LocalDate lastDay) {
+        LambdaQueryWrapper<EimsInspectSt> sWrapper = Wrappers.lambdaQuery();
+        sWrapper.in(EimsInspectSt::getEquId, equIds)
+            .between(EimsInspectSt::getPlanTime, firstDay, lastDay)
+            .eq(EimsInspectSt::getType, "Day");
+
+        return inspectStMapper.selectList(sWrapper).stream()
+            .collect(Collectors.groupingBy(EimsInspectSt::getEquId));
+    }
+
+    private Map<Long, List<EimsInspectPlan>> getInspectPlanMap(List<Long> equIds, YearMonth yearMonth) {
+        LocalDateTime firstDayTime = yearMonth.atDay(1).atStartOfDay();
+        LocalDateTime lastDayTime = yearMonth.atEndOfMonth().atTime(23, 59, 59);
+
+        LambdaQueryWrapper<EimsInspectPlan> pWrapper = Wrappers.lambdaQuery();
+        pWrapper.in(EimsInspectPlan::getEquId, equIds)
+            .between(EimsInspectPlan::getCreateTime, firstDayTime, lastDayTime);
+
+        return inspectPlanMapper.selectList(pWrapper).stream()
+            .collect(Collectors.groupingBy(EimsInspectPlan::getEquId));
+    }
+
+    private Map<String, Object> buildEquipmentReport(EimsEquVo equ,
+                                                     Map<Long, List<EimsInspectSt>> inspectStMap,
+                                                     Map<Long, List<EimsInspectPlan>> inspectPlanMap,
+                                                     YearMonth yearMonth) {
+        Map<String, Object> item = new HashMap<>();
+        item.put("equName", equ.getEquName());
+        item.put("assetNo", equ.getAssetNo());
+
+        List<EimsInspectSt> equInspList = inspectStMap.getOrDefault(equ.getEquId(), Collections.emptyList());
+        List<EimsInspectPlan> equPlanList = inspectPlanMap.getOrDefault(equ.getEquId(), Collections.emptyList());
+
+        Map<String, EimsInspectSt> stMap = equInspList.stream()
+            .collect(Collectors.toMap(
+                inspSt -> String.valueOf(inspSt.getPlanTime().toInstant()
+                    .atZone(ZoneId.systemDefault())
+                    .toLocalDate().getDayOfMonth()),
+                inspSt -> inspSt,
+                (existing, replacement) -> existing
+            ));
+
+        Map<String, EimsInspectPlan> planMap = equPlanList.stream()
+            .collect(Collectors.toMap(
+                inspPlan -> String.valueOf(inspPlan.getCreateTime().toInstant()
+                    .atZone(ZoneId.systemDefault())
+                    .toLocalDate().getDayOfMonth()),
+                inspPlan -> inspPlan,
+                (existing, replacement) -> existing
+            ));
+
+        int daysInMonth = yearMonth.lengthOfMonth();
+        for (int day = 1; day <= daysInMonth; day++) {
+            String dayKey = "day_" + day;
+            if (planMap.containsKey(String.valueOf(day))) {
+                item.put(dayKey, UNCHECKED);
+            } else if (stMap.containsKey(String.valueOf(day))) {
+                EimsInspectSt st = stMap.get(String.valueOf(day));
+                switch (st.getStatus()) {
+                    case "0":
+                        item.put(dayKey, UNCHECKED);
+                        break;
+                    case "1":
+                        item.put(dayKey, PENDING_VERIFICATION);
+                        break;
+                    case "2":
+                        item.put(dayKey, COMPLETED);
+                        break;
+                    default:
+                        item.put(dayKey, NOT_APPLICABLE);
+                }
+            } else {
+                item.put(dayKey, NOT_APPLICABLE);
+            }
+        }
+
+        return item;
+    }
+
+
+
+
+
+    private Integer getYearFromParams(Map<String, Object> queryParams) {
+        if (queryParams.containsKey("selectYear")) {
+            String year = queryParams.get("selectYear").toString();
+            return  Integer.parseInt(year);
+        }
+        return LocalDate.now().getYear();
+    }
+    private Map<Long, List<EimsMaintOrder>> getMaintOrderMap(List<Long> equIds, LocalDate firstDay, LocalDate lastDay) {
+        LambdaQueryWrapper<EimsMaintOrder> sWrapper = Wrappers.lambdaQuery();
+        sWrapper.in(EimsMaintOrder::getEquId, equIds)
+            .between(EimsMaintOrder::getPlanTime, firstDay, lastDay);
+
+        return maintOrderMapper.selectList(sWrapper).stream()
+            .collect(Collectors.groupingBy(EimsMaintOrder::getEquId));
+    }
+
+    private Map<Long, List<EimsMaintPlan>> getMaintPlanMap(List<Long> equIds, Integer year) {
+        LocalDateTime firstDay = Year.of(year).atDay(1).atStartOfDay();
+        LocalDateTime lastDay = Year.of(year).atDay(Year.of(year).length())
+            .atTime(23, 59, 59);
+
+        LambdaQueryWrapper<EimsMaintPlan> pWrapper = Wrappers.lambdaQuery();
+        pWrapper.in(EimsMaintPlan::getEquId, equIds)
+            .between(EimsMaintPlan::getCreateTime, firstDay, lastDay);
+
+        return maintPlanMapper.selectList(pWrapper).stream()
+            .collect(Collectors.groupingBy(EimsMaintPlan::getEquId));
+    }
+
+    private Map<String, Object> buildMaintReport(EimsEquVo equ,
+                                                     Map<Long, List<EimsMaintOrder>> maintOrderMap,
+                                                     Map<Long, List<EimsMaintPlan>> maintPlanMap) {
+        Map<String, Object> item = new HashMap<>();
+        item.put("equName", equ.getEquName());
+        item.put("assetNo", equ.getAssetNo());
+
+        List<EimsMaintOrder> equMaintOrderList = maintOrderMap.getOrDefault(equ.getEquId(), Collections.emptyList());
+        List<EimsMaintPlan> equMaintPlanList = maintPlanMap.getOrDefault(equ.getEquId(), Collections.emptyList());
+
+
+        Map<String, String> orderMap = equMaintOrderList.stream()
+            .collect(Collectors.groupingBy(
+                order -> String.valueOf(order.getPlanTime().toInstant()
+                    .atZone(ZoneId.systemDefault())
+                    .toLocalDate().getMonthValue()),
+                // 鎸夋湀浠藉垎缁勫悗锛屽垽鏂鏈堟槸鍚﹀叏閮╯tatus=3
+                Collectors.collectingAndThen(
+                    Collectors.toList(),
+                    orders -> orders.stream().allMatch(o -> o.getStatus().equals("3"))
+                        ? "宸插畬鎴�"
+                        : "鏈畬鎴�"
+                )
+            ));
+        Map<String, EimsMaintPlan> planMap = equMaintPlanList.stream()
+            .collect(Collectors.toMap(
+                maintPlan -> String.valueOf(maintPlan.getCreateTime().toInstant()
+                    .atZone(ZoneId.systemDefault())
+                    .toLocalDate().getMonthValue()),
+                maintPlan -> maintPlan,
+                (existing, replacement) -> existing
+            ));
+
+        int monthInYear = 12;
+        for (int month = 1; month <= monthInYear; month++) {
+            String monthKey = "month_" + month;
+            if (planMap.containsKey(String.valueOf(month))) {
+                item.put(monthKey, UNCHECKED);
+            } else if (orderMap.containsKey(String.valueOf(month))) {
+                String result  = orderMap.get(String.valueOf(month));
+                item.put(monthKey, result);
+
+            } else {
+                item.put(monthKey, NOT_APPLICABLE);
+            }
+        }
+
+        return item;
+    }
+}

--
Gitblit v1.9.3