From 167510c1019f2c72ce9a544daec91aa5fe5409a5 Mon Sep 17 00:00:00 2001
From: wangzhibo <wangzhibo@shsening.com>
Date: 星期六, 18 七月 2026 11:55:31 +0800
Subject: [PATCH] 添加收集点和机构的汇总

---
 src/modules/report/service/taskservice.ts              |  174 ++++++++++
 src/modules/report/entity/joblog.ts                    |   40 ++
 src/modules/report/service/dailysite.ts                |   14 
 src/modules/report/service/taskdepartmentservice.ts    |  160 +++++++++
 src/modules/report/controller/admin/dailydepartment.ts |   17 +
 src/modules/report/config.ts                           |   14 
 src/modules/report/controller/admin/dailysite.ts       |   17 +
 src/modules/report/entity/dailydepartment.ts           |  163 +++++++++
 src/modules/report/service/joblog.ts                   |   14 
 src/modules/report/controller/admin/joblog.ts          |   17 +
 src/modules/report/entity/dailysite.ts                 |  163 +++++++++
 src/modules/report/service/dailydepartment.ts          |   14 
 src/modules/report/service/tasksiteservice.ts          |  154 +++++++++
 13 files changed, 961 insertions(+), 0 deletions(-)

diff --git a/src/modules/report/config.ts b/src/modules/report/config.ts
new file mode 100644
index 0000000..500a4a4
--- /dev/null
+++ b/src/modules/report/config.ts
@@ -0,0 +1,14 @@
+import { ModuleConfig } from '@cool-midway/core';
+
+/**
+ * 杩愮淮缁熻涓嶦TL鏃ュ織妯″潡閰嶇疆
+ */
+export default () => {
+  return {
+    name: '杩愮淮姹囨�绘ā鍧�',
+    description: '鎻愪緵绔欑偣涓庨儴闂ㄦ瘡鏃ユ暟鎹粺璁℃眹鎬诲強ETL鎵ц鏃ュ織瀹¤鍔熻兘',
+    middlewares: [],
+    globalMiddlewares: [],
+    order: 0
+  } as ModuleConfig;
+};
\ No newline at end of file
diff --git a/src/modules/report/controller/admin/dailydepartment.ts b/src/modules/report/controller/admin/dailydepartment.ts
new file mode 100644
index 0000000..0ffc292
--- /dev/null
+++ b/src/modules/report/controller/admin/dailydepartment.ts
@@ -0,0 +1,17 @@
+import { CoolController, BaseController } from '@cool-midway/core';
+import { ReportDailyDepartmentEntity } from '../../entity/dailydepartment';
+import { ReportDailyDepartmentService } from '../../service/dailydepartment';
+
+/**
+ * 姣忔棩閮ㄩ棬姹囨�绘帶鍒跺櫒
+ */
+@CoolController({
+  api: ['add', 'delete', 'update', 'info', 'list', 'page'],
+  entity: ReportDailyDepartmentEntity,
+  service: ReportDailyDepartmentService,
+  pageQueryOp: {
+    keyWordLikeFields: ['a.departmentName'],
+    fieldEq: ['a.date', 'a.departmentId']
+  }
+})
+export class AdminReportDailydepartmentController extends BaseController {}
\ No newline at end of file
diff --git a/src/modules/report/controller/admin/dailysite.ts b/src/modules/report/controller/admin/dailysite.ts
new file mode 100644
index 0000000..7658a8d
--- /dev/null
+++ b/src/modules/report/controller/admin/dailysite.ts
@@ -0,0 +1,17 @@
+import { CoolController, BaseController } from '@cool-midway/core';
+import { ReportDailysiteEntity } from '../../entity/dailysite';
+import { ReportDailysiteService } from '../../service/dailysite';
+
+/**
+ * 姣忔棩绔欑偣姹囨�绘帶鍒跺櫒
+ */
+@CoolController({
+  api: ['add', 'delete', 'update', 'info', 'list', 'page'],
+  entity: ReportDailysiteEntity,
+  service: ReportDailysiteService,
+  pageQueryOp: {
+    keyWordLikeFields: ['a.siteName'],
+    fieldEq: ['a.date', 'a.siteId']
+  }
+})
+export class AdminReportDailysiteController extends BaseController {}
\ No newline at end of file
diff --git a/src/modules/report/controller/admin/joblog.ts b/src/modules/report/controller/admin/joblog.ts
new file mode 100644
index 0000000..c6339e5
--- /dev/null
+++ b/src/modules/report/controller/admin/joblog.ts
@@ -0,0 +1,17 @@
+import { CoolController, BaseController } from '@cool-midway/core';
+import { ReportJoblogEntity } from '../../entity/joblog';
+import { ReportJoblogService } from '../../service/joblog';
+
+/**
+ * ETL浠诲姟鏃ュ織鎺у埗鍣�
+ */
+@CoolController({
+  api: ['add', 'delete', 'update', 'info', 'list', 'page'],
+  entity: ReportJoblogEntity,
+  service: ReportJoblogService,
+  pageQueryOp: {
+    keyWordLikeFields: ['a.jobName'],
+    fieldEq: ['a.dataDate', 'a.status']
+  }
+})
+export class AdminReportJoblogController extends BaseController {}
\ No newline at end of file
diff --git a/src/modules/report/entity/dailydepartment.ts b/src/modules/report/entity/dailydepartment.ts
new file mode 100644
index 0000000..4f315a9
--- /dev/null
+++ b/src/modules/report/entity/dailydepartment.ts
@@ -0,0 +1,163 @@
+import { BaseEntity } from '../../base/entity/base';
+import { Column, Entity, Index, Unique } from 'typeorm';
+
+/**
+ * 姣忔棩绔欑偣姹囨��
+ */
+@Entity('t_report_daily_department')
+@Unique('uk_date_department', ['date', 'departmentId']) // 馃挕 閮ㄩ棬琛ㄧ殑澶嶅悎鍞竴绾︽潫
+export class ReportDailyDepartmentEntity extends BaseEntity {
+  @Index()
+  @Column({ comment: '鏃ユ湡', type: 'date' })
+  date: string;
+
+  @Index()
+  @Column({ comment: '閮ㄩ棬ID' })
+  departmentId: number;
+
+  @Index()
+  @Column({ comment: '閮ㄩ棬鍚嶇О' })
+  departmentName: string;
+
+  @Column({ comment: '鎬婚噸閲�', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightTotal: number;
+
+  @Column({ comment: 'sw60閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw60: number;
+
+  @Column({ comment: 'sw61閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw61: number;
+
+  @Column({ comment: 'sw62閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw62: number;
+
+  @Column({ comment: 'sw63閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw63: number;
+
+  @Column({ comment: 'sw64閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw64: number;
+
+  @Column({ comment: 'sw62_001閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw62001: number;
+
+  @Column({ comment: 'sw62_002閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw62002: number;
+
+  @Column({ comment: 'sw62_003閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw62003: number;
+
+  @Column({ comment: 'sw62_004閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw62004: number;
+
+  @Column({ comment: 'sw62_005閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw62005: number;
+
+  @Column({ comment: 'sw62_006閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw62006: number;
+
+  @Column({ comment: 'sw62_007閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw62007: number;
+
+  @Column({ comment: '鎬荤О閲嶆鏁�', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeTotal: number;
+
+  @Column({ comment: 'sw60娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw60: number;
+
+  @Column({ comment: 'sw61娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw61: number;
+
+  @Column({ comment: 'sw62娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw62: number;
+
+  @Column({ comment: 'sw63娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw63: number;
+
+  @Column({ comment: 'sw64娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw64: number;
+
+  @Column({ comment: 'sw62001娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw62001: number;
+
+  @Column({ comment: 'sw62002娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw62002: number;
+
+  @Column({ comment: 'sw62003娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw62003: number;
+
+  @Column({ comment: 'sw62004娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw62004: number;
+
+  @Column({ comment: 'sw62005娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw62005: number;
+
+  @Column({ comment: 'sw62006娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw62006: number;
+
+  @Column({ comment: 'sw62007娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw62007: number;
+
+  @Column({ comment: '鎬荤⒊鎺掗噺', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonTotal: number;
+
+  @Column({ comment: 'sw60纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw60: number;
+
+  @Column({ comment: 'sw61纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw61: number;
+
+  @Column({ comment: 'sw62纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw62: number;
+
+  @Column({ comment: 'sw63纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw63: number;
+
+  @Column({ comment: 'sw64纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw64: number;
+
+  @Column({ comment: 'sw62001纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw62001: number;
+
+  @Column({ comment: 'sw62002纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw62002: number;
+
+  @Column({ comment: 'sw62003纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw62003: number;
+
+  @Column({ comment: 'sw62004纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw62004: number;
+
+  @Column({ comment: 'sw62005纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw62005: number;
+
+  @Column({ comment: 'sw62006纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw62006: number;
+
+  @Column({ comment: 'sw62007纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw62007: number;
+
+  @Column({ comment: 'sw62浼扮畻閿�鍞', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  estimatedSalesSw62: number;
+
+  @Column({ comment: 'sw62_001浼扮畻閿�鍞', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  estimatedSalesSw62001: number;
+
+  @Column({ comment: 'sw62_002浼扮畻閿�鍞', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  estimatedSalesSw62002: number;
+
+  @Column({ comment: 'sw62_003浼扮畻閿�鍞', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  estimatedSalesSw62003: number;
+
+  @Column({ comment: 'sw62_004浼扮畻閿�鍞', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  estimatedSalesSw62004: number;
+
+  @Column({ comment: 'sw62_005浼扮畻閿�鍞', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  estimatedSalesSw62005: number;
+
+  @Column({ comment: 'sw62_006浼扮畻閿�鍞', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  estimatedSalesSw62006: number;
+
+  @Column({ comment: 'sw62_007浼扮畻閿�鍞', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  estimatedSalesSw62007: number;
+
+}
\ No newline at end of file
diff --git a/src/modules/report/entity/dailysite.ts b/src/modules/report/entity/dailysite.ts
new file mode 100644
index 0000000..f29bd5c
--- /dev/null
+++ b/src/modules/report/entity/dailysite.ts
@@ -0,0 +1,163 @@
+import { BaseEntity } from '../../base/entity/base';
+import { Column, Entity, Index, Unique } from 'typeorm';
+
+/**
+ * 姣忔棩绔欑偣姹囨��
+ */
+@Entity('t_report_daily_site')
+@Unique('uk_date_site', ['date', 'siteId']) // 馃挕 閲嶇偣锛氬湪杩欓噷鍔犱笂澶嶅悎鍞竴绾︽潫锛岀涓�涓弬鏁版槸绱㈠紩鍚嶏紝绗簩涓槸瀛楁鏁扮粍
+export class ReportDailysiteEntity extends BaseEntity {
+  @Index()
+  @Column({ comment: '鏃ユ湡', type: 'date' })
+  date: string;
+
+  @Index()
+  @Column({ comment: '绔欑偣ID' })
+  siteId: number;
+
+  @Index()
+  @Column({ comment: '绔欑偣鍚嶇О' })
+  siteName: string;
+
+  @Column({ comment: '鎬婚噸閲�', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightTotal: number;
+
+  @Column({ comment: 'sw60閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw60: number;
+
+  @Column({ comment: 'sw61閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw61: number;
+
+  @Column({ comment: 'sw62閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw62: number;
+
+  @Column({ comment: 'sw63閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw63: number;
+
+  @Column({ comment: 'sw64閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw64: number;
+
+  @Column({ comment: 'sw62_001閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw62001: number;
+
+  @Column({ comment: 'sw62_002閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw62002: number;
+
+  @Column({ comment: 'sw62_003閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw62003: number;
+
+  @Column({ comment: 'sw62_004閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw62004: number;
+
+  @Column({ comment: 'sw62_005閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw62005: number;
+
+  @Column({ comment: 'sw62_006閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw62006: number;
+
+  @Column({ comment: 'sw62_007閲嶉噺', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  weightSw62007: number;
+
+  @Column({ comment: '鎬荤О閲嶆鏁�', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeTotal: number;
+
+  @Column({ comment: 'sw60娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw60: number;
+
+  @Column({ comment: 'sw61娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw61: number;
+
+  @Column({ comment: 'sw62娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw62: number;
+
+  @Column({ comment: 'sw63娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw63: number;
+
+  @Column({ comment: 'sw64娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw64: number;
+
+  @Column({ comment: 'sw62001娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw62001: number;
+
+  @Column({ comment: 'sw62002娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw62002: number;
+
+  @Column({ comment: 'sw62003娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw62003: number;
+
+  @Column({ comment: 'sw62004娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw62004: number;
+
+  @Column({ comment: 'sw62005娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw62005: number;
+
+  @Column({ comment: 'sw62006娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw62006: number;
+
+  @Column({ comment: 'sw62007娆℃暟', type: 'decimal', precision: 10, scale: 2, default: 0.00 })
+  timeSw62007: number;
+
+  @Column({ comment: '鎬荤⒊鎺掗噺', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonTotal: number;
+
+  @Column({ comment: 'sw60纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw60: number;
+
+  @Column({ comment: 'sw61纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw61: number;
+
+  @Column({ comment: 'sw62纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw62: number;
+
+  @Column({ comment: 'sw63纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw63: number;
+
+  @Column({ comment: 'sw64纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw64: number;
+
+  @Column({ comment: 'sw62001纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw62001: number;
+
+  @Column({ comment: 'sw62002纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw62002: number;
+
+  @Column({ comment: 'sw62003纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw62003: number;
+
+  @Column({ comment: 'sw62004纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw62004: number;
+
+  @Column({ comment: 'sw62005纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw62005: number;
+
+  @Column({ comment: 'sw62006纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw62006: number;
+
+  @Column({ comment: 'sw62007纰虫帓閲�', type: 'decimal', precision: 15, scale: 2, default: 0.0 })
+  carbonSw62007: number;
+
+  @Column({ comment: 'sw62浼扮畻閿�鍞', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  estimatedSalesSw62: number;
+
+  @Column({ comment: 'sw62_001浼扮畻閿�鍞', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  estimatedSalesSw62001: number;
+
+  @Column({ comment: 'sw62_002浼扮畻閿�鍞', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  estimatedSalesSw62002: number;
+
+  @Column({ comment: 'sw62_003浼扮畻閿�鍞', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  estimatedSalesSw62003: number;
+
+  @Column({ comment: 'sw62_004浼扮畻閿�鍞', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  estimatedSalesSw62004: number;
+
+  @Column({ comment: 'sw62_005浼扮畻閿�鍞', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  estimatedSalesSw62005: number;
+
+  @Column({ comment: 'sw62_006浼扮畻閿�鍞', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  estimatedSalesSw62006: number;
+
+  @Column({ comment: 'sw62_007浼扮畻閿�鍞', type: 'decimal', precision: 15, scale: 2, default: 0.00 })
+  estimatedSalesSw62007: number;
+
+}
\ No newline at end of file
diff --git a/src/modules/report/entity/joblog.ts b/src/modules/report/entity/joblog.ts
new file mode 100644
index 0000000..9591bbb
--- /dev/null
+++ b/src/modules/report/entity/joblog.ts
@@ -0,0 +1,40 @@
+import { BaseEntity } from '../../base/entity/base';
+import { Column, Entity, Index } from 'typeorm';
+
+/**
+ * ETL浠诲姟鏃ュ織
+ */
+@Entity('t_report_joblog')
+@Index(['jobName', 'dataDate'], { unique: true })
+export class ReportJoblogEntity extends BaseEntity {
+  @Index()
+  @Column({ comment: '浠诲姟绫诲瀷: SITE_DAILY(绔欑偣鏃ユ眹鎬�), DEPT_DAILY(閮ㄩ棬鏃ユ眹鎬�)' })
+  jobName: string;
+
+  @Index()
+  @Column({ comment: '鏁版嵁鐩爣鏃ユ湡', type: 'date' })
+  dataDate: string;
+
+  @Column({ type: 'datetime',comment: '寮�濮嬫椂闂�', nullable: true })
+  startTime: Date;
+
+  @Column({ type: 'datetime',comment: '缁撴潫鏃堕棿', nullable: true })
+  endTime: Date;
+
+  @Column({
+    type: 'enum',
+    enum: ['INIT', 'RUNNING', 'SUCCESS', 'FAILED'],
+    default: 'INIT',
+    comment: '鐘舵��: 鏈紑濮�, 杩愯涓�, 鎴愬姛, 澶辫触',
+  })
+  status: 'INIT' | 'RUNNING' | 'SUCCESS' | 'FAILED';
+
+  @Column({ comment: '澶勭悊璁板綍鏁�', default: 0 })
+  recordsProcessed: number;
+
+  @Column({ comment: '澶辫触娆℃暟', default: 0 })
+  failedCount: number;
+
+  @Column({ comment: '閿欒鍫嗘爤淇℃伅', type: 'text', nullable: true })
+  errorMessage: string;
+}
\ No newline at end of file
diff --git a/src/modules/report/service/dailydepartment.ts b/src/modules/report/service/dailydepartment.ts
new file mode 100644
index 0000000..c464952
--- /dev/null
+++ b/src/modules/report/service/dailydepartment.ts
@@ -0,0 +1,14 @@
+import { Provide } from '@midwayjs/core';
+import { BaseService } from '@cool-midway/core';
+import { InjectEntityModel } from '@midwayjs/typeorm';
+import { Repository } from 'typeorm';
+import { ReportDailyDepartmentEntity } from '../entity/dailydepartment';
+
+/**
+ * 姣忔棩閮ㄩ棬姹囨�绘湇鍔�
+ */
+@Provide()
+export class ReportDailyDepartmentService extends BaseService {
+  @InjectEntityModel(ReportDailyDepartmentEntity)
+  reportDailyDepartmentEntity: Repository<ReportDailyDepartmentEntity>;
+}
\ No newline at end of file
diff --git a/src/modules/report/service/dailysite.ts b/src/modules/report/service/dailysite.ts
new file mode 100644
index 0000000..1245580
--- /dev/null
+++ b/src/modules/report/service/dailysite.ts
@@ -0,0 +1,14 @@
+import { Provide } from '@midwayjs/core';
+import { BaseService } from '@cool-midway/core';
+import { InjectEntityModel } from '@midwayjs/typeorm';
+import { Repository } from 'typeorm';
+import { ReportDailysiteEntity } from '../entity/dailysite';
+
+/**
+ * 姣忔棩绔欑偣姹囨�绘湇鍔�
+ */
+@Provide()
+export class ReportDailysiteService extends BaseService {
+  @InjectEntityModel(ReportDailysiteEntity)
+  reportDailysiteEntity: Repository<ReportDailysiteEntity>;
+}
\ No newline at end of file
diff --git a/src/modules/report/service/joblog.ts b/src/modules/report/service/joblog.ts
new file mode 100644
index 0000000..6ad74e3
--- /dev/null
+++ b/src/modules/report/service/joblog.ts
@@ -0,0 +1,14 @@
+import { Provide } from '@midwayjs/core';
+import { BaseService } from '@cool-midway/core';
+import { InjectEntityModel } from '@midwayjs/typeorm';
+import { Repository } from 'typeorm';
+import { ReportJoblogEntity } from '../entity/joblog';
+
+/**
+ * ETL浠诲姟鏃ュ織鏈嶅姟
+ */
+@Provide()
+export class ReportJoblogService extends BaseService {
+  @InjectEntityModel(ReportJoblogEntity)
+  reportJoblogEntity: Repository<ReportJoblogEntity>;
+}
\ No newline at end of file
diff --git a/src/modules/report/service/taskdepartmentservice.ts b/src/modules/report/service/taskdepartmentservice.ts
new file mode 100644
index 0000000..f0d920d
--- /dev/null
+++ b/src/modules/report/service/taskdepartmentservice.ts
@@ -0,0 +1,160 @@
+import { Provide, Inject } from '@midwayjs/core';
+import { BaseService } from '@cool-midway/core';
+import { InjectEntityModel } from '@midwayjs/typeorm';
+import { Repository } from 'typeorm';
+import { ReportDailysiteEntity } from '../entity/dailysite';
+import { ReportDailyDepartmentEntity } from '../entity/dailydepartment';
+import { BaseSysDepartmentEntity } from '../../base/entity/sys/department'; 
+import { BasicdataSiteEntity } from '../../basicdata/entity/site';
+
+
+/**
+ *  瀹氭椂浠诲姟-瀛愪换鍔� 姹囨�婚儴闂ㄧ殑鍚勭被鍨冨溇鏁版嵁
+ */
+@Provide()
+export class TaskDepartmentService extends BaseService {
+  @InjectEntityModel(ReportDailysiteEntity)
+  reportDailysiteEntity: Repository<ReportDailysiteEntity>;
+
+  @InjectEntityModel(ReportDailyDepartmentEntity)
+  reportDailyDepartmentEntity: Repository<ReportDailyDepartmentEntity>;
+
+  @InjectEntityModel(BaseSysDepartmentEntity)
+  baseSysDepartmentEntity: Repository<BaseSysDepartmentEntity>;
+
+  @InjectEntityModel(BasicdataSiteEntity)
+  basicdataSiteEntity: Repository<BasicdataSiteEntity>;
+
+  /**
+   * 閮ㄩ棬鏃ヨ〃鏃犵骇閫掑綊姹囨�诲紩鎿庡叆鍙�
+   * @param targetDate 鐩爣璁$畻鏃ユ湡鏍煎紡 'YYYY-MM-DD'
+   */
+  async aggregateDailyData(targetDate: string) {
+    // 1. 寮傛骞惰鍔犺浇鍩虹瀛楀吀鍜屽綋澶╁凡绠楀ソ鐨勭珯鐐规棩琛ㄦ暟鎹紝鏈�澶у寲I/O鏁堢巼
+    const [allDepts, allSites, dailySites] = await Promise.all([
+      this.baseSysDepartmentEntity.find({ select: ['id', 'parentId', 'name'] }),
+      this.basicdataSiteEntity.find({ select: ['id', 'departmentId'], where: { siteType: '鏀堕泦鐐�' } }),
+      this.reportDailysiteEntity.findBy({ date: targetDate })
+    ]);
+
+    if (dailySites.length === 0) {
+      // 濡傛灉褰撳ぉ娌℃湁浠讳綍绔欑偣浜х敓娴佹按锛岄『搴斺�滄病鏁颁笉瀛樷�濆師鍒欙紝閮ㄩ棬琛ㄤ篃鐩存帴涓嶅瓨浠讳綍鏁版嵁锛屽畬缇庢敹宸�
+      return;
+    }
+
+    // 2. 寤虹珛 绔欑偣ID -> 閮ㄩ棬ID 鐨勬槧灏勫揩鏌ヨ〃
+    const siteToDeptMap = new Map<number, number>();
+    allSites.forEach(s => {
+      if (s.departmentId) siteToDeptMap.set(s.id, s.departmentId);
+    });
+
+    // 3. 鏋勫缓閮ㄩ棬鏍戠殑鍏崇郴閾撅紙鐢ㄤ簬鏃犵骇鍚戜笂杩芥函锛�
+    const deptParentMap = new Map<number, number | null>();
+    const deptNameMap = new Map<number, string>();
+    allDepts.forEach(d => {
+      deptParentMap.set(d.id, d.parentId || null);
+      deptNameMap.set(d.id, d.name);
+    });
+
+    // 4. 瀹氫箟闇�瑕佺疮鍔犵殑鍔ㄦ�佸垎绫诲瓧娈垫竻鍗曪紙蹇呴』鍜岀珯鐐硅〃绠楀嚭鏉ョ殑50澶氫釜瀛楁瀹屽叏瀵归綈锛�
+    const typeSuffixes = [
+      'Sw60', 'Sw61', 'Sw62', 'Sw63', 'Sw64',
+      'Sw62001', 'Sw62002', 'Sw62003', 'Sw62004', 'Sw62005', 'Sw62006', 'Sw62007'
+    ];
+   
+
+    // 5. 澹版槑閮ㄩ棬鏁版嵁鐨勫唴瀛樿仛鍚堝鍣�
+    const deptAggMap = new Map<number, any>();
+
+    // 鍒濆鍖栦竴涓共鍑�閮ㄩ棬瀵硅薄鐨勮緟鍔╁嚱鏁�
+    const initDeptData = (deptId: number) => ({
+      date: targetDate,
+      departmentId: deptId,
+      departmentName: deptNameMap.get(deptId) || `鏈煡閮ㄩ棬(${deptId})`,
+      weightTotal: 0,
+      timeTotal: 0,
+      carbonTotal: 0,
+      estimatedSalesSw62: 0,
+      estimatedSalesSw62001: 0,
+      estimatedSalesSw62002: 0,
+      estimatedSalesSw62003: 0,
+      estimatedSalesSw62004: 0,
+      estimatedSalesSw62005: 0,
+      estimatedSalesSw62006: 0,
+      estimatedSalesSw62007: 0,
+      ...typeSuffixes.reduce((acc, suffix) => {
+        acc[`weight${suffix}`] = 0;
+        acc[`time${suffix}`] = 0;
+        acc[`carbon${suffix}`] = 0;
+        return acc;
+      }, {})
+    });
+
+    // 6. 鏍稿績鏃犵骇杞扮偢绠楁硶锛氶亶鍘嗘瘡涓�涓湁鏁版嵁鐨勭珯鐐癸紝鍚戝畠鐨勬墍鏈変笂绾х埗閮ㄩ棬鈥滃啋娉♀�濊础鐚暟鎹�
+    for (const siteRow of dailySites) {
+      const directDeptId = siteToDeptMap.get(siteRow.siteId);
+      if (!directDeptId) continue; // 濡傛灉绔欑偣鑴辩浜嗛儴闂ㄧ粍缁囨灦鏋勶紝璺宠繃
+
+      // 浠庡綋鍓嶇洿灞為儴闂ㄥ紑濮嬶紝娌跨潃 parentId 涓�璺線涓婄埇锛岀洿鍒扮埇鍒扮鍏堟牴鑺傜偣锛坧arentId 涓� null锛�
+      let currentDeptId: number | null | undefined = directDeptId;
+      
+      while (currentDeptId !== null && currentDeptId !== undefined) {
+        // 濡傛灉鏍戜腑娌℃湁杩欎釜閮ㄩ棬ID锛堝紓甯告暟鎹槻宕╋級锛屼腑鏂��鍑�
+        if (!deptParentMap.has(currentDeptId)) break;
+
+        // 濡傛灉鍐呭瓨涓繕娌℃湁杩欎釜閮ㄩ棬鐨勮仛鍚堢┖闂达紝绔嬪嵆涓哄叾鐙珛鍒濆鍖�
+        if (!deptAggMap.has(currentDeptId)) {
+          deptAggMap.set(currentDeptId, initDeptData(currentDeptId));
+        }
+
+        const deptAgg = deptAggMap.get(currentDeptId);
+
+        // 鏍稿績杞扮偢锛氬皢璇ョ珯鐐圭殑鍚勯」鏁板�硷紝鍙犲姞鍒板綋鍓嶉儴闂ㄧ殑鎵�鏈夊搴旀寚鏍囦笂
+        deptAgg.weightTotal += Number(siteRow.weightTotal) || 0;
+        deptAgg.timeTotal += Number(siteRow.timeTotal) || 0;
+        deptAgg.carbonTotal += Number(siteRow.carbonTotal) || 0;
+
+        // 鍔ㄦ�佽桨鐐� 50 澶氫釜瀛愮被瀛楁
+        typeSuffixes.forEach(suffix => {
+          deptAgg[`weight${suffix}`] += Number(siteRow[`weight${suffix}`]) || 0;
+          deptAgg[`time${suffix}`] += Number(siteRow[`time${suffix}`]) || 0;
+          deptAgg[`carbon${suffix}`] += Number(siteRow[`carbon${suffix}`]) || 0;
+
+          // 鍥炴敹鐗╁瓙绫荤殑浼扮畻閲戦鍚屾鍙犲姞
+          if (suffix.startsWith('Sw62')) {
+            deptAgg[`estimatedSales${suffix}`] += Number(siteRow[`estimatedSales${suffix}`]) || 0;
+          }
+        });
+
+        // 銆愬叧閿穬杩併�戯細灏嗘寚閽堢Щ鍚戝畠鐨勪翰鐢熺埗鑺傜偣锛屽疄鐜版棤绾ч�掑綊鍚戜笂鍐掓场锛�
+        currentDeptId = deptParentMap.get(currentDeptId);
+      }
+    }
+
+    // 7. 灏嗗唴瀛樹腑鎵�鏈夎鈥滆桨鐐糕�濊繃銆佷骇鐢熶簡鏈夋晥鏁版嵁鐨勯儴闂ㄨ褰曟彁鍙栧嚭鏉�
+    const finalDeptRows = Array.from(deptAggMap.values());
+
+    if (finalDeptRows.length > 0) {
+            // 1. 鍔ㄦ�佹姄鍙栨墍鏈夌殑閿紝灏嗕富閿�佽仈鍚堝敮涓�閿交搴曚粠鈥滃緟鏇存柊瀛楁鈥濅腑鎺掗櫎
+            // 杩欐牱鑳界‘淇� ON DUPLICATE KEY UPDATE 鍚庨潰鐨勮祴鍊艰鍙ョ粷瀵瑰共鍑�
+            const allUpdateFields = Object.keys(finalDeptRows[0]).filter(
+                k => k !== 'date' && k !== 'departmentId' && k !== 'id'
+            );
+
+            // 2. 渚濈劧鎺ㄨ崘閲囩敤 Chunk 鍒嗘壒锛岃繖鏄簲瀵规捣閲忔暟鎹�/澶氬瓙瀛楁鏈�瀹夊叏銆佹墽琛屾晥鐜囨渶楂樼殑鍋氭硶
+            const chunkSize = 50;
+            for (let i = 0; i < finalDeptRows.length; i += chunkSize) {
+                const chunk = finalDeptRows.slice(i, i + chunkSize);
+
+                await this.reportDailyDepartmentEntity.createQueryBuilder()
+                    .insert()
+                    .values(chunk)
+                    // 馃挕 鏄惧紡澹版槑锛氬綋 ['date', 'departmentId'] 鍙戠敓鍐茬獊鏃讹紝寮鸿鎶� allUpdateFields 閲岀殑瀛楁鍏ㄩ儴鏇存柊涓�閬�
+                    .orUpdate(allUpdateFields, ['date', 'departmentId'])
+                    .execute();
+            }
+        }
+
+    return finalDeptRows.length;
+  }
+}
\ No newline at end of file
diff --git a/src/modules/report/service/taskservice.ts b/src/modules/report/service/taskservice.ts
new file mode 100644
index 0000000..9536abe
--- /dev/null
+++ b/src/modules/report/service/taskservice.ts
@@ -0,0 +1,174 @@
+import { Provide, Inject } from '@midwayjs/core';
+import { BaseService } from '@cool-midway/core';
+import { InjectEntityModel } from '@midwayjs/typeorm';
+import { Repository, In } from 'typeorm';
+import { ReportJoblogEntity } from '../entity/joblog';
+import { TaskSiteService } from './tasksiteservice'
+import { TaskDepartmentService } from './taskdepartmentservice'
+
+/**
+ *  瀹氭椂浠诲姟-姹囨�荤珯鐐�/鍚勪釜鍗曚綅鐨勫悇绫诲瀮鍦炬眹鎬绘暟鎹�
+ *  鎬诲叆鍙o紝鍒涘缓鍜岃皟搴︿换鍔★紝鍏蜂綋鎵ц璋冪敤TaskSiteService 鍜�  TaskDepartmentService
+ */
+
+
+@Provide()
+export class ReportTaskService extends BaseService {
+  @InjectEntityModel(ReportJoblogEntity)
+  reportJoblogEntity: Repository<ReportJoblogEntity>;
+
+  // 娉ㄥ叆浣犲叿浣撶殑涓氬姟 Service锛岀敤浜庤窇鐪熸鐨勬眹鎬� SQL
+  @Inject()
+  taskSiteService: TaskSiteService;
+
+  @Inject()
+  departmentCubeService: TaskDepartmentService;
+
+  /**
+   * 澶栭儴瀹氭椂浠诲姟锛圕rontab锛夋瘡灏忔椂璋冪敤鐨勬�诲叆鍙�
+   * 寤鸿琛ㄨ揪寮�: 0 5 * * * * (姣忓皬鏃剁殑绗�5鍒嗛挓鎵ц)
+   */
+  async clockIn() {
+    // 1. 鑷姩鐪嬮棬鐙楋細纭繚鏄ㄥぉ鍜屼粖澶╃殑浠诲姟瀹炰緥宸茬粡鍦ㄨ〃閲屽垵濮嬪寲浜�
+    await this.ensureJobInstancesCreated();
+
+    // 2. 鎵ц寮曟搸锛氭崬鍑洪渶瑕佹墽琛屾垨閲嶈瘯鐨勪换鍔�
+    await this.executePendingJobs();
+
+    return '浠诲姟鎵ц鎴愬姛:ReportTaskService';
+  }
+
+  /**
+   * 妫�鏌ュ苟鍒濆鍖栦换鍔″疄渚嬶紙瀹堟姢鍔ㄤ綔锛�
+   */
+  private async ensureJobInstancesCreated() {
+    const now = new Date();
+    const year = now.getFullYear();
+    const month = String(now.getMonth() + 1).padStart(2, '0');
+    const day = String(now.getDate()).padStart(2, '0');
+    const yesterdayDate = new Date(now.getTime() - 24 * 60 * 60 * 1000);
+    const yYear = yesterdayDate.getFullYear();
+    const yMonth = String(yesterdayDate.getMonth() + 1).padStart(2, '0');
+    const yDay = String(yesterdayDate.getDate()).padStart(2, '0');
+
+    const todayStr = `${year}-${month}-${day}`;
+    const yesterdayStr = `${yYear}-${yMonth}-${yDay}`;
+
+    // 鎴戜滑闇�瑕佸畧鎶ょ殑鐩爣鏃ユ湡鍜屼换鍔$被鍨嬬粍鍚�
+    const targets = [
+      { date: yesterdayStr, type: 'SITE_DAILY',isToday: false},
+      { date: yesterdayStr, type: 'DEPT_DAILY',isToday: false},
+      { date: todayStr, type: 'SITE_DAILY' ,isToday: true},
+      { date: todayStr, type: 'DEPT_DAILY' ,isToday: true},
+    ];
+
+    for (const target of targets) {
+      try {
+        // 鍒╃敤 try-catch 鍜屾暟鎹簱鍞竴绱㈠紩锛岄槻骞跺彂鍚屾椂涔熻兘瀹炵幇鈥滀笉瀛樺湪鍒欏垱寤衡��
+        const exist = await this.reportJoblogEntity.findOneBy({
+          jobName: target.type,
+          dataDate: target.date,
+        });
+        if (!exist) {
+          const job = new ReportJoblogEntity();
+          job.jobName = target.type;
+          job.dataDate = target.date;
+          job.status = 'INIT';
+          await this.reportJoblogEntity.save(job);
+        }else if (target.isToday && exist.status === 'SUCCESS') {
+          // 2. 銆愭牳蹇冧慨澶嶃�戝鏋滄槸浠婂ぉ鐨勪换鍔★紝涓斿凡缁忔槸 SUCCESS 鐘舵�侊紝寮鸿娲楀洖 INIT 鍙備笌鏈皬鏃剁殑閲嶇畻
+          await this.reportJoblogEntity.update(exist.id, {
+            status: 'INIT'
+          });
+        }
+      } catch (err) {
+        // 骞跺彂鍐欏叆鏃跺彲鑳借Е鍙戝敮涓�绱㈠紩鍐茬獊锛岀洿鎺ュ拷鐣ュ嵆鍙�
+      }
+    }
+  }
+
+  /**
+   * 鎹炲嚭 INIT 鍜� FAILED 鐨勪换鍔★紝骞朵覆琛屼弗鏍兼寜椤哄簭鎵ц
+   */
+  private async executePendingJobs() {
+    // 鎹炲嚭鎵�鏈夊緟澶勭悊浠诲姟
+    const pendingJobs = await this.reportJoblogEntity.find({
+      where: {
+        status: In(['INIT', 'FAILED']),
+      },
+      order: {
+        dataDate: 'ASC', // 鍏堢畻鍘嗗彶锛屽悗绠椾粖澶�
+        // 纭繚鍦ㄥ悓涓�澶╁唴锛孲ITE_DAILY 鍏堣窇锛孌EPT_DAILY 鍚庤窇
+        // 鍦� JS 鏁扮粍涓垜浠彲浠ヨ繘涓�姝ユ帶鍒舵帓搴忥紝鎴栬�呯敱涓氬姟閫昏緫涓ユ牸鎺у埗渚濊禆
+      },
+    });
+
+    for (const job of pendingJobs) {
+      // 涓ユ牸渚濊禆妫�鏌ワ細濡傛灉鏄儴闂ㄦ棩琛ㄤ换鍔★紝蹇呴』纭繚鍚屽ぉ鐨勭珯鐐规棩琛ㄤ换鍔″凡缁忔槸 SUCCESS
+      if (job.jobName === 'DEPT_DAILY') {
+        const siteJob = await this.reportJoblogEntity.findOneBy({
+          dataDate: job.dataDate,
+          jobName: 'SITE_DAILY',
+        });
+        if (!siteJob || siteJob.status !== 'SUCCESS') {
+          // 婧愬ご鏄庣粏杩樻病绠楀ソ/鎴栬繕鍦ㄨ繍琛岋紝閮ㄩ棬琛ㄤ换鍔″繀椤荤瓑寰咃紝璺宠繃鏈鎵ц
+          continue;
+        }
+      }
+
+      // 閿佸畾浠诲姟鐘舵�佷负 RUNNING锛堣交閲忕骇涔愯閿侀攣鏈哄埗锛�
+      const lockResult = await this.reportJoblogEntity.update(
+        { id: job.id, status: job.status }, // 甯︽湁鍘熷鐘舵�佹鏌ワ紝闃叉琚埆鐨勭嚎绋嬫姠璺�
+        { status: 'RUNNING', startTime: new Date() }
+      );
+
+      if (lockResult.affected === 0) {
+        continue; // 娌℃姠鍒伴攣锛岃鏄庡埆鐨勫畾鏃跺櫒鍦ㄨ窇瀹冿紝璺宠繃
+      }
+
+      // 鐪熸寮�濮嬭窇鏁版嵁
+      try {
+        let recordsProcessed = 0 ;
+
+        if (job.jobName === 'SITE_DAILY') {
+          // 璋冪敤浣犲啓鐨� 绔欑偣鏃ヨ〃 鎻愬彇閫昏緫
+          recordsProcessed = await this.taskSiteService.aggregateDailyData(job.dataDate) || 0;
+        } else if (job.jobName === 'DEPT_DAILY') {
+          // 璋冪敤浣犲啓鐨� 閮ㄩ棬鏍戣桨鐐� 鎻愬彇閫昏緫
+          recordsProcessed = await this.departmentCubeService.aggregateDailyData(job.dataDate) || 0;
+        }
+
+        // 鎴愬姛锛氭洿鏂扮姸鎬�
+        await this.reportJoblogEntity.update(job.id, {
+            status: 'SUCCESS',
+            recordsProcessed:recordsProcessed,
+            endTime: new Date(),
+            errorMessage: '',
+          });
+
+      } catch (error) {
+        // 澶辫触锛氳褰曞爢鏍堬紝绛夊緟涓嬩竴灏忔椂閲嶈瘯
+
+        var errorMessage = '';
+        if (error instanceof Error) {
+          errorMessage = error.stack ?? error.message;
+        }
+        if (typeof error === 'string') {
+          errorMessage = error;
+        }
+        try {
+          errorMessage = JSON.stringify(error);
+        } catch {
+          errorMessage = String(error);
+        }
+
+        await this.reportJoblogEntity.update(job.id, {
+          status: 'FAILED',
+          endTime: new Date(),
+          errorMessage: errorMessage,
+          failedCount: () => 'failedCount + 1',
+        });
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/modules/report/service/tasksiteservice.ts b/src/modules/report/service/tasksiteservice.ts
new file mode 100644
index 0000000..3fce08c
--- /dev/null
+++ b/src/modules/report/service/tasksiteservice.ts
@@ -0,0 +1,154 @@
+import { Provide } from '@midwayjs/core';
+import { BaseService } from '@cool-midway/core';
+import { InjectEntityModel } from '@midwayjs/typeorm';
+import { Repository } from 'typeorm';
+import { ReportDailysiteEntity } from '../entity/dailysite';
+import { PushSiteweightEntity } from '../../push/entity/siteweight';
+import { BasicdataSiteEntity } from '../../basicdata/entity/site'
+
+@Provide()
+export class TaskSiteService extends BaseService {
+    @InjectEntityModel(ReportDailysiteEntity)
+    reportDailysiteEntity: Repository<ReportDailysiteEntity>;
+
+    @InjectEntityModel(PushSiteweightEntity)
+    pushSiteweightEntity: Repository<PushSiteweightEntity>;
+
+    @InjectEntityModel(BasicdataSiteEntity)
+    basicdataSiteEntity: Repository<BasicdataSiteEntity>;
+
+    async aggregateDailyData(targetDate: string) {
+        const [year, month, day] = targetDate.split('-').map(Number);
+        const startDateTime = new Date(year, month - 1, day);
+        const end = new Date(startDateTime);
+        end.setHours(24, 0, 0, 0);
+        const endDateTime = end;
+
+        // 1. 鍐呭瓨浼樺寲鏍稿績锛氭彁鍓嶆妸鎵�鏈夌殑鍨冨溇绫诲瀷鍗曚环銆佺⒊鎺掑洜鏁板姞杞藉埌鍐呭瓨瀛楀吀涓紝閬垮厤寰幆鍐呮煡搴�
+        const wasteTypes = await this.nativeQuery('SELECT code, price, carbonFactor, carbonDirection FROM t_basicdata_wastetype');
+        const wasteMap = new Map<string, { price: number; carbonFactor: number; carbonDirection: number }>();
+        for (const w of wasteTypes) {
+            wasteMap.set(w.code, {
+                price: Number(w.price) || 0,
+                carbonFactor: Number(w.carbonFactor) || 0,
+                carbonDirection: Number(w.carbonDirection) || 0
+            });
+        }
+
+        // 2. 鏁版嵁搴撳彧鍋氫竴浠朵簨鎯咃細杩囨护骞舵崬鍑哄綋澶┾�滄敹闆嗙偣鈥濈殑鎵�鏈夋槑缁嗘祦鏁版嵁锛�"杩囨护鏉′欢涓�"锛�
+        // 鏁版嵁搴撳彧璐熻矗锛氱簿鍑嗚繃婊ゅ嚭褰撳ぉ灞炰簬鈥滄敹闆嗙偣鈥濈殑銆佷笖鐪熸浜х敓绉伴噸鐨勬墍鏈夋祦姘�
+        const rawDetails = await this.pushSiteweightEntity.createQueryBuilder('weight')
+            .innerJoin(BasicdataSiteEntity, 'site', 'weight.businessId = site.businessId')
+            .select([
+                'site.id AS siteId',             // 鎷挎渶鏂扮殑鑷ID浣滀负姣忔棩琛ㄧ殑 siteId
+                'site.businessName AS siteName', // 姘歌繙鎷垮綋鍓嶅悗鍙版渶鏂扮殑绔欑偣鍚嶇О锛岄槻姝㈣澶囦紶鏃у悕绉�
+                'weight.weight AS weight',
+                'weight.garbageType AS garbageType'
+            ])
+            .where('site.siteType = :siteType', { siteType: '鏀堕泦鐐�' })
+            .andWhere('weight.uploadTime BETWEEN :start AND :end', { start: startDateTime, end: endDateTime })
+            .getRawMany();
+
+        if (!rawDetails || rawDetails.length === 0) return;
+
+        // 3. 鍐呭瓨鑱氬悎鏍稿績锛氬畾涔夋敮鎸佸姩鎬侀敭鍚嶇疮鍔犵殑瀵硅薄瀛楀吀
+        const siteAggMap = new Map<string, any>();
+
+        // 鏄犲皠琛細灏嗘槑缁嗛噷鐨� garbageType 鏄犲皠鍒颁綘 Entity 閲岀殑瀛楁鍚庣紑
+        const typeKeyMap: Record<string, string> = {
+            'SW60': 'Sw60', 'SW61': 'Sw61', 'SW62': 'Sw62', 'SW63': 'Sw63', 'SW64': 'Sw64',
+            '900-001-S62': 'Sw62001', '900-002-S62': 'Sw62002', '900-003-S62': 'Sw62003',
+            '900-004-S62': 'Sw62004', '900-005-S62': 'Sw62005', '900-006-S62': 'Sw62006', '900-007-S62': 'Sw62007'
+        };
+
+
+        for (const row of rawDetails) {
+            const siteId = row.siteId;
+            const weight = Number(row.weight) || 0;
+            const gType = row.garbageType || 'SW64';
+            const fieldSuffix = typeKeyMap[gType];
+
+            // 鍒濆鍖栬绔欑偣鐨勮仛鍚堢粨鏋�
+            if (!siteAggMap.has(siteId)) {
+                const initData: any = {
+                    date: targetDate,
+                    siteId: siteId,
+                    siteName: row.siteName,
+                    weightTotal: 0, timeTotal: 0, carbonTotal: 0
+                };
+                // 鍔ㄦ�佸垵濮嬪寲瀹借〃閲屽悇瀛愮被鍨嬬殑 0 鍊�
+                Object.values(typeKeyMap).forEach(suffix => {
+                    initData[`weight${suffix}`] = 0;
+                    initData[`time${suffix}`] = 0;
+                    initData[`carbon${suffix}`] = 0;
+                    // 浠� sw62 绯诲垪鏈変及绠楅攢鍞
+                    if (suffix.startsWith('Sw62')) {
+                        initData[`estimatedSales${suffix}`] = 0;
+                    }
+                });
+                siteAggMap.set(siteId, initData);
+            }
+
+            const agg = siteAggMap.get(siteId);
+
+            // 閫氱敤鎬昏绱姞
+            agg.weightTotal += weight;
+            agg.timeTotal += 1; // 绉伴噸娆℃暟 + 1
+
+            // 鎻愬彇璇ュ瀮鍦剧被鍨嬬殑纰虫帓涓庨攢鍞璁$畻瑙勫垯
+            const wasteCfg = wasteMap.get(row.garbageType) || { price: 0, carbonFactor: 0, carbonDirection: 0 };
+
+            // 璁$畻鍗曠瑪纰虫帓
+            let currentCarbon = weight * wasteCfg.carbonFactor;
+            if (wasteCfg.carbonDirection === 1) currentCarbon = -currentCarbon; // 璐熷悜纰虫帓
+            agg.carbonTotal += currentCarbon;
+
+            // 濡傛灉灞炰簬鎴戜滑缁熻鐨勮杞垪瀛愮被鍨嬶紝杩涜绮剧‘褰掔被
+            if (fieldSuffix) {
+                agg[`weight${fieldSuffix}`] += weight;
+                agg[`time${fieldSuffix}`] += 1;
+                agg[`carbon${fieldSuffix}`] += currentCarbon;
+
+                // 濡傛灉鏄� sw62 鍙婂叾瀛愮被锛岄澶栬绠椾及绠楅攢鍞
+                if (fieldSuffix.startsWith('Sw62')) {
+                    const currentSales = weight * wasteCfg.price;
+                    agg[`estimatedSales${fieldSuffix}`] += currentSales;
+                }
+            }
+        }
+
+        // 4. 楂樻�ц兘鍐欏叆锛氳浆鎹负鏁扮粍锛屽埄鐢ㄦ壒閲忔彃鍏� + ON DUPLICATE KEY UPDATE 鎻愪氦缁� MySQL
+        const finalRecords = Array.from(siteAggMap.values()).map(item => {
+            // 娴偣鏁伴槻绮惧害涓㈠け澶勭悊锛屼繚鐣欎袱浣嶅皬鏁�
+            Object.keys(item).forEach(key => {
+                if (typeof item[key] === 'number') {
+                    item[key] = Number(item[key].toFixed(2));
+                }
+            });
+            return item;
+        });
+
+        if (finalRecords.length > 0) {
+            // 1. 鍔ㄦ�佹姄鍙栨墍鏈夌殑閿紝灏嗕富閿�佽仈鍚堝敮涓�閿交搴曚粠鈥滃緟鏇存柊瀛楁鈥濅腑鎺掗櫎
+            // 杩欐牱鑳界‘淇� ON DUPLICATE KEY UPDATE 鍚庨潰鐨勮祴鍊艰鍙ョ粷瀵瑰共鍑�
+            const allUpdateFields = Object.keys(finalRecords[0]).filter(
+                k => k !== 'date' && k !== 'siteId' && k !== 'id'
+            );
+
+            // 2. 渚濈劧鎺ㄨ崘閲囩敤 Chunk 鍒嗘壒锛岃繖鏄簲瀵规捣閲忔暟鎹�/澶氬瓙瀛楁鏈�瀹夊叏銆佹墽琛屾晥鐜囨渶楂樼殑鍋氭硶
+            const chunkSize = 50;
+            for (let i = 0; i < finalRecords.length; i += chunkSize) {
+                const chunk = finalRecords.slice(i, i + chunkSize);
+
+                await this.reportDailysiteEntity.createQueryBuilder()
+                    .insert()
+                    .values(chunk)
+                    // 馃挕 鏄惧紡澹版槑锛氬綋 ['date', 'siteId'] 鍙戠敓鍐茬獊鏃讹紝寮鸿鎶� allUpdateFields 閲岀殑瀛楁鍏ㄩ儴鏇存柊涓�閬�
+                    .orUpdate(allUpdateFields, ['date', 'siteId'])
+                    .execute();
+            }
+        }
+
+        return finalRecords.length;
+    }
+}
\ No newline at end of file

--
Gitblit v1.9.1