# CNC机床数据采集系统 - 数据库设计文档

> 最后更新：2026-04-28
> 状态：定稿（界面设计已全部确认，数据库结构已落地）

---

## 一、数据库架构

- **业务库**：cnc_business
- **日志库**：cnc_log
- **引擎**：InnoDB，字符集 utf8mb4，排序 utf8mb4_unicode_ci
- **分区策略**：高写入表按月分区，定期创建新分区+删除过期分区

---

## 二、业务库 cnc_business（17张表）

### 2.1 车间表 cnc_workshop

``sql
CREATE TABLE cnc_workshop (
    id          INT AUTO_INCREMENT PRIMARY KEY,
    name        VARCHAR(100) NOT NULL UNIQUE,
    sort_order  INT NOT NULL DEFAULT 0,
    is_enabled  TINYINT(1) NOT NULL DEFAULT 1,
    created_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='车间表';
``

无额外索引。3个车间，数据量极小，主键足够。

---

### 2.2 品牌模板表 cnc_brand

``sql
CREATE TABLE cnc_brand (
    id              INT AUTO_INCREMENT PRIMARY KEY,
    brand_name      VARCHAR(50) NOT NULL UNIQUE,
    device_field    VARCHAR(50) NOT NULL DEFAULT 'device',
    tags_path       VARCHAR(100) NOT NULL DEFAULT 'tags',
    is_enabled      TINYINT(1) NOT NULL DEFAULT 1,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='CNC品牌模板表';
``

所有品牌顶层结构统一，device_field和tags_path默认值覆盖绝大多数品牌。品牌数量少，无额外索引。

---

### 2.3 品牌字段映射表 cnc_brand_field_mapping

``sql
CREATE TABLE cnc_brand_field_mapping (
    id              INT AUTO_INCREMENT PRIMARY KEY,
    brand_id        INT NOT NULL,
    standard_field  VARCHAR(50) NOT NULL,
    field_name      VARCHAR(50) NOT NULL,
    match_by        VARCHAR(20) NOT NULL DEFAULT 'id',
    data_type       VARCHAR(20) NOT NULL DEFAULT 'string',
    is_required     TINYINT(1) NOT NULL DEFAULT 0,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (brand_id) REFERENCES cnc_brand(id) ON DELETE CASCADE,
    UNIQUE KEY uk_brand_standard (brand_id, standard_field)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='品牌字段映射表';
``

**标准字段约定**（代码层面，不建表）：

| standard_field | 含义 | data_type |
|----------------|------|-----------|
| program_name | NC程序名 | string |
| part_count | 当前加工零件数 | number |
| device_status | 设备状态 | number |
| run_status | 运行状态 | number |
| operate_mode | 操作模式 | number |
| spindle_speed_set | 主轴设定速度 | number |
| feed_speed_set | 进给设定速度 | number |
| spindle_speed_actual | 主轴实际速度 | number |
| feed_speed_actual | 进给实际转速 | number |
| spindle_load | 主轴负载 | number |
| spindle_override | 主轴倍率 | number |
| power_on_time | 开机时间 | number |
| run_time | 运行时间 | number |
| cutting_time | 切削时间 | number |
| cycle_time | 循环时间 | number |
| machining_status | 加工状态 | string |

索引：uk_brand_standard唯一约束同时作为索引，保证同一品牌下标准字段不重复，采集时按brand_id查询所有映射规则。

---

### 2.4 采集地址表 cnc_collect_address

``sql
CREATE TABLE cnc_collect_address (
    id                INT AUTO_INCREMENT PRIMARY KEY,
    name              VARCHAR(100) NOT NULL,
    url               VARCHAR(500) NOT NULL,
    brand_id          INT NOT NULL,
    collect_interval  INT NOT NULL DEFAULT 30,
    is_enabled        TINYINT(1) NOT NULL DEFAULT 1,
    last_collect_time DATETIME NULL,
    last_collect_status VARCHAR(20) NULL,
    fail_count        INT NOT NULL DEFAULT 0,
    created_at        DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at        DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (brand_id) REFERENCES cnc_brand(id),
    INDEX idx_brand (brand_id),
    INDEX idx_enabled (is_enabled)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采集地址表';
``

索引：idx_brand用于采集服务按品牌加载地址列表；idx_enabled用于只查启用的地址。last_collect_time/status/fail_count由采集服务实时更新，大屏和管理后台直接查此表显示采集状态。

---

### 2.5 机床表 cnc_machine

``sql
CREATE TABLE cnc_machine (
    id                  INT AUTO_INCREMENT PRIMARY KEY,
    device_code         VARCHAR(100) NOT NULL,
    name                VARCHAR(100) NOT NULL,
    workshop_id         INT NOT NULL,
    collect_address_id  INT NOT NULL,
    ip_address          VARCHAR(50) NOT NULL,
    brand_id            INT NOT NULL,
    is_enabled          TINYINT(1) NOT NULL DEFAULT 1,
    is_online           TINYINT(1) NOT NULL DEFAULT 0,
    last_ping_time      DATETIME NULL,
    last_collect_time   DATETIME NULL,
    last_device_status  VARCHAR(20) NULL,
    last_run_status     VARCHAR(20) NULL,
    last_program_name   VARCHAR(200) NULL,
    last_part_count     DECIMAL(15,5) NULL,
    last_operate_mode   VARCHAR(20) NULL,
    last_machining_status VARCHAR(20) NULL,
    created_at          DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at          DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (workshop_id) REFERENCES cnc_workshop(id),
    FOREIGN KEY (collect_address_id) REFERENCES cnc_collect_address(id),
    FOREIGN KEY (brand_id) REFERENCES cnc_brand(id),
    UNIQUE KEY uk_device_code (device_code),
    INDEX idx_workshop (workshop_id),
    INDEX idx_collect_address (collect_address_id),
    INDEX idx_enabled_online (is_enabled, is_online),
    INDEX idx_enabled_address (is_enabled, collect_address_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='CNC机床表';
``

索引设计：
- uk_device_code：采集时用device值匹配机床，唯一约束防重复
- idx_workshop：按车间筛选机床
- idx_enabled_online：大屏查在线+可用机床列表，高频查询
- idx_enabled_address：采集服务按地址查启用的机床列表

实时状态字段：故意冗余到机床表，大屏和首页查询只读此表，不JOIN采集记录表，避免高频查询大表。

---

### 2.6 工人表 cnc_worker

``sql
CREATE TABLE cnc_worker (
    id          INT AUTO_INCREMENT PRIMARY KEY,
    name        VARCHAR(50) NOT NULL,
    code        VARCHAR(50) NOT NULL UNIQUE,
    is_enabled  TINYINT(1) NOT NULL DEFAULT 1,
    created_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工人表';
``

工人不归属车间。uk_code覆盖工号查询。

---

### 2.7 工人-机床绑定表 cnc_worker_machine

``sql
CREATE TABLE cnc_worker_machine (
    id          INT AUTO_INCREMENT PRIMARY KEY,
    worker_id   INT NOT NULL,
    machine_id  INT NOT NULL,
    created_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (worker_id) REFERENCES cnc_worker(id) ON DELETE CASCADE,
    FOREIGN KEY (machine_id) REFERENCES cnc_machine(id) ON DELETE CASCADE,
    UNIQUE KEY uk_machine (machine_id),
    INDEX idx_worker (worker_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工人-机床绑定表';
``

索引：uk_machine保证一对一绑定，同时作为按机床查工人的索引；idx_worker用于按工人查机床列表。

---

### 2.8 采集结构化记录表 cnc_collect_record（按月分区，高频写入）

``sql
CREATE TABLE cnc_collect_record (
    id                    BIGINT AUTO_INCREMENT,
    machine_id            INT NOT NULL,
    collect_time          DATETIME NOT NULL,
    device_time           DATETIME NULL,
    program_name          VARCHAR(200) NULL,
    part_count            DECIMAL(15,5) NULL,
    device_status         VARCHAR(20) NULL,
    run_status            VARCHAR(20) NULL,
    operate_mode          VARCHAR(20) NULL,
    spindle_speed_set     DECIMAL(15,5) NULL,
    feed_speed_set        DECIMAL(15,5) NULL,
    spindle_speed_actual  DECIMAL(15,5) NULL,
    feed_speed_actual     DECIMAL(15,5) NULL,
    spindle_load          DECIMAL(15,5) NULL,
    spindle_override      DECIMAL(5,2) NULL,
    power_on_time         DECIMAL(15,2) NULL,
    run_time              DECIMAL(15,2) NULL,
    cutting_time          DECIMAL(15,2) NULL,
    cycle_time            DECIMAL(15,2) NULL,
    machining_status      VARCHAR(20) NULL,
    extra_data            JSON NULL,
    created_at            DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id, collect_time),
    INDEX idx_machine_time (machine_id, collect_time),
    INDEX idx_collect_time (collect_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='采集结构化记录表（按月分区，90天自动清理）'
  PARTITION BY RANGE (TO_DAYS(collect_time)) (
    PARTITION p202604 VALUES LESS THAN (TO_DAYS('2026-05-01')),
    PARTITION p202605 VALUES LESS THAN (TO_DAYS('2026-06-01')),
    PARTITION p202606 VALUES LESS THAN (TO_DAYS('2026-07-01')),
    PARTITION p_future VALUES LESS THAN MAXVALUE
  );
``

索引设计：
- idx_machine_time：最核心索引，覆盖查某台机床某时间段采集数据（报表、趋势图、日汇总计算）
- idx_collect_time：覆盖查某个时间点所有机床数据（全局状态快照）

分区策略：按月分区，删除过期数据只需DROP PARTITION，避免DELETE大量行导致锁表和碎片。
数据量估算：160台 x 每天约2000条 = 32万/天，90天约2900万行。分区后单分区约300万行。

---

### 2.9 产量分段记录表 cnc_production_segment

``sql
CREATE TABLE cnc_production_segment (
    id                BIGINT AUTO_INCREMENT PRIMARY KEY,
    machine_id        INT NOT NULL,
    program_name      VARCHAR(200) NOT NULL,
    production_date   DATE NOT NULL,
    start_time        DATETIME NOT NULL,
    end_time          DATETIME NULL,
    start_part_count  DECIMAL(15,5) NOT NULL,
    end_part_count    DECIMAL(15,5) NULL,
    quantity          DECIMAL(15,5) NULL,
    is_settled        TINYINT(1) NOT NULL DEFAULT 0,
    close_reason      VARCHAR(30) NULL,
    created_at        DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at        DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (machine_id) REFERENCES cnc_machine(id),
    INDEX idx_machine_date (machine_id, production_date),
    INDEX idx_machine_date_program (machine_id, production_date, program_name),
    INDEX idx_active (machine_id, is_settled, end_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='产量分段记录表';
``

核心逻辑：每次采集检测到以下情况时结账当前段并开新段：
1. NC程序名变化 -> close_reason = 'program_change'
2. 同程序下part_count下降 -> close_reason = 'manual_reset'
3. 日终汇总时仍在运行 -> close_reason = 'end_of_day'
4. 采集服务停止时 -> close_reason = 'service_stop'

A-B-C-A-B场景：程序A第二次出现时创建新的段记录，日汇总按(machine_id, production_date, program_name)合并。

索引设计：
- idx_machine_date：日汇总计算时查某台机床某天的所有段
- idx_machine_date_program：查某台机床某天某个程序的所有段（合并计算）
- idx_active：采集服务运行时快速查找当前活跃段（is_settled=0, end_time IS NULL）

---

### 2.10 机床日状态表 cnc_machine_daily_status

``sql
CREATE TABLE cnc_machine_daily_status (
    id              INT AUTO_INCREMENT PRIMARY KEY,
    machine_id      INT NOT NULL,
    production_date DATE NOT NULL,
    data_status     VARCHAR(20) NOT NULL DEFAULT 'normal',
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (machine_id) REFERENCES cnc_machine(id),
    UNIQUE KEY uk_machine_date (machine_id, production_date),
    INDEX idx_date_status (production_date, data_status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='机床日状态表';
``

data_status规则：
- normal：当天至少成功采集过一次
- offline：当天从未Ping通，产量=0
- data_missing：Ping通但采集始终失败，前端显示-而非0

索引：uk_machine_date保证每台机床每天只有一条状态记录；idx_date_status支持按天查特定状态机床。

---

### 2.11 日汇总表 cnc_daily_production（按机床+程序）

``sql
CREATE TABLE cnc_daily_production (
    id                INT AUTO_INCREMENT PRIMARY KEY,
    machine_id        INT NOT NULL,
    production_date   DATE NOT NULL,
    program_name      VARCHAR(200) NOT NULL,
    total_quantity    DECIMAL(15,5) NOT NULL DEFAULT 0,
    segment_count     INT NOT NULL DEFAULT 1,
    total_run_time    DECIMAL(15,2) NULL,
    total_cutting_time DECIMAL(15,2) NULL,
    total_cycle_time  DECIMAL(15,2) NULL,
    is_adjusted       TINYINT(1) NOT NULL DEFAULT 0,
    adjusted_quantity DECIMAL(15,5) NULL,
    created_at        DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at        DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (machine_id) REFERENCES cnc_machine(id),
    UNIQUE KEY uk_machine_date_program (machine_id, production_date, program_name),
    INDEX idx_date (production_date),
    INDEX idx_date_program (production_date, program_name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='日汇总表(按机床+程序)';
``

索引设计：
- uk_machine_date_program：唯一约束+最核心查询索引
- idx_date：按日期查所有汇总
- idx_date_program：按日期+程序名查汇总

---

### 2.12 工人日汇总表 cnc_worker_daily_summary

``sql
CREATE TABLE cnc_worker_daily_summary (
    id                INT AUTO_INCREMENT PRIMARY KEY,
    worker_id         INT NOT NULL,
    production_date   DATE NOT NULL,
    total_quantity    DECIMAL(15,5) NOT NULL DEFAULT 0,
    machine_count     INT NOT NULL DEFAULT 0,
    program_count     INT NOT NULL DEFAULT 0,
    is_adjusted       TINYINT(1) NOT NULL DEFAULT 0,
    adjusted_quantity DECIMAL(15,5) NULL,
    created_at        DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at        DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (worker_id) REFERENCES cnc_worker(id) ON DELETE CASCADE,
    UNIQUE KEY uk_worker_date (worker_id, production_date),
    INDEX idx_date (production_date),
    INDEX idx_date_quantity (production_date, total_quantity)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工人日汇总表';
``

索引设计：
- uk_worker_date：唯一约束+核心查询
- idx_date：按日期查所有工人汇总
- idx_date_quantity：排行榜核心索引，按日期查产量排名，覆盖大屏排行榜高频查询

---

### 2.13 产量修正审计表 cnc_production_adjustment

``sql
CREATE TABLE cnc_production_adjustment (
    id                BIGINT AUTO_INCREMENT PRIMARY KEY,
    target_table      VARCHAR(30) NOT NULL,
    target_id         INT NOT NULL,
    field_name        VARCHAR(50) NOT NULL,
    old_value         DECIMAL(15,5) NULL,
    new_value         DECIMAL(15,5) NOT NULL,
    reason            VARCHAR(500) NOT NULL,
    operator_ip       VARCHAR(50) NULL,
    created_at        DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_target (target_table, target_id),
    INDEX idx_created (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='产量修正审计表';
``

索引：idx_target用于查某条记录的修正历史；idx_created用于按时间查审计日志。

---

### 2.14 告警表 cnc_alert

``sql
CREATE TABLE cnc_alert (
    id              BIGINT AUTO_INCREMENT PRIMARY KEY,
    alert_type      VARCHAR(30) NOT NULL,
    machine_id      INT NULL,
    collect_address_id INT NULL,
    title           VARCHAR(200) NOT NULL,
    detail          TEXT NULL,
    is_resolved     TINYINT(1) NOT NULL DEFAULT 0,
    resolved_at     DATETIME NULL,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (machine_id) REFERENCES cnc_machine(id) ON DELETE SET NULL,
    FOREIGN KEY (collect_address_id) REFERENCES cnc_collect_address(id) ON DELETE SET NULL,
    INDEX idx_type_resolved (alert_type, is_resolved),
    INDEX idx_machine_time (machine_id, created_at),
    INDEX idx_created (created_at),
    INDEX idx_unresolved (is_resolved, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='告警表';
``

告警类型：collect_fail / device_offline / production_anomaly / unknown_device / service_error

索引设计：
- idx_type_resolved：按类型查未处理告警
- idx_machine_time：查某台机床的告警历史
- idx_unresolved：管理后台最常用查询，查所有未处理告警按时间排序

---

### 2.15 系统配置表 cnc_sys_config

``sql
CREATE TABLE cnc_sys_config (
    id          INT AUTO_INCREMENT PRIMARY KEY,
    config_key  VARCHAR(100) NOT NULL UNIQUE,
    config_value TEXT NOT NULL,
    value_type  VARCHAR(20) NOT NULL DEFAULT 'string',
    description VARCHAR(200) NULL,
    updated_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='系统配置表';
``

预置配置项：

| config_key | 默认值 | 说明 |
|------------|--------|------|
| ping_interval | 60 | Ping检测间隔(秒) |
| collect_retry_count | 3 | 采集失败重试次数 |
| collect_retry_interval | 30 | 采集重试间隔(秒) |
| collect_fail_alert_threshold | 5 | 连续失败N次触发告警 |
| heartbeat_interval | 10 | 心跳写入间隔(秒) |
| daily_summary_time | 01:00 | 日终汇总执行时间 |
| config_poll_interval | 30 | 采集服务轮询配置间隔(秒) |
| log_retention_days | 90 | 原始采集日志保留天数 |
| system_log_retention_days | 30 | 系统日志保留天数 |
| alert_log_retention_days | 180 | 异常日志保留天数 |
| heartbeat_retention_days | 7 | 心跳记录保留天数 |
| bigscreen_refresh_interval | 10 | 大屏刷新间隔(秒) |
| api_token | (自动生成) | 前端API Token |
| collector_api_port | 5800 | 采集服务管理API端口 |
| collector_api_key | (自动生成) | 采集服务间通信API Key |
| error_retry_base_interval | 60 | 任务错误重试基础间隔(秒) |
| admin_username | admin | 管理员用户名（不可修改） |
| admin_password_hash | (bcrypt hash of admin123) | 管理员密码哈希 |

---

### 2.16 大屏配置表 cnc_screen_config

``sql
CREATE TABLE cnc_screen_config (
    id              INT AUTO_INCREMENT PRIMARY KEY,
    card_key        VARCHAR(50) NOT NULL UNIQUE,
    card_type       VARCHAR(30) NOT NULL,
    title           VARCHAR(100) NOT NULL,
    metric          VARCHAR(50) NOT NULL,
    dimension       VARCHAR(30) NULL,
    sort_order      INT NOT NULL DEFAULT 0,
    is_enabled      TINYINT(1) NOT NULL DEFAULT 1,
    chart_config    JSON NULL,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='大屏卡片配置表';
``

card_type类型：stat_number / bar_chart / line_chart / pie_chart / status_grid / rank_list

预置卡片：

| card_key | card_type | title | metric | dimension |
|----------|-----------|-------|--------|-----------|
| total_online | stat_number | 在线机床数 | online_count | - |
| total_production_today | stat_number | 今日总产量 | part_count | - |
| workshop_production | bar_chart | 各车间产量 | part_count | workshop |
| worker_rank | rank_list | 工人产量排行 | part_count | worker |
| machine_rank | rank_list | 机床产量排行 | part_count | machine |
| machine_status | status_grid | 机床状态总览 | - | - |
| collector_status | stat_number | 采集服务状态 | - | - |
| production_trend | line_chart | 产量趋势(7天) | part_count | - |

---

### 2.17 大屏筛选配置表 cnc_screen_filter

``sql
CREATE TABLE cnc_screen_filter (
    id              INT AUTO_INCREMENT PRIMARY KEY,
    screen_key      VARCHAR(50) NOT NULL,
    filter_type     VARCHAR(30) NOT NULL,
    filter_value    VARCHAR(100) NOT NULL,
    is_default      TINYINT(1) NOT NULL DEFAULT 0,
    sort_order      INT NOT NULL DEFAULT 0,
    UNIQUE KEY uk_screen_filter (screen_key, filter_type, filter_value)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='大屏筛选配置表';
``

---

## 三、日志库 cnc_log（5张表）

### 3.1 原始采集JSON表 log_collect_raw（按月分区）

``sql
CREATE TABLE log_collect_raw (
    id                BIGINT AUTO_INCREMENT,
    collect_address_id INT NOT NULL,
    request_time      DATETIME NOT NULL,
    response_time     DATETIME NOT NULL,
    response_duration INT NULL,
    is_success        TINYINT(1) NOT NULL DEFAULT 1,
    status_code       INT NULL,
    raw_json          MEDIUMTEXT NOT NULL,
    error_message     VARCHAR(500) NULL,
    created_at        DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id, request_time),
    INDEX idx_address_time (collect_address_id, request_time),
    INDEX idx_request_time (request_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='原始采集JSON表（按月分区，90天自动清理）'
  PARTITION BY RANGE (TO_DAYS(request_time)) (
    PARTITION p202604 VALUES LESS THAN (TO_DAYS('2026-05-01')),
    PARTITION p202605 VALUES LESS THAN (TO_DAYS('2026-06-01')),
    PARTITION p202606 VALUES LESS THAN (TO_DAYS('2026-07-01')),
    PARTITION p_future VALUES LESS THAN MAXVALUE
  );
``

数据量估算：每30秒一次采集 x 5-10个地址 x 86400秒/天 = 1.4-2.9万条/天，每条约20KB = 280-580MB/天。90天约25-52GB。

---

### 3.2 系统日志表 log_system（按月分区）

``sql
CREATE TABLE log_system (
    id              BIGINT AUTO_INCREMENT,
    log_level       VARCHAR(10) NOT NULL,
    source          VARCHAR(50) NOT NULL,
    message         TEXT NOT NULL,
    stack_trace     TEXT NULL,
    extra_data      JSON NULL,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id, created_at),
    INDEX idx_level_time (log_level, created_at),
    INDEX idx_source_time (source, created_at),
    INDEX idx_created (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='系统日志表（按月分区，30天自动清理）'
  PARTITION BY RANGE (TO_DAYS(created_at)) (
    PARTITION p202604 VALUES LESS THAN (TO_DAYS('2026-05-01')),
    PARTITION p202605 VALUES LESS THAN (TO_DAYS('2026-06-01')),
    PARTITION p202606 VALUES LESS THAN (TO_DAYS('2026-07-01')),
    PARTITION p_future VALUES LESS THAN MAXVALUE
  );
``

---

### 3.3 采集服务心跳表 log_collector_heartbeat

``sql
CREATE TABLE log_collector_heartbeat (
    id                BIGINT AUTO_INCREMENT PRIMARY KEY,
    service_id        VARCHAR(50) NOT NULL DEFAULT 'CncCollector',
    status            VARCHAR(20) NOT NULL,
    collect_address_id INT NULL,
    last_collect_time DATETIME NULL,
    success_count     INT NOT NULL DEFAULT 0,
    fail_count        INT NOT NULL DEFAULT 0,
    uptime_seconds    BIGINT NULL,
    detail            JSON NULL,
    created_at        DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_service_time (service_id, created_at),
    INDEX idx_address_time (collect_address_id, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采集服务心跳表（7天自动清理）';
``

心跳检测逻辑：Web API查WHERE service_id='CncCollector' AND created_at > NOW() - INTERVAL 30 SECOND，有记录=运行中，无记录=已停止。

---

### 3.4 采集分析日志表 log_collect_analysis（按月分区）

记录每次采集后对每台机床的数据变化分析。每次采集周期中，每台机床产生一条分析记录，包含与上一次采集数据的对比结果。

``sql
CREATE TABLE log_collect_analysis (
    id                  BIGINT AUTO_INCREMENT,
    analysis_time       DATETIME NOT NULL          COMMENT '分析时间（分区键）',
    raw_log_id          BIGINT NOT NULL             COMMENT '关联原始日志ID（log_collect_raw.id）',
    collect_address_id  INT NOT NULL                COMMENT '采集地址ID（关联cnc_collect_address）',
    machine_id          INT NOT NULL                COMMENT '机床ID（关联cnc_machine）',
    analysis_type       VARCHAR(30) NOT NULL        COMMENT '分析类型枚举',
    previous_program    VARCHAR(200) NULL           COMMENT '上一次NC程序名',
    current_program     VARCHAR(200) NULL           COMMENT '本次NC程序名',
    previous_part_count DECIMAL(15,5) NULL          COMMENT '上一次零件计数',
    current_part_count  DECIMAL(15,5) NULL          COMMENT '本次零件计数',
    part_count_delta    DECIMAL(15,5) NULL          COMMENT '零件计数变化量（正=增加，负=减少）',
    previous_status     VARCHAR(20) NULL            COMMENT '上一次设备状态',
    current_status      VARCHAR(20) NULL            COMMENT '本次设备状态',
    analysis_summary    VARCHAR(500) NOT NULL       COMMENT '人类可读的分析摘要',
    analysis_detail     JSON NULL                   COMMENT '完整的字段级对比数据（JSON）',
    created_at          DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id, analysis_time),
    INDEX idx_address_time (collect_address_id, analysis_time),
    INDEX idx_machine_time (machine_id, analysis_time),
    INDEX idx_type_time (analysis_type, analysis_time),
    INDEX idx_raw_log (raw_log_id),
    INDEX idx_program_time (current_program, analysis_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='采集分析日志表（按月分区，记录每次采集对每台机床的数据变化分析）'
  PARTITION BY RANGE (TO_DAYS(analysis_time)) (
    PARTITION p202605 VALUES LESS THAN (TO_DAYS('2026-06-01')),
    PARTITION p202606 VALUES LESS THAN (TO_DAYS('2026-07-01')),
    PARTITION p202607 VALUES LESS THAN (TO_DAYS('2026-08-01')),
    PARTITION p_future VALUES LESS THAN MAXVALUE
  );
``

**analysis_type 分析类型枚举：**

| 值 | 含义 | 说明 |
|----|------|------|
| NORMAL_UNCHANGED | 正常无变化 | 数据与上次一致，正常加工中 |
| PART_COUNT_INCREASE | 零件数增加 | 零件计数增长，正常加工中 |
| PROGRAM_SWITCH | NC程序切换 | 程序名变更，触发上一段结账 |
| MANUAL_RESET | 手动清零 | 同程序下零件计数下降 |
| DEVICE_ONLINE | 设备上线 | 设备从离线恢复在线 |
| DEVICE_OFFLINE | 设备离线 | 设备变为离线状态 |
| NEW_DEVICE_FOUND | 发现新设备 | 采集到未注册的device |
| DATA_ANOMALY | 数据异常 | 字段缺失/格式错误/值异常 |
| COLLECTION_FAILED | 采集失败 | 本次采集请求失败 |

**数据量估算**：每次采集周期×每台机床1条。假设每30秒1次采集×5-10个地址×每地址2-5台机床 = 600-2500条/分钟 = 约86-360万条/天。按月分区便于查询和清理。

---

### 3.5 采集周期汇总表 log_collect_cycle（按月分区）

记录每次采集周期（一个地址的一次完整HTTP采集）的汇总信息。一个周期对应 log_collect_raw 中的一条记录和 log_collect_analysis 中的多条记录。

``sql
CREATE TABLE log_collect_cycle (
    id                  BIGINT AUTO_INCREMENT,
    cycle_time          DATETIME NOT NULL           COMMENT '周期开始时间（分区键）',
    collect_address_id  INT NOT NULL                COMMENT '采集地址ID（关联cnc_collect_address）',
    raw_log_id          BIGINT NOT NULL             COMMENT '关联原始日志ID（log_collect_raw.id）',
    end_time            DATETIME NULL               COMMENT '周期结束时间',
    duration_ms         INT NULL                    COMMENT '本次采集总耗时（毫秒）',
    total_machines      INT NOT NULL DEFAULT 0      COMMENT '本周期采集的机床总数',
    success_count       INT NOT NULL DEFAULT 0      COMMENT '成功采集的机床数',
    fail_count          INT NOT NULL DEFAULT 0      COMMENT '失败采集的机床数',
    change_distribution JSON NULL                   COMMENT '变化类型分布（如 {"PROGRAM_SWITCH":2,"PART_COUNT_INCREASE":5}）',
    has_anomaly         TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否存在异常（1=有异常）',
    cycle_summary       VARCHAR(500) NULL           COMMENT '人类可读的周期汇总',
    created_at          DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id, cycle_time),
    INDEX idx_address_time (collect_address_id, cycle_time),
    INDEX idx_time (cycle_time),
    INDEX idx_anomaly_time (has_anomaly, cycle_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='采集周期汇总表（按月分区，每次采集周期的汇总信息）'
  PARTITION BY RANGE (TO_DAYS(cycle_time)) (
    PARTITION p202605 VALUES LESS THAN (TO_DAYS('2026-06-01')),
    PARTITION p202606 VALUES LESS THAN (TO_DAYS('2026-07-01')),
    PARTITION p202607 VALUES LESS THAN (TO_DAYS('2026-08-01')),
    PARTITION p_future VALUES LESS THAN MAXVALUE
  );
``

**数据量估算**：每次采集1条。每30秒×5-10个地址 = 10-20条/分钟 = 约1.4-2.9万条/天。远小于分析表，查询负担轻。

---

## 四、ER关系总览

`
cnc_workshop 1-----N cnc_machine
cnc_brand    1-----N cnc_brand_field_mapping
cnc_brand    1-----N cnc_collect_address
cnc_collect_address 1--N cnc_machine
cnc_machine  1-----1 cnc_worker_machine  (uk_machine)
cnc_worker   1-----N cnc_worker_machine
cnc_machine  1-----N cnc_collect_record
cnc_machine  1-----N cnc_production_segment
cnc_machine  1-----N cnc_daily_production
cnc_machine  1-----N cnc_machine_daily_status
cnc_worker   1-----N cnc_worker_daily_summary
cnc_daily_production 1--N cnc_production_adjustment
cnc_worker_daily_summary 1--N cnc_production_adjustment
cnc_machine  1-----N cnc_alert

--- 日志库 ---
cnc_collect_address 1--N log_collect_raw (弱关联，跨库)
log_collect_raw     1--N log_collect_analysis (原始日志→分析记录)
log_collect_raw     1---1 log_collect_cycle (原始日志→周期汇总)
cnc_machine         1--N log_collect_analysis (弱关联，跨库)
cnc_collect_address 1--N log_collect_cycle (弱关联，跨库)
log_collector_heartbeat (独立，弱关联collect_address_id)
log_system (独立)
`

---

## 五、分区维护策略

| 任务 | 频率 | 操作 |
|------|------|------|
| 创建新分区 | 每月1日 | 为cnc_collect_record、log_collect_raw、log_system、log_collect_analysis、log_collect_cycle预创建下下月分区 |
| 删除过期分区 | 每月1日 | DROP超过保留期的分区（保留天数=0时不删除） |
| 清理心跳表 | 每天 | DELETE log_collector_heartbeat超过7天的记录（保留天数=0时不删除） |
| 清理告警表 | 每天 | DELETE cnc_alert已处理且超过180天的记录 |

---

## 六、索引设计总览

| 表 | 索引名 | 类型 | 覆盖场景 |
|----|--------|------|---------|
| cnc_brand_field_mapping | uk_brand_standard | UNIQUE | 按品牌查映射规则 |
| cnc_collect_address | idx_brand | INDEX | 按品牌加载地址 |
| cnc_collect_address | idx_enabled | INDEX | 查启用的地址 |
| cnc_machine | uk_device_code | UNIQUE | 采集device匹配 |
| cnc_machine | idx_workshop | INDEX | 按车间筛选 |
| cnc_machine | idx_enabled_online | INDEX | 大屏在线机床查询 |
| cnc_machine | idx_enabled_address | INDEX | 采集服务按地址查机床 |
| cnc_worker_machine | uk_machine | UNIQUE | 一对一绑定+按机床查工人 |
| cnc_worker_machine | idx_worker | INDEX | 按工人查机床列表 |
| cnc_collect_record | idx_machine_time | INDEX | 报表/趋势图/汇总计算 |
| cnc_collect_record | idx_collect_time | INDEX | 全局状态快照 |
| cnc_production_segment | idx_machine_date | INDEX | 日汇总计算 |
| cnc_production_segment | idx_machine_date_program | INDEX | 按程序合并计算 |
| cnc_production_segment | idx_active | INDEX | 查找活跃段 |
| cnc_machine_daily_status | uk_machine_date | UNIQUE | 每天一条状态 |
| cnc_machine_daily_status | idx_date_status | INDEX | 按天查状态 |
| cnc_daily_production | uk_machine_date_program | UNIQUE | 机床+日+程序唯一 |
| cnc_daily_production | idx_date | INDEX | 按日期查汇总 |
| cnc_daily_production | idx_date_program | INDEX | 按日期+程序查 |
| cnc_worker_daily_summary | uk_worker_date | UNIQUE | 工人+日唯一 |
| cnc_worker_daily_summary | idx_date | INDEX | 按日期查工人汇总 |
| cnc_worker_daily_summary | idx_date_quantity | INDEX | 排行榜查询 |
| cnc_production_adjustment | idx_target | INDEX | 修正历史查询 |
| cnc_production_adjustment | idx_created | INDEX | 审计日志时间查询 |
| cnc_alert | idx_type_resolved | INDEX | 按类型查未处理 |
| cnc_alert | idx_machine_time | INDEX | 机床告警历史 |
| cnc_alert | idx_unresolved | INDEX | 未处理告警列表 |
| log_collect_raw | idx_address_time | INDEX | 按地址查原始数据 |
| log_collect_raw | idx_request_time | INDEX | 时间范围清理 |
| log_system | idx_level_time | INDEX | 按级别查错误日志 |
| log_system | idx_source_time | INDEX | 按来源查日志 |
| log_collect_raw | idx_success_time | INDEX | 按成功/失败筛选 |
| log_collect_analysis | idx_address_time | INDEX | 按采集地址+时间查分析 |
| log_collect_analysis | idx_machine_time | INDEX | 按机床+时间查分析 |
| log_collect_analysis | idx_type_time | INDEX | 按分析类型+时间查 |
| log_collect_analysis | idx_raw_log | INDEX | 按原始日志ID关联查 |
| log_collect_analysis | idx_program_time | INDEX | 按NC程序名+时间查 |
| log_collect_cycle | idx_address_time | INDEX | 按采集地址+时间查周期 |
| log_collect_cycle | idx_time | INDEX | 时间范围查询 |
| log_collect_cycle | idx_anomaly_time | INDEX | 查异常周期 |
| log_collector_heartbeat | idx_service_time | INDEX | 服务最新状态 |
| log_collector_heartbeat | idx_address_time | INDEX | 地址心跳历史 |

共计：22张表，43个索引（含唯一索引）
