From 55c6624407e8ae9d8ba9dd4c65d4c1d3b0ffaed0 Mon Sep 17 00:00:00 2001
From: wangrong <wangrong@shsening.com>
Date: 星期一, 12 十月 2026 00:03:54 +0800
Subject: [PATCH] 首页图表做下尊

---
 src/modules/report/controller/admin/screen.ts |   10 +++++
 src/modules/base/middleware/authority.ts      |    5 +-
 src/modules/report/service/screen.ts          |   97 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 110 insertions(+), 2 deletions(-)

diff --git a/src/modules/base/middleware/authority.ts b/src/modules/base/middleware/authority.ts
index 6f1e934..5f9b021 100644
--- a/src/modules/base/middleware/authority.ts
+++ b/src/modules/base/middleware/authority.ts
@@ -101,8 +101,9 @@
             new RegExp(`^${adminUrl}?.*/comm/`).test(url) ||
             // 瀛楀吀鎺ュ彛
             url == '/admin/dict/info/data' ||
-            // 椹鹃┒鑸遍椤垫眹鎬伙細鑿滃崟銆岄椤点�嶆病鏈夋寜閽潈闄愶紝鏈櫥璁� report:screen:query 鏃堕潪瓒呯浼� 403
-            url == '/admin/report/screen/query'
+            // 椹鹃┒鑸遍椤碉細鑿滃崟銆岄椤点�嶆病鏈夋寜閽潈闄愶紝鏈櫥璁� report:screen:* 鏃堕潪瓒呯浼� 403
+            url == '/admin/report/screen/query' ||
+            url == '/admin/report/screen/drill'
           ) {
             await next();
             return;
diff --git a/src/modules/report/controller/admin/screen.ts b/src/modules/report/controller/admin/screen.ts
index c862300..47fa398 100644
--- a/src/modules/report/controller/admin/screen.ts
+++ b/src/modules/report/controller/admin/screen.ts
@@ -14,4 +14,14 @@
   async query(@Body('range') range: string) {
     return this.ok(await this.reportScreenService.query(range));
   }
+
+  @Post('/drill', { summary: '鍒嗙被涓嬮捇閮ㄩ棬/绔欑偣' })
+  async drill(
+    @Body('range') range: string,
+    @Body('code') code: string,
+    @Body('metric') metric: string,
+    @Body('departmentId') departmentId?: number
+  ) {
+    return this.ok(await this.reportScreenService.drill(range, code, metric, departmentId));
+  }
 }
diff --git a/src/modules/report/service/screen.ts b/src/modules/report/service/screen.ts
index ac1c6d2..31b7b45 100644
--- a/src/modules/report/service/screen.ts
+++ b/src/modules/report/service/screen.ts
@@ -17,6 +17,7 @@
 import { UserInfoEntity } from '../../user/entity/info';
 import { PushVehicleGpsEntity } from '../../push/entity/vehiclegps';
 import { BaseSysParamEntity } from '../../base/entity/sys/param';
+import { BaseSysDepartmentEntity } from '../../base/entity/sys/department';
 
 const WASTE_COLORS: Record<string, string> = {
   SW62: '#16a34a',
@@ -278,6 +279,102 @@
     };
   }
 
+  /**
+   * 鍒嗙被涓嬮捇锛氫笉浼� departmentId 鎸夐儴闂ㄦ眹鎬昏绫绘寚鏍囷紱浼犲叆鍒欎笅閽昏閮ㄩ棬涓嬪悇绔欑偣銆�
+   * metric: weight | value | count | carbon
+   */
+  async drill(range: string, code: string, metric: string, departmentId?: number) {
+    const dates = this.getRange(range || 'month');
+    const rawCode = String(code || '').toUpperCase();
+    const field = this.resolveDrillField(rawCode, metric);
+    const names = await this.wasteNames();
+    const categoryName = names.get(rawCode) || this.defaultWasteName(rawCode);
+    const deptId =
+      departmentId == null || departmentId === ('' as any)
+        ? undefined
+        : Number(departmentId);
+    const level = Number.isFinite(deptId as number) ? 'site' : 'department';
+
+    if (!field) {
+      return {
+        level,
+        code: rawCode,
+        categoryName,
+        departmentId: Number.isFinite(deptId as number) ? deptId : null,
+        departmentName: null,
+        items: [],
+      };
+    }
+
+    const expr = `SUM(IFNULL(a.${field},0))`;
+    const qb = this.dailySite.createQueryBuilder('a');
+
+    let departmentName: string | null = null;
+    if (level === 'site') {
+      qb.select('a.siteId', 'id')
+        .addSelect('a.siteName', 'name')
+        .addSelect(expr, 'metricValue')
+        .where('a.date BETWEEN :start AND :end', { start: dates.start, end: dates.end })
+        .andWhere('a.departmentId = :deptId', { deptId })
+        .groupBy('a.siteId')
+        .addGroupBy('a.siteName');
+      const dept = await this.dailySite.manager
+        .createQueryBuilder(BaseSysDepartmentEntity, 'd')
+        .select('d.name', 'name')
+        .where('d.id = :id', { id: deptId })
+        .getRawOne();
+      departmentName = dept?.name || `閮ㄩ棬${deptId}`;
+    } else {
+      qb.select('a.departmentId', 'id')
+        .addSelect(`IFNULL(d.name, CONCAT('閮ㄩ棬', a.departmentId))`, 'name')
+        .addSelect(expr, 'metricValue')
+        .leftJoin(BaseSysDepartmentEntity, 'd', 'd.id = a.departmentId')
+        .where('a.date BETWEEN :start AND :end', { start: dates.start, end: dates.end })
+        .groupBy('a.departmentId')
+        .addGroupBy('d.name');
+    }
+
+    this.applyDept(qb, 'a');
+    const rows = await qb.getRawMany();
+    const absCarbon = metric === 'carbon';
+    const decimals = metric === 'value' || metric === 'count' ? 0 : 1;
+    const items = rows
+      .map(r => {
+        const raw = n(r.metricValue);
+        const value = round(absCarbon ? Math.abs(raw) : raw, decimals);
+        return {
+          id: n(r.id),
+          name: String(r.name || (level === 'site' ? `绔欑偣${r.id}` : `閮ㄩ棬${r.id}`)),
+          value,
+        };
+      })
+      .filter(e => e.value !== 0)
+      .sort((a, b) => b.value - a.value);
+
+    const total = items.reduce((s, e) => s + e.value, 0);
+    return {
+      level,
+      code: rawCode,
+      categoryName,
+      departmentId: Number.isFinite(deptId as number) ? deptId : null,
+      departmentName,
+      items: items.map(e => ({
+        ...e,
+        percent: total ? round((e.value / total) * 100, 1) : 0,
+      })),
+    };
+  }
+
+  private resolveDrillField(code: string, metric: string): string | null {
+    const f = WASTE_FIELDS.find(e => e.code === code);
+    if (!f) return null;
+    if (metric === 'weight') return f.weight;
+    if (metric === 'carbon') return f.carbon;
+    if (metric === 'count') return f.time;
+    if (metric === 'value') return f.sales || null;
+    return null;
+  }
+
   private deptIds(): number[] | null {
     const admin = this.ctx.admin;
     if (!admin || admin.username === 'admin') return null;

--
Gitblit v1.9.1