같은 cafe24 DB의 다른 시스템 테이블과 혼동/충돌을 막기 위해 5개 테이블을 leak_ 네임스페이스로 격리: sensor_log→leak_sensor_log, sensor_status→leak_sensor_status, sensor_metric→leak_sensor_metric, sms_log→leak_sms_log, ingest_nonce→leak_ingest_nonce (leak_incident/leak_photo는 이미 접두사). 컬럼명은 불변(sensor_id, is_leak 등) — 테이블명만 변경. sql/migration_add_leak.sql, migration_drop_leak.sql 삭제: 리네임 후 구버전 무접두 테이블 대상이라 실행 즉시 에러. 신규 설치는 schema_sht30.sql 하나로 완결. BREAKING CHANGE: 기존 무접두 테이블 배포와 호환 안 됨(신규 설치 전용). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01821XP36Q6t9eWKTPSEumnC
166 lines
9.1 KiB
SQL
166 lines
9.1 KiB
SQL
-- =============================================================================
|
|
-- schema_sht30.sql - 통합 모니터링 시스템 신규 설치용 스키마
|
|
-- (1번 모듈=누수+카메라 sensor_id=1, 2번 모듈=SHT30 온습도 sensor_id=2 — 단일 서버)
|
|
--
|
|
-- 신규 설치: 이 파일 하나만 실행합니다.
|
|
-- SOURCE /path/to/sql/schema_sht30.sql;
|
|
--
|
|
-- v2607: 모든 테이블에 leak_ 접두사 적용 — 같은 DB 의 다른(비관련) 테이블과 격리.
|
|
-- (구버전 무접두 테이블 대상 마이그레이션 파일들은 리네임과 함께 폐기 — git 이력 참고.)
|
|
--
|
|
-- 이 파일은 두 모듈을 하나의 서버에서 받는 "통합" 스키마입니다. 포함 요소:
|
|
-- - 온습도(SHT30): leak_sensor_metric 테이블 + 서버 임계 판정(config.php METRIC_*).
|
|
-- - 누수(leak): leak_sensor_log.is_leak / leak_sensor_status.is_leak 컬럼,
|
|
-- event_type 의 leak_alert / leak_recovery,
|
|
-- leak_incident(사고 추적) + leak_photo(사진 메타) 테이블.
|
|
-- 임계/누수 경보 SMS 발송은 서버 PHP(api/sensor_data.php + sms_send.php)에서 수행하며,
|
|
-- 발송 이력은 leak_sms_log 에 남깁니다(leak_sms_log 기준 쿨다운/복구 판정 — 별도 경보 상태 테이블 없음).
|
|
-- 누수 사진 실제 파일은 php/var/photos/ 에 저장되고, leak_photo 에는 메타만 둡니다.
|
|
-- =============================================================================
|
|
|
|
SET NAMES utf8mb4;
|
|
SET time_zone = '+09:00';
|
|
|
|
CREATE TABLE IF NOT EXISTS `leak_sensor_log` (
|
|
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
`device_id` VARCHAR(64) NOT NULL COMMENT 'STM32 기기 ID',
|
|
`device_location` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '설치 위치',
|
|
`sensor_id` TINYINT UNSIGNED NOT NULL COMMENT '센서 번호',
|
|
`sensor_name` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '센서 이름',
|
|
`event_type` ENUM('startup','periodic','leak_alert','leak_recovery')
|
|
NOT NULL DEFAULT 'periodic',
|
|
`is_leak` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '누수 여부 (1=누수, 0=정상)',
|
|
`sensor_time` DATETIME NOT NULL COMMENT '센서 측정 시각 (기기 기준)',
|
|
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'DB 저장 시각',
|
|
|
|
PRIMARY KEY (`id`),
|
|
INDEX `idx_sensor_time` (`sensor_time`),
|
|
INDEX `idx_device_sensor` (`device_id`, `sensor_id`),
|
|
INDEX `idx_event_type` (`event_type`, `created_at`)
|
|
) ENGINE=InnoDB
|
|
DEFAULT CHARSET=utf8mb4
|
|
COLLATE=utf8mb4_unicode_ci
|
|
COMMENT='센서 이벤트 전체 로그';
|
|
|
|
CREATE TABLE IF NOT EXISTS `leak_sensor_status` (
|
|
`sensor_id` TINYINT UNSIGNED NOT NULL,
|
|
`device_id` VARCHAR(64) NOT NULL,
|
|
`sensor_name` VARCHAR(100) NOT NULL DEFAULT '',
|
|
`device_location` VARCHAR(100) NOT NULL DEFAULT '',
|
|
`last_event_type` VARCHAR(20) NOT NULL DEFAULT '',
|
|
`is_leak` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '최신 누수 상태 (1=누수, 0=정상)',
|
|
`last_seen` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
ON UPDATE CURRENT_TIMESTAMP,
|
|
`offline_alerted` TINYINT(1) NOT NULL DEFAULT 0
|
|
COMMENT '오프라인 알림 발송 여부 (1=발송됨, 0=미발송)',
|
|
|
|
PRIMARY KEY (`sensor_id`)
|
|
) ENGINE=InnoDB
|
|
DEFAULT CHARSET=utf8mb4
|
|
COLLATE=utf8mb4_unicode_ci
|
|
COMMENT='센서별 최신 상태 (업서트)';
|
|
|
|
CREATE TABLE IF NOT EXISTS `leak_sensor_metric` (
|
|
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
`device_id` VARCHAR(64) NOT NULL DEFAULT '',
|
|
`device_location` VARCHAR(100) NOT NULL DEFAULT '',
|
|
`sensor_id` TINYINT UNSIGNED NOT NULL,
|
|
`sensor_name` VARCHAR(100) NOT NULL DEFAULT '',
|
|
`metric_type` VARCHAR(30) NOT NULL DEFAULT 'sht30',
|
|
`temperature_c` DECIMAL(6,2) NULL,
|
|
`humidity_percent` DECIMAL(5,2) NULL,
|
|
`metric_status` VARCHAR(30) NOT NULL DEFAULT 'normal'
|
|
COMMENT '서버 임계 판정 결과: normal/high_temp/low_temp/high_humidity/low_humidity/condensation_risk',
|
|
`measured_at` DATETIME NOT NULL,
|
|
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
PRIMARY KEY (`id`),
|
|
INDEX `idx_sensor_measured` (`sensor_id`, `measured_at`),
|
|
INDEX `idx_device_sensor` (`device_id`, `sensor_id`),
|
|
INDEX `idx_metric_type` (`metric_type`, `created_at`),
|
|
INDEX `idx_status_measured` (`metric_status`, `measured_at`)
|
|
) ENGINE=InnoDB
|
|
DEFAULT CHARSET=utf8mb4
|
|
COLLATE=utf8mb4_unicode_ci
|
|
COMMENT='SHT30 온습도 측정 이력 (온도/습도/임계 상태)';
|
|
|
|
CREATE TABLE IF NOT EXISTS `leak_sms_log` (
|
|
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
`sensor_id` TINYINT UNSIGNED NOT NULL,
|
|
`sensor_name` VARCHAR(100) NOT NULL DEFAULT '',
|
|
`device_id` VARCHAR(64) NOT NULL DEFAULT '',
|
|
`message` TEXT NOT NULL,
|
|
`receivers` VARCHAR(500) NOT NULL COMMENT '쉼표 구분 수신자 번호',
|
|
`status` ENUM('success','fail') NOT NULL,
|
|
`result_code` VARCHAR(20) NOT NULL DEFAULT '',
|
|
`result_message` VARCHAR(255) NOT NULL DEFAULT '',
|
|
`sent_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
PRIMARY KEY (`id`),
|
|
INDEX `idx_sent_at` (`sent_at`),
|
|
INDEX `idx_sensor_sent` (`sensor_id`, `sent_at`)
|
|
) ENGINE=InnoDB
|
|
DEFAULT CHARSET=utf8mb4
|
|
COLLATE=utf8mb4_unicode_ci
|
|
COMMENT='SMS 발송 이력 (온습도 임계 경보/복구, 장비 오프라인/복구)';
|
|
|
|
-- 수집 재전송(replay) 멱등 nonce. api/sensor_data.php 가 인증·신선도를 통과한 요청의
|
|
-- 서명(X-Signature)을 여기에 원자적으로 기록한다. 시계오차 창(INGEST_MAX_CLOCK_SKEW_SEC)
|
|
-- 안에서 같은 서명 본문이 다시 오면(펌웨어 재시도/캡처 재생) 중복 저장·중복 SMS 없이
|
|
-- 멱등 처리된다. 오래된 행은 서버가 주기적으로 정리한다(이 테이블이 없어도 수집은
|
|
-- 동작하며 멱등 방어만 비활성화된다 — graceful degrade).
|
|
CREATE TABLE IF NOT EXISTS `leak_ingest_nonce` (
|
|
`signature` VARCHAR(128) CHARACTER SET ascii COLLATE ascii_bin NOT NULL
|
|
COMMENT '요청 서명(X-Signature) — 멱등 키',
|
|
`seen_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
COMMENT '최초 수신 시각(정리 기준)',
|
|
|
|
PRIMARY KEY (`signature`),
|
|
INDEX `idx_seen_at` (`seen_at`)
|
|
) ENGINE=InnoDB
|
|
DEFAULT CHARSET=utf8mb4
|
|
COLLATE=utf8mb4_unicode_ci
|
|
COMMENT='수집 재전송 멱등 nonce (시계오차 창 내 중복 차단; 주기적 정리)';
|
|
|
|
-- 누수 사고(에피소드) 추적. leak_alert 시 행을 열고(open), 같은 센서의 leak_recovery
|
|
-- 시 가장 최근 미복구(recovered_at IS NULL) 행을 닫는다. 대시보드의 누수 상태/이력 표시에 사용.
|
|
CREATE TABLE IF NOT EXISTS `leak_incident` (
|
|
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
`sensor_id` TINYINT UNSIGNED NOT NULL COMMENT '센서 번호(누수=1)',
|
|
`device_id` VARCHAR(64) NOT NULL DEFAULT '',
|
|
`sensor_name` VARCHAR(100) NOT NULL DEFAULT '',
|
|
`device_location` VARCHAR(100) NOT NULL DEFAULT '',
|
|
`started_at` DATETIME NOT NULL COMMENT '누수 감지(시작) 시각',
|
|
`recovered_at` DATETIME NULL DEFAULT NULL COMMENT '누수 복구 시각(미복구면 NULL)',
|
|
`log_id_alert` BIGINT UNSIGNED NULL DEFAULT NULL COMMENT 'leak_alert leak_sensor_log.id',
|
|
`log_id_recovery` BIGINT UNSIGNED NULL DEFAULT NULL COMMENT 'leak_recovery leak_sensor_log.id',
|
|
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
PRIMARY KEY (`id`),
|
|
INDEX `idx_sensor_open` (`sensor_id`, `recovered_at`),
|
|
INDEX `idx_started_at` (`started_at`)
|
|
) ENGINE=InnoDB
|
|
DEFAULT CHARSET=utf8mb4
|
|
COLLATE=utf8mb4_unicode_ci
|
|
COMMENT='누수 사고(에피소드) 추적 (시작/복구)';
|
|
|
|
-- 누수 사진 메타데이터. 실제 파일은 php/var/photos/ 에 서버 생성 무작위 파일명으로 저장
|
|
-- (클라이언트 파일명/경로 미사용 — 경로주입 방지). api/photo_upload.php 가 기록한다.
|
|
CREATE TABLE IF NOT EXISTS `leak_photo` (
|
|
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
`log_id` BIGINT UNSIGNED NOT NULL COMMENT 'leak_alert leak_sensor_log.id',
|
|
`sensor_id` TINYINT UNSIGNED NOT NULL,
|
|
`device_id` VARCHAR(64) NOT NULL DEFAULT '',
|
|
`filename` VARCHAR(128) NOT NULL COMMENT '서버 생성 파일명(var/photos/ 하위)',
|
|
`mime` VARCHAR(40) NOT NULL DEFAULT 'image/jpeg',
|
|
`bytes` INT UNSIGNED NOT NULL DEFAULT 0,
|
|
`captured_at` DATETIME NOT NULL COMMENT '촬영(업로드) 시각',
|
|
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
PRIMARY KEY (`id`),
|
|
INDEX `idx_log_id` (`log_id`),
|
|
INDEX `idx_captured_at` (`captured_at`)
|
|
) ENGINE=InnoDB
|
|
DEFAULT CHARSET=utf8mb4
|
|
COLLATE=utf8mb4_unicode_ci
|
|
COMMENT='누수 사진 메타데이터 (파일은 var/photos/)';
|