/*
  振动传感器振动温度整点报表

  说明：
  1. 存储过程 dbo.sp_InsertVibrationSensorHourlyReport 在整点执行时，默认取「上一完整小时」区间内，
     每个 sensor_id 在 T_VibrationSensor_Data 中 collectTime 最晚的一条（并列时取 objId 较大者）。
  2. reportTime 存该小时起始时间。例如 2025-03-23 09:00:00 表示统计区间 [09:00, 10:00) 的末条数据。
  3. 定时请使用 SQL Server 代理作业（见脚本末尾示例），不要在应用内写 Quartz 定时任务。

  该存储过程无入参：
  - 以「当前服务器时间的整点」为区间右端点（不包含），汇总上一小时。
  - 区间为 [DATEADD(hour,-1,@EndExclusive), @EndExclusive)。
*/

IF OBJECT_ID(N'dbo.T_VibrationSensor_Hourly_Report', N'U') IS NULL
BEGIN
    CREATE TABLE dbo.T_VibrationSensor_Hourly_Report
    (
        objId          BIGINT IDENTITY
            CONSTRAINT PK_T_VibrationSensor_Hourly_Report PRIMARY KEY,
        reportTime     DATETIME     NOT NULL,
        sensor_id      NVARCHAR(64) NOT NULL,
        collectTime    DATETIME     NULL,
        speed          DECIMAL(18, 2) NULL,
        displacement   DECIMAL(18, 2) NULL,
        acceleration   DECIMAL(18, 2) NULL,
        temperature    DECIMAL(18, 2) NULL,
        recodeTime     DATETIME     NOT NULL CONSTRAINT DF_T_VibrationSensor_Hourly_Report_recodeTime DEFAULT GETDATE(),
        remark         VARCHAR(64)  NULL,
        CONSTRAINT UQ_T_VibrationSensor_Hourly_Report_hour_sensor UNIQUE (reportTime, sensor_id)
    );

    CREATE NONCLUSTERED INDEX IX_T_VibrationSensor_Hourly_Report_reportTime
        ON dbo.T_VibrationSensor_Hourly_Report (reportTime DESC);

    EXEC sp_addextendedproperty N'MS_Description', N'振动传感器整点报表（每小时末条采集）', N'SCHEMA', N'dbo', N'TABLE', N'T_VibrationSensor_Hourly_Report';
    EXEC sp_addextendedproperty N'MS_Description', N'主键', N'SCHEMA', N'dbo', N'TABLE', N'T_VibrationSensor_Hourly_Report', N'COLUMN', N'objId';
    EXEC sp_addextendedproperty N'MS_Description', N'报表归属小时（该小时起点）', N'SCHEMA', N'dbo', N'TABLE', N'T_VibrationSensor_Hourly_Report', N'COLUMN', N'reportTime';
    EXEC sp_addextendedproperty N'MS_Description', N'振动传感器ID', N'SCHEMA', N'dbo', N'TABLE', N'T_VibrationSensor_Hourly_Report', N'COLUMN', N'sensor_id';
    EXEC sp_addextendedproperty N'MS_Description', N'该小时内末条记录的采集时间', N'SCHEMA', N'dbo', N'TABLE', N'T_VibrationSensor_Hourly_Report', N'COLUMN', N'collectTime';
    EXEC sp_addextendedproperty N'MS_Description', N'速度(mm/s)', N'SCHEMA', N'dbo', N'TABLE', N'T_VibrationSensor_Hourly_Report', N'COLUMN', N'speed';
    EXEC sp_addextendedproperty N'MS_Description', N'位移(um)', N'SCHEMA', N'dbo', N'TABLE', N'T_VibrationSensor_Hourly_Report', N'COLUMN', N'displacement';
    EXEC sp_addextendedproperty N'MS_Description', N'加速度(g)', N'SCHEMA', N'dbo', N'TABLE', N'T_VibrationSensor_Hourly_Report', N'COLUMN', N'acceleration';
    EXEC sp_addextendedproperty N'MS_Description', N'温度(℃)', N'SCHEMA', N'dbo', N'TABLE', N'T_VibrationSensor_Hourly_Report', N'COLUMN', N'temperature';
    EXEC sp_addextendedproperty N'MS_Description', N'写入报表时间', N'SCHEMA', N'dbo', N'TABLE', N'T_VibrationSensor_Hourly_Report', N'COLUMN', N'recodeTime';
    EXEC sp_addextendedproperty N'MS_Description', N'备注', N'SCHEMA', N'dbo', N'TABLE', N'T_VibrationSensor_Hourly_Report', N'COLUMN', N'remark';
END
GO

IF OBJECT_ID(N'dbo.sp_InsertVibrationSensorHourlyReport', N'P') IS NOT NULL
    DROP PROCEDURE dbo.sp_InsertVibrationSensorHourlyReport;
GO

CREATE PROCEDURE dbo.sp_InsertVibrationSensorHourlyReport
AS
BEGIN
    SET NOCOUNT ON;
    SET XACT_ABORT ON;

    DECLARE @EndExclusive DATETIME = DATEADD(HOUR, DATEDIFF(HOUR, 0, GETDATE()), 0);
    DECLARE @StartInclusive DATETIME = DATEADD(HOUR, -1, @EndExclusive);
    DECLARE @ReportTime DATETIME = @StartInclusive;

    ;WITH Ranked AS (
        SELECT d.objId,
               d.sensor_id,
               d.collectTime,
               d.speed,
               d.displacement,
               d.acceleration,
               d.temperature,
               d.remark,
               ROW_NUMBER() OVER (
                   PARTITION BY d.sensor_id
                   ORDER BY d.collectTime DESC, d.objId DESC
               ) AS rn
        FROM dbo.T_VibrationSensor_Data d
        WHERE d.collectTime >= @StartInclusive
          AND d.collectTime < @EndExclusive
    )
    INSERT INTO dbo.T_VibrationSensor_Hourly_Report (reportTime, sensor_id, collectTime, speed, displacement,
                                                     acceleration, temperature, recodeTime, remark)
    SELECT @ReportTime,
           r.sensor_id,
           @ReportTime,
           r.speed,
           r.displacement,
           r.acceleration,
           r.temperature,
           @ReportTime,
           r.remark
    FROM Ranked r
    WHERE r.rn = 1
      AND NOT EXISTS (
            SELECT 1
            FROM dbo.T_VibrationSensor_Hourly_Report x
            WHERE x.reportTime = @ReportTime
              AND x.sensor_id = r.sensor_id
        );

    -- 清理原始明细：删除 recodeTime 三个月前的数据
    DELETE FROM dbo.T_VibrationSensor_Data
    WHERE recodeTime < DATEADD(MONTH, -3, @EndExclusive);
END
GO

IF NOT EXISTS (
    SELECT 1
    FROM sys.extended_properties ep
    WHERE ep.class = 1
      AND ep.major_id = OBJECT_ID(N'dbo.sp_InsertVibrationSensorHourlyReport')
      AND ep.minor_id = 0
      AND ep.name = N'MS_Description'
)
    EXEC sp_addextendedproperty N'MS_Description',
         N'按小时汇总振动温度：无入参，固定按当前整点回算上一小时，取各传感器 collectTime 最后一条写入整点报表。',
         N'SCHEMA', N'dbo', N'PROCEDURE', N'sp_InsertVibrationSensorHourlyReport';
ELSE
    EXEC sp_updateextendedproperty N'MS_Description',
         N'按小时汇总振动温度：无入参，固定按当前整点回算上一小时，取各传感器 collectTime 最后一条写入整点报表。',
         N'SCHEMA', N'dbo', N'PROCEDURE', N'sp_InsertVibrationSensorHourlyReport';
GO

/*
--------------------------------------------------------------------
若依菜单与权限（在「菜单管理」中自行添加，或执行类似语句，注意 menu_id / parent_id 勿冲突）

-- 目录或菜单 C：url = /system/TVibrationSensorHourlyReport
-- perms: system:TVibrationSensorHourlyReport:view / list / export

INSERT INTO sys_menu (menu_name, parent_id, order_num, url, menu_type, visible, perms, icon, create_by, create_time, remark)
VALUES (N'振动整点报表', <父菜单ID>, 10, N'/system/TVibrationSensorHourlyReport', N'C', N'0', N'system:TVibrationSensorHourlyReport:view', N'fa fa-table', N'admin', GETDATE(), N'');

-- 将返回的 menu_id 记入 @mid，再插入按钮权限 F：
-- system:TVibrationSensorHourlyReport:list
-- system:TVibrationSensorHourlyReport:export
--------------------------------------------------------------------
*/

/*
--------------------------------------------------------------------
SQL Server 代理作业（在 SSMS 中图形化创建，或按需执行以下模板并修改作业名）

USE msdb;
GO
EXEC dbo.sp_add_job
    @job_name = N'振动传感器整点报表',
    @enabled = 1,
    @description = N'每小时执行 sp_InsertVibrationSensorHourlyReport';
GO
EXEC dbo.sp_add_jobstep
    @job_name = N'振动传感器整点报表',
    @step_name = N'写入整点报表',
    @subsystem = N'TSQL',
    @database_name = N'你的数据库名',
    @command = N'EXEC dbo.sp_InsertVibrationSensorHourlyReport;',
    @retry_attempts = 2,
    @retry_interval = 5;
GO
EXEC dbo.sp_add_schedule
    @schedule_name = N'每小时整点',
    @freq_type = 4,
    @freq_interval = 1,
    @freq_subday_type = 8,
    @freq_subday_interval = 1,
    @active_start_time = 0;
GO
EXEC dbo.sp_attach_schedule
    @job_name = N'振动传感器整点报表',
    @schedule_name = N'每小时整点';
GO
EXEC dbo.sp_add_jobserver
    @job_name = N'振动传感器整点报表';
GO

注意：@active_start_time = 0 表示从 00:00:00 起每 1 小时一次（与 freq_subday 组合为每小时）。
若环境与版本不同，请用 SSMS「新建作业」→「步骤」执行上述 EXEC →「计划」选「重复执行」每小时、在整点触发更直观。
--------------------------------------------------------------------
*/

/*
--------------------------------------------------------------------
振动整点报表随机测试数据（可直接执行）
说明：
1) 生成“今天 00:00 ~ 当前小时前一小时”之间，2 个传感器、每小时 1 条随机整点报表数据。
2) 已存在同小时同传感器数据会跳过（依赖唯一键 reportTime + sensor_id）。
--------------------------------------------------------------------
*/
DECLARE @TodayStart DATETIME = DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 0);
DECLARE @CurrentHour DATETIME = DATEADD(HOUR, DATEDIFF(HOUR, 0, GETDATE()), 0);

;WITH H AS (
    SELECT @TodayStart AS reportTime
    UNION ALL
    SELECT DATEADD(HOUR, 1, reportTime)
    FROM H
    WHERE DATEADD(HOUR, 1, reportTime) < @CurrentHour
),
S AS (
    SELECT N'VS-001' AS sensor_id
    UNION ALL
    SELECT N'VS-002'
),
R AS (
    SELECT
        h.reportTime,
        s.sensor_id,
        h.reportTime AS collectTime,
        CAST((ABS(CHECKSUM(NEWID())) % 1200) / 100.0 AS DECIMAL(18, 2)) AS speed,
        CAST((ABS(CHECKSUM(NEWID())) % 5000) / 100.0 AS DECIMAL(18, 2)) AS displacement,
        CAST((ABS(CHECKSUM(NEWID())) % 300) / 100.0 AS DECIMAL(18, 2)) AS acceleration,
        CAST((ABS(CHECKSUM(NEWID())) % 7000) / 100.0 AS DECIMAL(18, 2)) AS temperature
    FROM H h
    CROSS JOIN S s
)
INSERT INTO dbo.T_VibrationSensor_Hourly_Report
(
    reportTime, sensor_id, collectTime, speed, displacement, acceleration, temperature, recodeTime, remark
)
SELECT
    r.reportTime,
    r.sensor_id,
    r.collectTime,
    r.speed,
    r.displacement,
    r.acceleration,
    r.temperature,
    r.reportTime,
    N'随机测试数据'
FROM R r
WHERE NOT EXISTS (
    SELECT 1
    FROM dbo.T_VibrationSensor_Hourly_Report x
    WHERE x.reportTime = r.reportTime
      AND x.sensor_id = r.sensor_id
)
OPTION (MAXRECURSION 1000);
GO
