ソースを参照

Phase 1 #18: PostgreSQL + TDengine 完整 DDL 建表脚本

- 15+张核心表: sys_*(部门/用户/角色/菜单/日志/字典/通知/文件)
- iot_*: 设备模型/设备实例/设备影子/设备事件/站点(PostGIS空间坐标)
- rev_*: 用水户/水价阶梯/水表档案/水表操作日志/抄表/账单/缴费/报装
- production: alert_rule(报警规则)/alert_event(报警事件)/dispatch_command(调度)/duty(值班)/patrol(巡检)/water_quality(水质)
- TDengine: iot_telemetry超级表(6类设备子表)+事件表+小时聚合
- 种子数据: admin管理员/5个角色/6个片区/基础字典
bot_pm 5 日 前
コミット
f337ebedfd
共有6 個のファイルを変更した811 個の追加0 個の削除を含む
  1. 210
    0
      db/postgresql/V1__base.sql
  2. 101
    0
      db/postgresql/V1__iot.sql
  3. 189
    0
      db/postgresql/V1__production.sql
  4. 164
    0
      db/postgresql/V1__revenue.sql
  5. 79
    0
      db/seed/V1__seed.sql
  6. 68
    0
      db/tdengine/init.sql

+ 210
- 0
db/postgresql/V1__base.sql ファイルの表示

@@ -0,0 +1,210 @@
1
+-- =============================================
2
+-- 智慧水务管理系统 - PostgreSQL DDL
3
+-- 版本: V1
4
+-- 描述: 核心业务表建表脚本
5
+-- =============================================
6
+
7
+-- ==================== 系统管理 ====================
8
+
9
+-- 部门表
10
+CREATE TABLE IF NOT EXISTS sys_dept (
11
+    id BIGSERIAL PRIMARY KEY,
12
+    parent_id BIGINT,
13
+    dept_name VARCHAR(100) NOT NULL,
14
+    dept_type VARCHAR(20) NOT NULL DEFAULT 'water_company',  -- water_bureau/water_company/ops
15
+    sort_order INT DEFAULT 0,
16
+    leader VARCHAR(50),
17
+    phone VARCHAR(20),
18
+    status SMALLINT DEFAULT 1,
19
+    deleted SMALLINT DEFAULT 0,
20
+    created_at TIMESTAMP DEFAULT NOW(),
21
+    updated_at TIMESTAMP DEFAULT NOW()
22
+);
23
+COMMENT ON TABLE sys_dept IS '部门表';
24
+COMMENT ON COLUMN sys_dept.dept_type IS '部门类型: water_bureau(水利局)/water_company(水务公司)/ops(运维单位)';
25
+
26
+-- 用户表
27
+CREATE TABLE IF NOT EXISTS sys_user (
28
+    id BIGSERIAL PRIMARY KEY,
29
+    dept_id BIGINT REFERENCES sys_dept(id),
30
+    username VARCHAR(50) UNIQUE NOT NULL,
31
+    password VARCHAR(255) NOT NULL,
32
+    real_name VARCHAR(50),
33
+    nickname VARCHAR(50),
34
+    phone VARCHAR(20),
35
+    email VARCHAR(100),
36
+    avatar VARCHAR(500),
37
+    gender SMALLINT DEFAULT 0,  -- 0:未知 1:男 2:女
38
+    role_type VARCHAR(30) DEFAULT 'operator',  -- admin/leader/manager/operator/tech
39
+    status SMALLINT DEFAULT 1,  -- 0:停用 1:启用
40
+    login_ip VARCHAR(50),
41
+    login_at TIMESTAMP,
42
+    deleted SMALLINT DEFAULT 0,
43
+    created_at TIMESTAMP DEFAULT NOW(),
44
+    updated_at TIMESTAMP DEFAULT NOW()
45
+);
46
+COMMENT ON TABLE sys_user IS '用户表';
47
+COMMENT ON COLUMN sys_user.role_type IS '角色类型: admin/leader/manager/operator/tech';
48
+
49
+-- 角色表
50
+CREATE TABLE IF NOT EXISTS sys_role (
51
+    id BIGSERIAL PRIMARY KEY,
52
+    role_name VARCHAR(50) UNIQUE NOT NULL,
53
+    role_key VARCHAR(50) UNIQUE NOT NULL,
54
+    role_sort INT DEFAULT 0,
55
+    data_scope VARCHAR(20) DEFAULT 'SELF',  -- ALL/DEPT/CUSTOM/SELF
56
+    status SMALLINT DEFAULT 1,
57
+    remark VARCHAR(500),
58
+    deleted SMALLINT DEFAULT 0,
59
+    created_at TIMESTAMP DEFAULT NOW(),
60
+    updated_at TIMESTAMP DEFAULT NOW()
61
+);
62
+COMMENT ON TABLE sys_role IS '角色表';
63
+COMMENT ON COLUMN sys_role.role_key IS '角色标识: admin/supervisor/biz_manager/field_ops/tech_maintain';
64
+COMMENT ON COLUMN sys_role.data_scope IS '数据权限范围: ALL/DEPT/CUSTOM/SELF';
65
+
66
+-- 菜单表
67
+CREATE TABLE IF NOT EXISTS sys_menu (
68
+    id BIGSERIAL PRIMARY KEY,
69
+    parent_id BIGINT DEFAULT 0,
70
+    menu_name VARCHAR(50) NOT NULL,
71
+    menu_type CHAR(1) DEFAULT 'M',  -- M:目录 C:菜单 F:按钮
72
+    path VARCHAR(200),
73
+    component VARCHAR(255),
74
+    perms VARCHAR(100),
75
+    icon VARCHAR(100),
76
+    sort_order INT DEFAULT 0,
77
+    visible SMALLINT DEFAULT 1,
78
+    status SMALLINT DEFAULT 1,
79
+    deleted SMALLINT DEFAULT 0,
80
+    created_at TIMESTAMP DEFAULT NOW(),
81
+    updated_at TIMESTAMP DEFAULT NOW()
82
+);
83
+COMMENT ON TABLE sys_menu IS '菜单表';
84
+
85
+-- 角色-菜单关联
86
+CREATE TABLE IF NOT EXISTS sys_role_menu (
87
+    role_id BIGINT NOT NULL REFERENCES sys_role(id),
88
+    menu_id BIGINT NOT NULL REFERENCES sys_menu(id),
89
+    PRIMARY KEY (role_id, menu_id)
90
+);
91
+COMMENT ON TABLE sys_role_menu IS '角色菜单关联表';
92
+
93
+-- 用户-角色关联
94
+CREATE TABLE IF NOT EXISTS sys_user_role (
95
+    user_id BIGINT NOT NULL REFERENCES sys_user(id),
96
+    role_id BIGINT NOT NULL REFERENCES sys_role(id),
97
+    PRIMARY KEY (user_id, role_id)
98
+);
99
+COMMENT ON TABLE sys_user_role IS '用户角色关联表';
100
+
101
+-- 操作日志表
102
+CREATE TABLE IF NOT EXISTS sys_oper_log (
103
+    id BIGSERIAL PRIMARY KEY,
104
+    user_id BIGINT,
105
+    username VARCHAR(50),
106
+    module VARCHAR(50),
107
+    operation VARCHAR(50),
108
+    method VARCHAR(200),
109
+    request_method VARCHAR(10),
110
+    request_url VARCHAR(500),
111
+    request_params TEXT,
112
+    response_result TEXT,
113
+    ip VARCHAR(50),
114
+    location VARCHAR(100),
115
+    duration BIGINT,
116
+    status SMALLINT DEFAULT 1,
117
+    error_msg TEXT,
118
+    created_at TIMESTAMP DEFAULT NOW()
119
+);
120
+COMMENT ON TABLE sys_oper_log IS '操作日志表';
121
+
122
+-- 登录日志表
123
+CREATE TABLE IF NOT EXISTS sys_login_log (
124
+    id BIGSERIAL PRIMARY KEY,
125
+    username VARCHAR(50),
126
+    ip VARCHAR(50),
127
+    location VARCHAR(100),
128
+    browser VARCHAR(100),
129
+    os VARCHAR(100),
130
+    status SMALLINT DEFAULT 1,  -- 0:失败 1:成功
131
+    msg VARCHAR(500),
132
+    login_at TIMESTAMP DEFAULT NOW()
133
+);
134
+COMMENT ON TABLE sys_login_log IS '登录日志表';
135
+
136
+-- ==================== 数据字典 ====================
137
+
138
+CREATE TABLE IF NOT EXISTS sys_dict_type (
139
+    id BIGSERIAL PRIMARY KEY,
140
+    dict_name VARCHAR(100) NOT NULL,
141
+    dict_key VARCHAR(100) UNIQUE NOT NULL,
142
+    status SMALLINT DEFAULT 1,
143
+    remark VARCHAR(500),
144
+    created_at TIMESTAMP DEFAULT NOW(),
145
+    updated_at TIMESTAMP DEFAULT NOW()
146
+);
147
+COMMENT ON TABLE sys_dict_type IS '字典类型表';
148
+
149
+CREATE TABLE IF NOT EXISTS sys_dict_data (
150
+    id BIGSERIAL PRIMARY KEY,
151
+    dict_type_id BIGINT REFERENCES sys_dict_type(id),
152
+    dict_label VARCHAR(100) NOT NULL,
153
+    dict_value VARCHAR(100) NOT NULL,
154
+    css_class VARCHAR(100),
155
+    list_class VARCHAR(100),
156
+    sort_order INT DEFAULT 0,
157
+    status SMALLINT DEFAULT 1,
158
+    remark VARCHAR(500),
159
+    created_at TIMESTAMP DEFAULT NOW(),
160
+    updated_at TIMESTAMP DEFAULT NOW()
161
+);
162
+COMMENT ON TABLE sys_dict_data IS '字典数据表';
163
+
164
+-- ==================== 通知方案 ====================
165
+
166
+CREATE TABLE IF NOT EXISTS sys_notify_scheme (
167
+    id BIGSERIAL PRIMARY KEY,
168
+    scheme_name VARCHAR(100) NOT NULL,
169
+    channels VARCHAR(200),  -- sms,app_push,websocket,wechat (逗号分隔)
170
+    template_id VARCHAR(50),
171
+    status SMALLINT DEFAULT 1,
172
+    created_at TIMESTAMP DEFAULT NOW()
173
+);
174
+COMMENT ON TABLE sys_notify_scheme IS '通知方案表';
175
+
176
+CREATE TABLE IF NOT EXISTS sys_notify_record (
177
+    id BIGSERIAL PRIMARY KEY,
178
+    scheme_id BIGINT REFERENCES sys_notify_scheme(id),
179
+    target_user_id BIGINT,
180
+    target_phone VARCHAR(20),
181
+    channel VARCHAR(20),
182
+    title VARCHAR(200),
183
+    content TEXT,
184
+    status VARCHAR(20) DEFAULT 'pending',  -- pending/sent/failed/read
185
+    sent_at TIMESTAMP,
186
+    read_at TIMESTAMP,
187
+    error_msg VARCHAR(500),
188
+    created_at TIMESTAMP DEFAULT NOW()
189
+);
190
+COMMENT ON TABLE sys_notify_record IS '通知记录表';
191
+
192
+-- ==================== 文件管理 ====================
193
+
194
+CREATE TABLE IF NOT EXISTS sys_file (
195
+    id BIGSERIAL PRIMARY KEY,
196
+    file_name VARCHAR(200) NOT NULL,
197
+    original_name VARCHAR(200),
198
+    file_path VARCHAR(500) NOT NULL,
199
+    file_size BIGINT,
200
+    mime_type VARCHAR(100),
201
+    file_ext VARCHAR(20),
202
+    storage_type VARCHAR(20) DEFAULT 'minio',
203
+    bucket VARCHAR(100) DEFAULT 'water-management',
204
+    module VARCHAR(50),
205
+    biz_id BIGINT,
206
+    upload_by BIGINT,
207
+    deleted SMALLINT DEFAULT 0,
208
+    created_at TIMESTAMP DEFAULT NOW()
209
+);
210
+COMMENT ON TABLE sys_file IS '文件管理表';

+ 101
- 0
db/postgresql/V1__iot.sql ファイルの表示

@@ -0,0 +1,101 @@
1
+-- =============================================
2
+-- 智慧水务管理系统 - 物联网设备相关 DDL
3
+-- 版本: V1
4
+-- =============================================
5
+
6
+-- 扩展
7
+CREATE EXTENSION IF NOT EXISTS postgis;
8
+
9
+-- 设备模型定义
10
+CREATE TABLE IF NOT EXISTS iot_device_model (
11
+    id BIGSERIAL PRIMARY KEY,
12
+    model_key VARCHAR(50) UNIQUE NOT NULL,
13
+    model_name VARCHAR(100) NOT NULL,
14
+    vendor VARCHAR(100),
15
+    protocol VARCHAR(20) NOT NULL,  -- MQTT/Modbus/CoAP/HTTP
16
+    properties JSONB,               -- [{key, name, unit, data_type, range_min, range_max}]
17
+    commands JSONB,                 -- [{key, name, params: [{key, name, type}]}]
18
+    status SMALLINT DEFAULT 1,
19
+    created_at TIMESTAMP DEFAULT NOW(),
20
+    updated_at TIMESTAMP DEFAULT NOW()
21
+);
22
+COMMENT ON TABLE iot_device_model IS '设备模型定义表';
23
+COMMENT ON COLUMN iot_device_model.properties IS '属性定义JSON数组';
24
+COMMENT ON COLUMN iot_device_model.commands IS '支持指令JSON数组';
25
+
26
+-- 设备实例
27
+CREATE TABLE IF NOT EXISTS iot_device (
28
+    id BIGSERIAL PRIMARY KEY,
29
+    device_sn VARCHAR(100) UNIQUE NOT NULL,
30
+    device_name VARCHAR(200) NOT NULL,
31
+    model_id BIGINT REFERENCES iot_device_model(id),
32
+    device_type VARCHAR(30) NOT NULL,       -- flow_meter/pressure_sensor/valve/water_quality/level_sensor/pump
33
+    position_type VARCHAR(20),              -- water_plant/pressure_station/pipe_network/village
34
+    label VARCHAR(50),                      -- 设备标签(便于搜索)
35
+    loc_lng DOUBLE PRECISION,
36
+    loc_lat DOUBLE PRECISION,
37
+    geom GEOMETRY(Point, 4326),
38
+    area VARCHAR(50) NOT NULL,              -- 所属片区
39
+    station_id BIGINT,                      -- 所属站点
40
+    address VARCHAR(300),
41
+    install_date DATE,
42
+    warranty_until DATE,
43
+    status VARCHAR(20) DEFAULT 'offline',   -- online/offline/maintenance/fault
44
+    firmware_version VARCHAR(20),
45
+    last_report_time TIMESTAMP,
46
+    metadata JSONB,
47
+    created_at TIMESTAMP DEFAULT NOW(),
48
+    updated_at TIMESTAMP DEFAULT NOW()
49
+);
50
+COMMENT ON TABLE iot_device IS '设备实例表';
51
+COMMENT ON COLUMN iot_device.geom IS 'PostGIS空间坐标点';
52
+COMMENT ON COLUMN iot_device.metadata IS '扩展元数据';
53
+
54
+-- 空间索引
55
+CREATE INDEX IF NOT EXISTS idx_iot_device_geom ON iot_device USING GIST(geom);
56
+CREATE INDEX IF NOT EXISTS idx_iot_device_area ON iot_device(area);
57
+CREATE INDEX IF NOT EXISTS idx_iot_device_type ON iot_device(device_type);
58
+CREATE INDEX IF NOT EXISTS idx_iot_device_status ON iot_device(status);
59
+
60
+-- 设备影子
61
+CREATE TABLE IF NOT EXISTS iot_device_shadow (
62
+    id BIGSERIAL PRIMARY KEY,
63
+    device_id BIGINT UNIQUE REFERENCES iot_device(id),
64
+    reported_state JSONB,   -- 设备上报的最新状态
65
+    desired_state JSONB,    -- 期望状态(云端下发)
66
+    delta_state JSONB,      -- 差异状态
67
+    version BIGINT DEFAULT 1,
68
+    updated_at TIMESTAMP DEFAULT NOW()
69
+);
70
+COMMENT ON TABLE iot_device_shadow IS '设备影子表';
71
+
72
+-- 设备事件
73
+CREATE TABLE IF NOT EXISTS iot_device_event (
74
+    id BIGSERIAL PRIMARY KEY,
75
+    device_id BIGINT REFERENCES iot_device(id),
76
+    device_sn VARCHAR(100) NOT NULL,
77
+    event_type VARCHAR(30) NOT NULL,  -- online/offline/fault/recovery/ota
78
+    event_data JSONB,
79
+    created_at TIMESTAMP DEFAULT NOW()
80
+);
81
+COMMENT ON TABLE iot_device_event IS '设备事件表';
82
+CREATE INDEX IF NOT EXISTS idx_iot_device_event_sn ON iot_device_event(device_sn, created_at DESC);
83
+
84
+-- 站点管理(水厂/调压站等)
85
+CREATE TABLE IF NOT EXISTS iot_station (
86
+    id BIGSERIAL PRIMARY KEY,
87
+    station_name VARCHAR(100) NOT NULL,
88
+    station_type VARCHAR(30) NOT NULL,       -- water_plant/pressure_station/pump_station
89
+    area VARCHAR(50) NOT NULL,
90
+    loc_lng DOUBLE PRECISION,
91
+    loc_lat DOUBLE PRECISION,
92
+    geom GEOMETRY(Point, 4326),
93
+    address VARCHAR(300),
94
+    manager VARCHAR(50),
95
+    manager_phone VARCHAR(20),
96
+    status SMALLINT DEFAULT 1,
97
+    created_at TIMESTAMP DEFAULT NOW(),
98
+    updated_at TIMESTAMP DEFAULT NOW()
99
+);
100
+COMMENT ON TABLE iot_station IS '站点管理表';
101
+CREATE INDEX IF NOT EXISTS idx_iot_station_geom ON iot_station USING GIST(geom);

+ 189
- 0
db/postgresql/V1__production.sql ファイルの表示

@@ -0,0 +1,189 @@
1
+-- =============================================
2
+-- 智慧水务管理系统 - 供水生产 + 巡检 + 报警 DDL
3
+-- 版本: V1
4
+-- =============================================
5
+
6
+-- ==================== 报警规则 ====================
7
+
8
+CREATE TABLE IF NOT EXISTS alert_rule (
9
+    id BIGSERIAL PRIMARY KEY,
10
+    rule_name VARCHAR(100) NOT NULL,
11
+    device_type VARCHAR(30),
12
+    metric_key VARCHAR(50) NOT NULL,
13
+    alert_level VARCHAR(10) NOT NULL,        -- info/warning/critical/emergency
14
+    condition_expr VARCHAR(200) NOT NULL,    -- "> 0.5" 或 "ABS_DELTA > 10"
15
+    threshold_value DECIMAL(12,4),
16
+    debounce_sec INT DEFAULT 300,            -- 去重窗口(秒)
17
+    notify_scheme_id BIGINT REFERENCES sys_notify_scheme(id),
18
+    enabled SMALLINT DEFAULT 1,
19
+    created_at TIMESTAMP DEFAULT NOW(),
20
+    updated_at TIMESTAMP DEFAULT NOW()
21
+);
22
+COMMENT ON TABLE alert_rule IS '报警规则表';
23
+COMMENT ON COLUMN alert_rule.alert_level IS '报警等级: info/warning/critical/emergency';
24
+
25
+CREATE TABLE IF NOT EXISTS alert_event (
26
+    id BIGSERIAL PRIMARY KEY,
27
+    rule_id BIGINT REFERENCES alert_rule(id),
28
+    device_id BIGINT,
29
+    device_sn VARCHAR(100) NOT NULL,
30
+    device_name VARCHAR(200),
31
+    area VARCHAR(50),
32
+    metric_key VARCHAR(50) NOT NULL,
33
+    metric_value DECIMAL(12,4),
34
+    threshold_value VARCHAR(50),
35
+    alert_level VARCHAR(10) NOT NULL,
36
+    title VARCHAR(200),
37
+    message TEXT,
38
+    confirmed_by BIGINT,
39
+    confirmed_at TIMESTAMP,
40
+    dispatched SMALLINT DEFAULT 0,           -- 是否已派单
41
+    resolved_by BIGINT,
42
+    resolved_at TIMESTAMP,
43
+    created_at TIMESTAMP DEFAULT NOW()
44
+);
45
+COMMENT ON TABLE alert_event IS '报警事件表';
46
+CREATE INDEX IF NOT EXISTS idx_alert_event_time ON alert_event(created_at DESC);
47
+CREATE INDEX IF NOT EXISTS idx_alert_event_device ON alert_event(device_sn, created_at DESC);
48
+
49
+-- ==================== 调度指令 ====================
50
+
51
+CREATE TABLE IF NOT EXISTS dispatch_command (
52
+    id BIGSERIAL PRIMARY KEY,
53
+    command_no VARCHAR(30) UNIQUE NOT NULL,
54
+    command_type VARCHAR(30) NOT NULL,       -- routine/emergency
55
+    command_title VARCHAR(200) NOT NULL,
56
+    command_content TEXT NOT NULL,
57
+    source VARCHAR(50),                      -- 来源: 平台/SCADA/上级
58
+    issuer_id BIGINT,
59
+    issuer_name VARCHAR(50),
60
+    target_type VARCHAR(30),                 -- station/device_area/person
61
+    target_ids JSONB,                        -- 目标站点/设备ID数组
62
+    priority VARCHAR(10) DEFAULT 'normal',   -- low/normal/high/urgent
63
+    status VARCHAR(20) DEFAULT 'draft',      -- draft/issued/received/executing/completed/rejected
64
+    issued_at TIMESTAMP,
65
+    received_at TIMESTAMP,
66
+    completed_at TIMESTAMP,
67
+    result_remark TEXT,
68
+    created_at TIMESTAMP DEFAULT NOW(),
69
+    updated_at TIMESTAMP DEFAULT NOW()
70
+);
71
+COMMENT ON TABLE dispatch_command IS '调度指令表';
72
+
73
+CREATE TABLE IF NOT EXISTS dispatch_log (
74
+    id BIGSERIAL PRIMARY KEY,
75
+    command_id BIGINT REFERENCES dispatch_command(id),
76
+    action VARCHAR(20) NOT NULL,             -- issue/receive/execute/complete/reject
77
+    operator_id BIGINT,
78
+    operator_name VARCHAR(50),
79
+    remark TEXT,
80
+    created_at TIMESTAMP DEFAULT NOW()
81
+);
82
+COMMENT ON TABLE dispatch_log IS '调度指令操作日志';
83
+
84
+-- ==================== 值班管理 ====================
85
+
86
+CREATE TABLE IF NOT EXISTS duty_schedule (
87
+    id BIGSERIAL PRIMARY KEY,
88
+    schedule_name VARCHAR(100),
89
+    dept_id BIGINT REFERENCES sys_dept(id),
90
+    station_id BIGINT,
91
+    shift_type VARCHAR(20),                  -- day/night/full
92
+    start_time TIME NOT NULL,
93
+    end_time TIME NOT NULL,
94
+    status SMALLINT DEFAULT 1,
95
+    created_at TIMESTAMP DEFAULT NOW()
96
+);
97
+COMMENT ON TABLE duty_schedule IS '值班班次表';
98
+
99
+CREATE TABLE IF NOT EXISTS duty_record (
100
+    id BIGSERIAL PRIMARY KEY,
101
+    schedule_id BIGINT REFERENCES duty_schedule(id),
102
+    duty_date DATE NOT NULL,
103
+    user_id BIGINT REFERENCES sys_user(id),
104
+    shift_type VARCHAR(20),
105
+    on_duty_at TIMESTAMP,
106
+    off_duty_at TIMESTAMP,
107
+    status VARCHAR(20) DEFAULT 'pending',    -- pending/on_duty/off_duty
108
+    handover_remark TEXT,
109
+    created_at TIMESTAMP DEFAULT NOW()
110
+);
111
+COMMENT ON TABLE duty_record IS '值班记录表';
112
+
113
+-- ==================== 巡检管理 ====================
114
+
115
+CREATE TABLE IF NOT EXISTS patrol_route (
116
+    id BIGSERIAL PRIMARY KEY,
117
+    route_name VARCHAR(100) NOT NULL,
118
+    area VARCHAR(50) NOT NULL,
119
+    route_points JSONB,                      -- [{seq, lng, lat, name, device_ids}]
120
+    estim_duration INT,                      -- 预计时长(分钟)
121
+    status SMALLINT DEFAULT 1,
122
+    created_at TIMESTAMP DEFAULT NOW(),
123
+    updated_at TIMESTAMP DEFAULT NOW()
124
+);
125
+COMMENT ON TABLE patrol_route IS '巡检路线表';
126
+
127
+CREATE TABLE IF NOT EXISTS patrol_task (
128
+    id BIGSERIAL PRIMARY KEY,
129
+    route_id BIGINT REFERENCES patrol_route(id),
130
+    assignee_id BIGINT REFERENCES sys_user(id),
131
+    task_name VARCHAR(200),
132
+    task_date DATE NOT NULL,
133
+    plan_start TIMESTAMP,
134
+    plan_end TIMESTAMP,
135
+    actual_start TIMESTAMP,
136
+    actual_end TIMESTAMP,
137
+    status VARCHAR(20) DEFAULT 'pending',    -- pending/in_progress/completed/expired/cancelled
138
+    distance DECIMAL(8,2),                   -- 实际巡检距离(km)
139
+    score INT,                               -- 评分
140
+    created_at TIMESTAMP DEFAULT NOW(),
141
+    updated_at TIMESTAMP DEFAULT NOW()
142
+);
143
+COMMENT ON TABLE patrol_task IS '巡检任务表';
144
+CREATE INDEX IF NOT EXISTS idx_patrol_task_date ON patrol_task(task_date);
145
+
146
+CREATE TABLE IF NOT EXISTS patrol_record (
147
+    id BIGSERIAL PRIMARY KEY,
148
+    task_id BIGINT REFERENCES patrol_task(id),
149
+    point_seq INT,
150
+    device_id BIGINT,
151
+    device_name VARCHAR(200),
152
+    check_items JSONB,                       -- [{item, result, value, photo_urls, remark}]
153
+    gps_lng DOUBLE PRECISION,
154
+    gps_lat DOUBLE PRECISION,
155
+    geom GEOMETRY(Point, 4326),
156
+    record_time TIMESTAMP,
157
+    created_at TIMESTAMP DEFAULT NOW()
158
+);
159
+COMMENT ON TABLE patrol_record IS '巡检记录表';
160
+CREATE INDEX IF NOT EXISTS idx_patrol_record_geom ON patrol_record USING GIST(geom);
161
+
162
+-- ==================== 水质检测 ====================
163
+
164
+CREATE TABLE IF NOT EXISTS water_quality_record (
165
+    id BIGSERIAL PRIMARY KEY,
166
+    test_type VARCHAR(20) NOT NULL,          -- manual/auto
167
+    test_point VARCHAR(100) NOT NULL,        -- 检测点位名称
168
+    point_type VARCHAR(20),                  -- source_water/plant_out/pipe_network/terminal
169
+    area VARCHAR(50),
170
+    station_id BIGINT,
171
+    lng DOUBLE PRECISION,
172
+    lat DOUBLE PRECISION,
173
+    test_date DATE NOT NULL,
174
+    test_time TIME,
175
+    tester VARCHAR(50),
176
+    turbidity DECIMAL(8,2),                  -- 浊度(NTU)
177
+    ph DECIMAL(5,2),
178
+    residual_chlorine DECIMAL(8,2),          -- 余氯(mg/L)
179
+    total_chlorine DECIMAL(8,2),
180
+    cod DECIMAL(8,2),                        -- 化学需氧量(mg/L)
181
+    ammonia DECIMAL(8,2),                    -- 氨氮(mg/L)
182
+    total_bacteria INT,                      -- 总大肠菌群(CFU/100mL)
183
+    extra_data JSONB,                        -- 扩展指标
184
+    is_qualified SMALLINT,
185
+    created_at TIMESTAMP DEFAULT NOW()
186
+);
187
+COMMENT ON TABLE water_quality_record IS '水质检测记录表';
188
+CREATE INDEX IF NOT EXISTS idx_wq_record_date ON water_quality_record(test_date);
189
+CREATE INDEX IF NOT EXISTS idx_wq_record_area ON water_quality_record(area);

+ 164
- 0
db/postgresql/V1__revenue.sql ファイルの表示

@@ -0,0 +1,164 @@
1
+-- =============================================
2
+-- 智慧水务管理系统 - 营业收费相关 DDL
3
+-- 版本: V1
4
+-- =============================================
5
+
6
+-- 用水户
7
+CREATE TABLE IF NOT EXISTS rev_customer (
8
+    id BIGSERIAL PRIMARY KEY,
9
+    customer_no VARCHAR(30) UNIQUE NOT NULL,
10
+    customer_name VARCHAR(100) NOT NULL,
11
+    customer_type VARCHAR(20) NOT NULL,      -- residential/business/enterprise/institution
12
+    area VARCHAR(50) NOT NULL,
13
+    address VARCHAR(300),
14
+    phone VARCHAR(20),
15
+    id_card VARCHAR(18),
16
+    contract_no VARCHAR(50),
17
+    open_date DATE,
18
+    meter_count INT DEFAULT 0,
19
+    status VARCHAR(20) DEFAULT 'active',     -- active/suspended/closed
20
+    remark VARCHAR(500),
21
+    created_at TIMESTAMP DEFAULT NOW(),
22
+    updated_at TIMESTAMP DEFAULT NOW()
23
+);
24
+COMMENT ON TABLE rev_customer IS '用水户表';
25
+
26
+-- 水价阶梯
27
+CREATE TABLE IF NOT EXISTS rev_water_price (
28
+    id BIGSERIAL PRIMARY KEY,
29
+    customer_type VARCHAR(20) NOT NULL,      -- residential/business/enterprise/institution
30
+    tier_no INT NOT NULL,                    -- 第几阶梯
31
+    range_start DECIMAL(12,2) DEFAULT 0,     -- 起始水量(立方米)
32
+    range_end DECIMAL(12,2),                 -- 结束水量(null=无上限)
33
+    water_price DECIMAL(10,4) NOT NULL,      -- 水价(元/立方米)
34
+    sewage_price DECIMAL(10,4) DEFAULT 0,    -- 污水处理费
35
+    effective_date DATE NOT NULL,
36
+    status SMALLINT DEFAULT 1,
37
+    created_at TIMESTAMP DEFAULT NOW()
38
+);
39
+COMMENT ON TABLE rev_water_price IS '水价阶梯配置表';
40
+
41
+-- 水表档案
42
+CREATE TABLE IF NOT EXISTS rev_meter (
43
+    id BIGSERIAL PRIMARY KEY,
44
+    meter_no VARCHAR(50) UNIQUE NOT NULL,
45
+    customer_id BIGINT REFERENCES rev_customer(id),
46
+    device_id BIGINT,                        -- 关联 IoT 设备
47
+    caliber VARCHAR(10),                     -- DN15/DN20/DN40/DN80+
48
+    meter_type VARCHAR(20),                  -- mechanical/ultrasonic/electromagnetic
49
+    manufacturer VARCHAR(100),
50
+    max_reading DECIMAL(10,2) DEFAULT 99999,
51
+    initial_reading DECIMAL(10,2) DEFAULT 0,
52
+    current_reading DECIMAL(10,2) DEFAULT 0,
53
+    install_date DATE,
54
+    install_address VARCHAR(300),
55
+    status VARCHAR(20) DEFAULT 'active',     -- active/dismantled/scrapped/repaired/warehouse
56
+    remark VARCHAR(500),
57
+    created_at TIMESTAMP DEFAULT NOW(),
58
+    updated_at TIMESTAMP DEFAULT NOW()
59
+);
60
+COMMENT ON TABLE rev_meter IS '水表档案表';
61
+
62
+-- 水表操作记录(全生命周期)
63
+CREATE TABLE IF NOT EXISTS rev_meter_log (
64
+    id BIGSERIAL PRIMARY KEY,
65
+    meter_id BIGINT REFERENCES rev_meter(id),
66
+    operation_type VARCHAR(30) NOT NULL,     -- install/dismantle/repair/change/scrap/calibrate/refurbish
67
+    old_reading DECIMAL(10,2),
68
+    new_reading DECIMAL(10,2),
69
+    new_meter_no VARCHAR(50),
70
+    operator_id BIGINT,
71
+    operator_name VARCHAR(50),
72
+    photos JSONB,                            -- 现场照片URL数组
73
+    remark VARCHAR(500),
74
+    created_at TIMESTAMP DEFAULT NOW()
75
+);
76
+COMMENT ON TABLE rev_meter_log IS '水表操作记录表(全生命周期)';
77
+
78
+-- 抄表记录
79
+CREATE TABLE IF NOT EXISTS rev_reading (
80
+    id BIGSERIAL PRIMARY KEY,
81
+    meter_id BIGINT REFERENCES rev_meter(id),
82
+    reading_date DATE NOT NULL,
83
+    reading_period VARCHAR(10),              -- 2026-06
84
+    prev_reading DECIMAL(10,2),
85
+    curr_reading DECIMAL(10,2),
86
+    consumption DECIMAL(10,2),
87
+    read_type VARCHAR(20) DEFAULT 'manual',  -- manual/remote/estimate
88
+    reader_id BIGINT,
89
+    reader_name VARCHAR(50),
90
+    photo_urls JSONB,                        -- 拍照图片URL数组
91
+    abnormal_flag SMALLINT DEFAULT 0,        -- 0:正常 1:异常
92
+    verified SMALLINT DEFAULT 0,             -- 0:未审核 1:已审核
93
+    verified_by BIGINT,
94
+    verified_at TIMESTAMP,
95
+    created_at TIMESTAMP DEFAULT NOW()
96
+);
97
+COMMENT ON TABLE rev_reading IS '抄表记录表';
98
+CREATE INDEX IF NOT EXISTS idx_reading_period ON rev_reading(reading_period);
99
+
100
+-- 水费账单
101
+CREATE TABLE IF NOT EXISTS rev_bill (
102
+    id BIGSERIAL PRIMARY KEY,
103
+    bill_no VARCHAR(30) UNIQUE NOT NULL,
104
+    customer_id BIGINT REFERENCES rev_customer(id),
105
+    meter_id BIGINT REFERENCES rev_meter(id),
106
+    reading_id BIGINT REFERENCES rev_reading(id),
107
+    bill_period VARCHAR(10) NOT NULL,        -- 2026-06
108
+    prev_reading DECIMAL(10,2),
109
+    curr_reading DECIMAL(10,2),
110
+    consumption DECIMAL(10,2),
111
+    water_fee DECIMAL(10,2),
112
+    sewage_fee DECIMAL(10,2),
113
+    other_fee DECIMAL(10,2) DEFAULT 0,       -- 污水处理费/垃圾处理费等
114
+    total_fee DECIMAL(10,2),
115
+    paid_fee DECIMAL(10,2) DEFAULT 0,
116
+    discount_fee DECIMAL(10,2) DEFAULT 0,
117
+    status VARCHAR(20) DEFAULT 'pending',    -- pending/partial/paid/overdue/cancelled
118
+    due_date DATE,
119
+    paid_at TIMESTAMP,
120
+    created_at TIMESTAMP DEFAULT NOW(),
121
+    updated_at TIMESTAMP DEFAULT NOW()
122
+);
123
+COMMENT ON TABLE rev_bill IS '水费账单表';
124
+CREATE INDEX IF NOT EXISTS idx_bill_customer ON rev_bill(customer_id, bill_period);
125
+CREATE INDEX IF NOT EXISTS idx_bill_status ON rev_bill(status);
126
+
127
+-- 缴费记录
128
+CREATE TABLE IF NOT EXISTS rev_payment (
129
+    id BIGSERIAL PRIMARY KEY,
130
+    bill_id BIGINT REFERENCES rev_bill(id),
131
+    customer_id BIGINT REFERENCES rev_customer(id),
132
+    payment_no VARCHAR(50) UNIQUE NOT NULL,
133
+    amount DECIMAL(10,2) NOT NULL,
134
+    pay_method VARCHAR(20),                  -- wechat/alipay/cash/bank_transfer/pos
135
+    pay_channel VARCHAR(30),                 -- counter/app/wechat_mini/third_party
136
+    transaction_id VARCHAR(100),
137
+    operator_id BIGINT,
138
+    remark VARCHAR(500),
139
+    paid_at TIMESTAMP DEFAULT NOW()
140
+);
141
+COMMENT ON TABLE rev_payment IS '缴费记录表';
142
+
143
+-- 报装申请
144
+CREATE TABLE IF NOT EXISTS rev_install (
145
+    id BIGSERIAL PRIMARY KEY,
146
+    application_no VARCHAR(30) UNIQUE NOT NULL,
147
+    applicant_name VARCHAR(50) NOT NULL,
148
+    applicant_phone VARCHAR(20) NOT NULL,
149
+    applicant_id_card VARCHAR(18),
150
+    area VARCHAR(50),
151
+    address VARCHAR(300),
152
+    customer_type VARCHAR(20),               -- residential/business/enterprise
153
+    caliber VARCHAR(10),                     -- 申请管径
154
+    purpose VARCHAR(200),
155
+    status VARCHAR(20) DEFAULT 'pre_apply',  -- pre_apply/engineering/pending_review/approved/rejected/completed
156
+    survey_date DATE,
157
+    survey_result TEXT,
158
+    approved_by BIGINT,
159
+    approved_at TIMESTAMP,
160
+    completed_at TIMESTAMP,
161
+    created_at TIMESTAMP DEFAULT NOW(),
162
+    updated_at TIMESTAMP DEFAULT NOW()
163
+);
164
+COMMENT ON TABLE rev_install IS '报装申请表';

+ 79
- 0
db/seed/V1__seed.sql ファイルの表示

@@ -0,0 +1,79 @@
1
+-- =============================================
2
+-- 种子数据:管理员 + 基础角色 + 字典
3
+-- =============================================
4
+
5
+-- 插入部门
6
+INSERT INTO sys_dept (id, parent_id, dept_name, dept_type, sort_order) VALUES
7
+(1,  NULL, '精河县水利局', 'water_bureau', 1),
8
+(2,  NULL, '安阜清源水务公司', 'water_company', 2),
9
+(3,  2, '一体化水厂', 'water_company', 1),
10
+(4,  2, '精芒片区', 'water_company', 2),
11
+(5,  2, '八家户片区', 'water_company', 3),
12
+(6,  2, '托里片区', 'water_company', 4),
13
+(7,  2, '大镇阿合其片区', 'water_company', 5),
14
+(8,  2, '托托片区', 'water_company', 6),
15
+(9,  2, '运维中心', 'ops', 7)
16
+ON CONFLICT DO NOTHING;
17
+
18
+-- 重置序列
19
+SELECT setval('sys_dept_id_seq', (SELECT MAX(id) FROM sys_dept));
20
+
21
+-- 插入角色
22
+INSERT INTO sys_role (id, role_name, role_key, role_sort, data_scope, remark) VALUES
23
+(1, '系统管理员', 'admin', 1, 'ALL', '最高权限,管理全部功能'),
24
+(2, '水务分管领导', 'leader', 2, 'ALL', '查看全部数据、BI看板'),
25
+(3, '水务业务管理人员', 'manager', 3, 'DEPT', '调度、营收、水质管理'),
26
+(4, '现场运维操作人员', 'operator', 4, 'DEPT', '巡检、值班、工单处理'),
27
+(5, '系统技术维护人员', 'tech', 5, 'DEPT', '配置管理、设备维护')
28
+ON CONFLICT (role_key) DO NOTHING;
29
+
30
+-- 插入管理员用户 (密码: admin123, BCrypt加密)
31
+INSERT INTO sys_user (id, dept_id, username, password, real_name, role_type, status) VALUES
32
+(1, 1, 'admin', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5Eh', '系统管理员', 'admin', 1)
33
+ON CONFLICT (username) DO NOTHING;
34
+
35
+-- 管理员关联角色
36
+INSERT INTO sys_user_role (user_id, role_id) VALUES (1, 1) ON CONFLICT DO NOTHING;
37
+
38
+-- 插入字典类型
39
+INSERT INTO sys_dict_type (id, dict_key, dict_name) VALUES
40
+(1, 'device_type', '设备类型'),
41
+(2, 'area', '片区'),
42
+(3, 'customer_type', '用水户类型'),
43
+(4, 'alert_level', '报警等级'),
44
+(5, 'meter_status', '水表状态')
45
+ON CONFLICT (dict_key) DO NOTHING;
46
+
47
+-- 插入字典数据
48
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, sort_order) VALUES
49
+-- 设备类型
50
+(1, '流量计', 'flow_meter', 1),
51
+(1, '压力传感器', 'pressure_sensor', 2),
52
+(1, '电动阀门', 'valve', 3),
53
+(1, '水质监测仪', 'water_quality', 4),
54
+(1, '液位计', 'level_sensor', 5),
55
+(1, '水泵', 'pump', 6),
56
+-- 片区
57
+(2, '一体化水厂', '一体化水厂', 1),
58
+(2, '精芒片区', '精芒片区', 2),
59
+(2, '八家户片区', '八家户片区', 3),
60
+(2, '托里片区', '托里片区', 4),
61
+(2, '大镇阿合其片区', '大镇阿合其片区', 5),
62
+(2, '托托片区', '托托片区', 6),
63
+-- 用水户类型
64
+(3, '居民', 'residential', 1),
65
+(3, '商业', 'business', 2),
66
+(3, '企业', 'enterprise', 3),
67
+(3, '事业单位', 'institution', 4),
68
+-- 报警等级
69
+(4, '提示', 'info', 1),
70
+(4, '一般', 'warning', 2),
71
+(4, '严重', 'critical', 3),
72
+(4, '紧急', 'emergency', 4),
73
+-- 水表状态
74
+(5, '正常使用', 'active', 1),
75
+(5, '已拆除', 'dismantled', 2),
76
+(5, '已报废', 'scrapped', 3),
77
+(5, '维修中', 'repaired', 4),
78
+(5, '库存', 'warehouse', 5)
79
+ON CONFLICT DO NOTHING;

+ 68
- 0
db/tdengine/init.sql ファイルの表示

@@ -0,0 +1,68 @@
1
+-- =============================================
2
+-- 智慧水务管理系统 - TDengine 建表脚本
3
+-- 用于物联网设备遥测数据存储
4
+-- =============================================
5
+
6
+-- 创建数据库
7
+CREATE DATABASE IF NOT EXISTS water_iot
8
+  KEEP 365
9
+  DURATION 30
10
+  BUFFER 256
11
+  PAGES 256
12
+  PAGESIZE 4
13
+  WAL_LEVEL 1;
14
+
15
+USE water_iot;
16
+
17
+-- 遥测数据超级表
18
+CREATE STABLE IF NOT EXISTS iot_telemetry (
19
+    ts TIMESTAMP,
20
+    device_sn NCHAR(100),
21
+    metric_key NCHAR(50),
22
+    metric_value DOUBLE,
23
+    quality TINYINT
24
+) TAGS (
25
+    device_type NCHAR(30),
26
+    area NCHAR(50),
27
+    station NCHAR(100)
28
+);
29
+
30
+-- 按设备类型创建子表(模板,实际运行时动态创建)
31
+CREATE TABLE IF NOT EXISTS telemetry_flow_meter    USING iot_telemetry TAGS('flow_meter', 'default', 'default');
32
+CREATE TABLE IF NOT EXISTS telemetry_pressure      USING iot_telemetry TAGS('pressure_sensor', 'default', 'default');
33
+CREATE TABLE IF NOT EXISTS telemetry_valve         USING iot_telemetry TAGS('valve', 'default', 'default');
34
+CREATE TABLE IF NOT EXISTS telemetry_water_quality USING iot_telemetry TAGS('water_quality', 'default', 'default');
35
+CREATE TABLE IF NOT EXISTS telemetry_level         USING iot_telemetry TAGS('level_sensor', 'default', 'default');
36
+CREATE TABLE IF NOT EXISTS telemetry_pump          USING iot_telemetry TAGS('pump', 'default', 'default');
37
+
38
+-- 设备事件超级表
39
+CREATE STABLE IF NOT EXISTS iot_device_event (
40
+    ts TIMESTAMP,
41
+    device_sn NCHAR(100),
42
+    event_type NCHAR(30),
43
+    event_data NCHAR(2000)
44
+) TAGS (
45
+    device_type NCHAR(30),
46
+    area NCHAR(50)
47
+);
48
+
49
+-- 小时聚合超级表(自动聚合)
50
+CREATE STABLE IF NOT EXISTS iot_telemetry_hourly (
51
+    ts TIMESTAMP,
52
+    device_sn NCHAR(100),
53
+    metric_key NCHAR(50),
54
+    min_value DOUBLE,
55
+    max_value DOUBLE,
56
+    avg_value DOUBLE,
57
+    count_value INT
58
+) TAGS (
59
+    device_type NCHAR(30),
60
+    area NCHAR(50)
61
+);
62
+
63
+-- 日聚合视图(示例)
64
+-- CREATE STREAM IF NOT EXISTS hourly_stream INTO iot_telemetry_hourly AS
65
+-- SELECT _wstart as ts, device_sn, metric_key,
66
+--        MIN(metric_value), MAX(metric_value), AVG(metric_value), COUNT(*)
67
+-- FROM iot_telemetry
68
+-- INTERVAL(1h);