==============================================================================
### 0. charge_records 现有行（cabinet_id / start_time / energy）
SQL> SELECT cr.id, cr.cabinet_id, c.name, cr.start_time, cr.end_time, cr.energy FROM charge_records cr LEFT JOIN cabinets c ON cr.cabinet_id = c.id ORDER BY cr.start_time DESC, cr.cabinet_id
     id | cabinet_id | name | start_time | end_time | energy
     10 | 1 | 联调测试柜 | 2026-09-20 10:00:00 | 2026-09-20 12:00:00 | 220.00
     12 | 2 | 能耗验证临时柜A | 2026-09-20 10:00:00 | 2026-09-20 12:00:00 | 330.00
     14 | 3 | 能耗验证临时柜B | 2026-09-20 10:00:00 | 2026-09-20 12:00:00 | 440.00
     11 | 1 | 联调测试柜 | 2026-09-19 10:00:00 | 2026-09-19 12:00:00 | 1000.00
     13 | 2 | 能耗验证临时柜A | 2026-09-19 10:00:00 | 2026-09-19 12:00:00 | 2000.00
     15 | 3 | 能耗验证临时柜B | 2026-09-19 10:00:00 | 2026-09-19 12:00:00 | 3000.00
     => 6 行
==============================================================================
### 1a. summary today（总管理员，无组织条件）
SQL> SELECT CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED) FROM charge_records cr WHERE cr.start_time >= CURDATE() AND cr.start_time < CURDATE() + INTERVAL 1 DAY
     CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED)
     990
     => 1 行
==============================================================================
### 1b. summary month（总管理员）
SQL> SELECT CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED) FROM charge_records cr WHERE cr.start_time >= DATE_FORMAT(CURDATE(), '%Y-%m-01') AND cr.start_time < DATE_FORMAT(CURDATE() + INTERVAL 1 MONTH, '%Y-%m-01')
     CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED)
     6990
     => 1 行
==============================================================================
### 1c. summary today（组织 1 隔离）
SQL> SELECT CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED) FROM charge_records cr WHERE cr.start_time >= CURDATE() AND cr.start_time < CURDATE() + INTERVAL 1 DAY AND cr.cabinet_id IN (SELECT c.id FROM cabinets c JOIN projects p ON c.project_id = p.id WHERE p.organization_id = 1)
     CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED)
     990
     => 1 行
==============================================================================
### 1d. summary today（不存在组织 999 ⇒ 必须为空集 0）
SQL> SELECT CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED) FROM charge_records cr WHERE cr.start_time >= CURDATE() AND cr.start_time < CURDATE() + INTERVAL 1 DAY AND cr.cabinet_id IN (SELECT c.id FROM cabinets c JOIN projects p ON c.project_id = p.id WHERE p.organization_id = 999)
     CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED)
     0
     => 1 行
==============================================================================
### 1e. summary today（客户侧无组织 ⇒ AND 1=0）
SQL> SELECT CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED) FROM charge_records cr WHERE cr.start_time >= CURDATE() AND cr.start_time < CURDATE() + INTERVAL 1 DAY AND 1=0
     CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED)
     0
     => 1 行
==============================================================================
### 2a. cabinets 排行（总管理员）
SQL> SELECT c.id, COALESCE(c.abstract_id, '') AS abstract_id, c.name, CAST(COALESCE(cr_today.today_total, 0) AS SIGNED) AS today_energy, CAST(COALESCE(cr_month.month_total, 0) AS SIGNED) AS month_energy FROM cabinets c LEFT JOIN ( SELECT cr.cabinet_id, SUM(cr.energy) AS today_total FROM charge_records cr WHERE cr.start_time >= CURDATE() AND cr.start_time < CURDATE() + INTERVAL 1 DAY GROUP BY cr.cabinet_id ) cr_today ON c.id = cr_today.cabinet_id LEFT JOIN ( SELECT cr.cabinet_id, SUM(cr.energy) AS month_total FROM charge_records cr WHERE cr.start_time >= DATE_FORMAT(CURDATE(), '%Y-%m-01') AND cr.start_time < DATE_FORMAT(CURDATE() + INTERVAL 1 MONTH, '%Y-%m-01') GROUP BY cr.cabinet_id ) cr_month ON c.id = cr_month.cabinet_id ORDER BY today_energy DESC, c.id ASC
     id | abstract_id | name | today_energy | month_energy
     3 | 99-00000002 | 能耗验证临时柜B | 440 | 3440
     2 | 99-00000001 | 能耗验证临时柜A | 330 | 2330
     1 |  | 联调测试柜 | 220 | 1220
     => 3 行
==============================================================================
### 2b. cabinets 排行（组织 1 隔离）
SQL> SELECT c.id, COALESCE(c.abstract_id, '') AS abstract_id, c.name, CAST(COALESCE(cr_today.today_total, 0) AS SIGNED) AS today_energy, CAST(COALESCE(cr_month.month_total, 0) AS SIGNED) AS month_energy FROM cabinets c LEFT JOIN ( SELECT cr.cabinet_id, SUM(cr.energy) AS today_total FROM charge_records cr WHERE cr.start_time >= CURDATE() AND cr.start_time < CURDATE() + INTERVAL 1 DAY GROUP BY cr.cabinet_id ) cr_today ON c.id = cr_today.cabinet_id LEFT JOIN ( SELECT cr.cabinet_id, SUM(cr.energy) AS month_total FROM charge_records cr WHERE cr.start_time >= DATE_FORMAT(CURDATE(), '%Y-%m-01') AND cr.start_time < DATE_FORMAT(CURDATE() + INTERVAL 1 MONTH, '%Y-%m-01') GROUP BY cr.cabinet_id ) cr_month ON c.id = cr_month.cabinet_id JOIN projects p ON c.project_id = p.id AND p.organization_id = 1 ORDER BY today_energy DESC, c.id ASC
     id | abstract_id | name | today_energy | month_energy
     3 | 99-00000002 | 能耗验证临时柜B | 440 | 3440
     2 | 99-00000001 | 能耗验证临时柜A | 330 | 2330
     1 |  | 联调测试柜 | 220 | 1220
     => 3 行
==============================================================================
### 2c. cabinets 排行（客户侧无组织 ⇒ JOIN projects p ON 1=0 ⇒ 空）
SQL> SELECT c.id, COALESCE(c.abstract_id, '') AS abstract_id, c.name, CAST(COALESCE(cr_today.today_total, 0) AS SIGNED) AS today_energy, CAST(COALESCE(cr_month.month_total, 0) AS SIGNED) AS month_energy FROM cabinets c LEFT JOIN ( SELECT cr.cabinet_id, SUM(cr.energy) AS today_total FROM charge_records cr WHERE cr.start_time >= CURDATE() AND cr.start_time < CURDATE() + INTERVAL 1 DAY GROUP BY cr.cabinet_id ) cr_today ON c.id = cr_today.cabinet_id LEFT JOIN ( SELECT cr.cabinet_id, SUM(cr.energy) AS month_total FROM charge_records cr WHERE cr.start_time >= DATE_FORMAT(CURDATE(), '%Y-%m-01') AND cr.start_time < DATE_FORMAT(CURDATE() + INTERVAL 1 MONTH, '%Y-%m-01') GROUP BY cr.cabinet_id ) cr_month ON c.id = cr_month.cabinet_id JOIN projects p ON 1=0 ORDER BY today_energy DESC, c.id ASC
     id | abstract_id | name | today_energy | month_energy
     => 0 行
==============================================================================
### 3a. projects 统计（总管理员）
SQL> SELECT p.id, p.name, COUNT(DISTINCT c.id) AS cabinet_count, CAST(COALESCE(SUM(cr_today.today_total), 0) AS SIGNED) AS today_energy, CAST(COALESCE(SUM(cr_month.month_total), 0) AS SIGNED) AS month_energy FROM projects p LEFT JOIN cabinets c ON p.id = c.project_id LEFT JOIN ( SELECT cr.cabinet_id, SUM(cr.energy) AS today_total FROM charge_records cr WHERE cr.start_time >= CURDATE() AND cr.start_time < CURDATE() + INTERVAL 1 DAY GROUP BY cr.cabinet_id ) cr_today ON c.id = cr_today.cabinet_id LEFT JOIN ( SELECT cr.cabinet_id, SUM(cr.energy) AS month_total FROM charge_records cr WHERE cr.start_time >= DATE_FORMAT(CURDATE(), '%Y-%m-01') AND cr.start_time < DATE_FORMAT(CURDATE() + INTERVAL 1 MONTH, '%Y-%m-01') GROUP BY cr.cabinet_id ) cr_month ON c.id = cr_month.cabinet_id GROUP BY p.id, p.name ORDER BY today_energy DESC, p.id ASC
     id | name | cabinet_count | today_energy | month_energy
     2 | 能耗验证临时项目 | 2 | 770 | 5770
     1 | 联调测试项目 | 1 | 220 | 1220
     => 2 行
==============================================================================
### 3b. projects 统计（组织 1 隔离）
SQL> SELECT p.id, p.name, COUNT(DISTINCT c.id) AS cabinet_count, CAST(COALESCE(SUM(cr_today.today_total), 0) AS SIGNED) AS today_energy, CAST(COALESCE(SUM(cr_month.month_total), 0) AS SIGNED) AS month_energy FROM projects p LEFT JOIN cabinets c ON p.id = c.project_id LEFT JOIN ( SELECT cr.cabinet_id, SUM(cr.energy) AS today_total FROM charge_records cr WHERE cr.start_time >= CURDATE() AND cr.start_time < CURDATE() + INTERVAL 1 DAY GROUP BY cr.cabinet_id ) cr_today ON c.id = cr_today.cabinet_id LEFT JOIN ( SELECT cr.cabinet_id, SUM(cr.energy) AS month_total FROM charge_records cr WHERE cr.start_time >= DATE_FORMAT(CURDATE(), '%Y-%m-01') AND cr.start_time < DATE_FORMAT(CURDATE() + INTERVAL 1 MONTH, '%Y-%m-01') GROUP BY cr.cabinet_id ) cr_month ON c.id = cr_month.cabinet_id WHERE p.organization_id = 1 GROUP BY p.id, p.name ORDER BY today_energy DESC, p.id ASC
     id | name | cabinet_count | today_energy | month_energy
     2 | 能耗验证临时项目 | 2 | 770 | 5770
     1 | 联调测试项目 | 1 | 220 | 1220
     => 2 行
==============================================================================
### 3c. projects 统计（客户侧无组织 ⇒ WHERE 1=0）
SQL> SELECT p.id, p.name, COUNT(DISTINCT c.id) AS cabinet_count, CAST(COALESCE(SUM(cr_today.today_total), 0) AS SIGNED) AS today_energy, CAST(COALESCE(SUM(cr_month.month_total), 0) AS SIGNED) AS month_energy FROM projects p LEFT JOIN cabinets c ON p.id = c.project_id LEFT JOIN ( SELECT cr.cabinet_id, SUM(cr.energy) AS today_total FROM charge_records cr WHERE cr.start_time >= CURDATE() AND cr.start_time < CURDATE() + INTERVAL 1 DAY GROUP BY cr.cabinet_id ) cr_today ON c.id = cr_today.cabinet_id LEFT JOIN ( SELECT cr.cabinet_id, SUM(cr.energy) AS month_total FROM charge_records cr WHERE cr.start_time >= DATE_FORMAT(CURDATE(), '%Y-%m-01') AND cr.start_time < DATE_FORMAT(CURDATE() + INTERVAL 1 MONTH, '%Y-%m-01') GROUP BY cr.cabinet_id ) cr_month ON c.id = cr_month.cabinet_id WHERE 1=0 GROUP BY p.id, p.name ORDER BY today_energy DESC, p.id ASC
     id | name | cabinet_count | today_energy | month_energy
     => 0 行
==============================================================================
### 4a. trend granularity=day（总管理员, range=30）
SQL> SELECT DATE_FORMAT(cr.start_time, '%Y-%m-%d') AS period, CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED) AS energy, CAST(COUNT(*) AS SIGNED) AS charge_count FROM charge_records cr WHERE cr.start_time >= DATE_SUB(CURDATE(), INTERVAL 30 DAY) GROUP BY period ORDER BY period ASC
     period | energy | charge_count
     2026-09-19 | 6000 | 3
     2026-09-20 | 990 | 3
     => 2 行
==============================================================================
### 4b. trend granularity=week（range=30）
SQL> SELECT DATE_FORMAT(cr.start_time, '%Y-%u') AS period, CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED) AS energy, CAST(COUNT(*) AS SIGNED) AS charge_count FROM charge_records cr WHERE cr.start_time >= DATE_SUB(CURDATE(), INTERVAL 30 DAY) GROUP BY period ORDER BY period ASC
     period | energy | charge_count
     2026-38 | 6990 | 6
     => 1 行
==============================================================================
### 4c. trend granularity=month（range=30）
SQL> SELECT DATE_FORMAT(cr.start_time, '%Y-%m') AS period, CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED) AS energy, CAST(COUNT(*) AS SIGNED) AS charge_count FROM charge_records cr WHERE cr.start_time >= DATE_SUB(CURDATE(), INTERVAL 30 DAY) GROUP BY period ORDER BY period ASC
     period | energy | charge_count
     2026-09 | 6990 | 6
     => 1 行
==============================================================================
### 4d. trend granularity=day（组织 1 隔离）
SQL> SELECT DATE_FORMAT(cr.start_time, '%Y-%m-%d') AS period, CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED) AS energy, CAST(COUNT(*) AS SIGNED) AS charge_count FROM charge_records cr WHERE cr.start_time >= DATE_SUB(CURDATE(), INTERVAL 30 DAY) AND cr.cabinet_id IN (SELECT c.id FROM cabinets c JOIN projects p ON c.project_id = p.id WHERE p.organization_id = 1) GROUP BY period ORDER BY period ASC
     period | energy | charge_count
     2026-09-19 | 6000 | 3
     2026-09-20 | 990 | 3
     => 2 行
==============================================================================
### 4e. trend range=-5 **未经夹紧**（DATE_SUB 变成未来 5 天 ⇒ 必然空）
SQL> SELECT DATE_FORMAT(cr.start_time, '%Y-%m-%d') AS period, CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED) AS energy, CAST(COUNT(*) AS SIGNED) AS charge_count FROM charge_records cr WHERE cr.start_time >= DATE_SUB(CURDATE(), INTERVAL -5 DAY) GROUP BY period ORDER BY period ASC
     period | energy | charge_count
     => 0 行
==============================================================================
### 4f. trend range=-5 经 clamp_range ⇒ 1（应含昨天+今天）
SQL> SELECT DATE_FORMAT(cr.start_time, '%Y-%m-%d') AS period, CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED) AS energy, CAST(COUNT(*) AS SIGNED) AS charge_count FROM charge_records cr WHERE cr.start_time >= DATE_SUB(CURDATE(), INTERVAL 1 DAY) GROUP BY period ORDER BY period ASC
     period | energy | charge_count
     2026-09-19 | 6000 | 3
     2026-09-20 | 990 | 3
     => 2 行
==============================================================================
### 5a. export 明细（无筛选）
SQL> SELECT DATE(cr.start_time) AS stat_date, cr.cabinet_id, c.name AS cabinet_name, p.name AS project_name, CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED) AS total_energy, CAST(COUNT(*) AS SIGNED) AS charge_count FROM charge_records cr LEFT JOIN cabinets c ON cr.cabinet_id = c.id LEFT JOIN projects p ON c.project_id = p.id WHERE cr.start_time IS NOT NULL GROUP BY DATE(cr.start_time), cr.cabinet_id, c.name, p.name ORDER BY stat_date DESC, cr.cabinet_id ASC LIMIT 100000
     stat_date | cabinet_id | cabinet_name | project_name | total_energy | charge_count
     2026-09-20 | 1 | 联调测试柜 | 联调测试项目 | 220 | 1
     2026-09-20 | 2 | 能耗验证临时柜A | 能耗验证临时项目 | 330 | 1
     2026-09-20 | 3 | 能耗验证临时柜B | 能耗验证临时项目 | 440 | 1
     2026-09-19 | 1 | 联调测试柜 | 联调测试项目 | 1000 | 1
     2026-09-19 | 2 | 能耗验证临时柜A | 能耗验证临时项目 | 2000 | 1
     2026-09-19 | 3 | 能耗验证临时柜B | 能耗验证临时项目 | 3000 | 1
     => 6 行
==============================================================================
### 5b. export 明细（end_date=今天必须含当天）
SQL> SELECT DATE(cr.start_time) AS stat_date, cr.cabinet_id, c.name AS cabinet_name, p.name AS project_name, CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED) AS total_energy, CAST(COUNT(*) AS SIGNED) AS charge_count FROM charge_records cr LEFT JOIN cabinets c ON cr.cabinet_id = c.id LEFT JOIN projects p ON c.project_id = p.id WHERE cr.start_time IS NOT NULL AND cr.start_time >= DATE_SUB(CURDATE(), INTERVAL 1 DAY) AND cr.start_time < DATE_ADD(CURDATE(), INTERVAL 1 DAY) GROUP BY DATE(cr.start_time), cr.cabinet_id, c.name, p.name ORDER BY stat_date DESC, cr.cabinet_id ASC LIMIT 100000
     stat_date | cabinet_id | cabinet_name | project_name | total_energy | charge_count
     2026-09-20 | 1 | 联调测试柜 | 联调测试项目 | 220 | 1
     2026-09-20 | 2 | 能耗验证临时柜A | 能耗验证临时项目 | 330 | 1
     2026-09-20 | 3 | 能耗验证临时柜B | 能耗验证临时项目 | 440 | 1
     2026-09-19 | 1 | 联调测试柜 | 联调测试项目 | 1000 | 1
     2026-09-19 | 2 | 能耗验证临时柜A | 能耗验证临时项目 | 2000 | 1
     2026-09-19 | 3 | 能耗验证临时柜B | 能耗验证临时项目 | 3000 | 1
     => 6 行
==============================================================================
### 5c. export 明细（组织 1 隔离）
SQL> SELECT DATE(cr.start_time) AS stat_date, cr.cabinet_id, c.name AS cabinet_name, p.name AS project_name, CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED) AS total_energy, CAST(COUNT(*) AS SIGNED) AS charge_count FROM charge_records cr LEFT JOIN cabinets c ON cr.cabinet_id = c.id LEFT JOIN projects p ON c.project_id = p.id WHERE cr.start_time IS NOT NULL AND cr.cabinet_id IN (SELECT c.id FROM cabinets c JOIN projects p ON c.project_id = p.id WHERE p.organization_id = 1) GROUP BY DATE(cr.start_time), cr.cabinet_id, c.name, p.name ORDER BY stat_date DESC, cr.cabinet_id ASC LIMIT 100000
     stat_date | cabinet_id | cabinet_name | project_name | total_energy | charge_count
     2026-09-20 | 1 | 联调测试柜 | 联调测试项目 | 220 | 1
     2026-09-20 | 2 | 能耗验证临时柜A | 能耗验证临时项目 | 330 | 1
     2026-09-20 | 3 | 能耗验证临时柜B | 能耗验证临时项目 | 440 | 1
     2026-09-19 | 1 | 联调测试柜 | 联调测试项目 | 1000 | 1
     2026-09-19 | 2 | 能耗验证临时柜A | 能耗验证临时项目 | 2000 | 1
     2026-09-19 | 3 | 能耗验证临时柜B | 能耗验证临时项目 | 3000 | 1
     => 6 行
==============================================================================
### 9. 对照：energy_stats 表行数（已无人读写）
SQL> SELECT COUNT(*) AS energy_stats_rows FROM energy_stats
     energy_stats_rows
     0
     => 1 行
