Files

1782 lines
65 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 智慧水务管理系统 — 详细设计规格书
> 版本:v2.0 | 日期:2026-06-14 | 作者:bot_pm
> 基于:requirements.md (需求) + architecture.md (架构)
> 用途:直接指导开发实现,每个 Issue 标注对应章节
---
## 第1章 项目框架与基础设施
### 1.1 Maven 模块结构
```
water-management-system/
├── pom.xml # 父 POM (Spring Boot 3.x + Cloud Alibaba 2024.x)
├── wm-common/ # 公共模块
│ └── src/main/java/com/water/common/
│ ├── core/
│ │ ├── result/R.java # 统一响应
│ │ ├── exception/BizException.java # 业务异常
│ │ ├── exception/GlobalExceptionHandler.java
│ │ └── constant/ErrorCode.java # 错误码枚举
│ ├── web/
│ │ ├── config/WebMvcConfig.java
│ │ └── interceptor/AuthInterceptor.java
│ └── util/
│ ├── FileUtil.java
│ ├── CaptchaUtil.java
│ └── DictUtil.java
├── wm-base/ # 基础服务 (8081)
│ └── src/main/java/com/water/base/
│ ├── controller/ # UserController, RoleController, DeptController...
│ ├── service/ # UserService, RoleService, DeptService...
│ ├── mapper/ # MyBatis-Plus Mapper
│ └── entity/ # SysUser, SysRole, SysDept, SysMenu, SysLog...
├── wm-gateway/ # API 网关 (8080)
│ └── src/main/resources/
│ └── application.yml # Spring Cloud Gateway 路由配置
├── wm-iot/ # 物联网平台 (8082)
├── wm-data-engine/ # 数据引擎 (8083)
├── wm-bpm/ # 流程引擎 (8084)
├── wm-production/ # 供水生产管理 (8085)
├── wm-revenue/ # 营业收费 (8086)
├── wm-patrol/ # 巡检管理 (8087)
├── wm-bi/ # 大数据分析 (8088)
├── wm-notify/ # 消息通知 (8089)
├── wm-job/ # 定时任务 (8090)
├── frontend/ # Vue3 前端
│ └── src/
│ ├── api/ # 接口封装
│ ├── views/ # 页面
│ ├── router/ # 路由
│ ├── stores/ # Pinia
│ └── components/ # 公共组件
├── mobile/ # Flutter APP
│ └── lib/
│ ├── pages/
│ ├── services/
│ ├── models/
│ └── widgets/
├── db/
│ ├── postgresql/ # PostgreSQL DDL 脚本
│ ├── tdengine/ # TDengine 建库脚本
│ └── seed/ # 种子数据
├── docker/ # Docker Compose
└── docs/ # 文档
```
**对应 Issue**:#17 项目框架搭建
### 1.2 数据库完整 DDL
> **对应 Issue**:#18 数据库设计
#### 1.2.1 系统管理表 (wm-base)
```sql
-- 部门表
CREATE TABLE sys_dept (
id BIGSERIAL PRIMARY KEY,
parent_id BIGINT DEFAULT 0,
dept_name VARCHAR(100) NOT NULL,
dept_type VARCHAR(30) DEFAULT 'water_company', -- water_bureau/water_company/ops
sort_order INT DEFAULT 0,
status SMALLINT DEFAULT 1, -- 1启用 0停用
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
COMMENT ON TABLE sys_dept IS '部门组织架构表';
COMMENT ON COLUMN sys_dept.dept_type IS 'water_bureau(水利局)/water_company(水务公司)/ops(运维单位)';
-- 用户表
CREATE TABLE sys_user (
id BIGSERIAL PRIMARY KEY,
dept_id BIGINT REFERENCES sys_dept(id),
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL, -- BCrypt 加密
real_name VARCHAR(50),
phone VARCHAR(20),
email VARCHAR(100),
role_type VARCHAR(30) DEFAULT 'operator', -- admin/leader/manager/operator/tech
status SMALLINT DEFAULT 1, -- 1启用 0停用
last_login_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
COMMENT ON TABLE sys_user IS '系统用户表';
COMMENT ON COLUMN sys_user.role_type IS 'admin(系统管理员)/leader(分管领导)/manager(业务管理)/operator(现场运维)/tech(技术维护)';
-- 角色表
CREATE TABLE sys_role (
id BIGSERIAL PRIMARY KEY,
role_name VARCHAR(50) UNIQUE NOT NULL,
role_key VARCHAR(50) UNIQUE NOT NULL, -- admin/supervisor/biz_manager/field_ops/tech_maintain
data_scope VARCHAR(20) DEFAULT 'DEPT', -- ALL/DEPT/SELF
description VARCHAR(200),
status SMALLINT DEFAULT 1,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- 角色-菜单权限
CREATE TABLE sys_role_menu (
role_id BIGINT REFERENCES sys_role(id),
menu_id BIGINT REFERENCES sys_menu(id),
PRIMARY KEY (role_id, menu_id)
);
-- 用户-角色关联 (多角色)
CREATE TABLE sys_user_role (
user_id BIGINT REFERENCES sys_user(id),
role_id BIGINT REFERENCES sys_role(id),
PRIMARY KEY (user_id, role_id)
);
-- 菜单表
CREATE TABLE sys_menu (
id BIGSERIAL PRIMARY KEY,
parent_id BIGINT DEFAULT 0,
menu_name VARCHAR(50) NOT NULL,
menu_type VARCHAR(20) DEFAULT 'menu', -- menu/button
path VARCHAR(200), -- 前端路由
component VARCHAR(200), -- 前端组件
icon VARCHAR(100),
permission VARCHAR(100), -- 权限标识 e.g. production:alert:confirm
sort_order INT DEFAULT 0,
visible SMALLINT DEFAULT 1,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 操作日志表
CREATE TABLE sys_log (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT,
username VARCHAR(50),
operation VARCHAR(100), -- 操作类型 e.g. LOGIN/DELETE/UPDATE
method VARCHAR(200), -- 请求方法
params TEXT, -- 请求参数 JSON
ip VARCHAR(50),
duration_ms INT, -- 执行耗时
status SMALLINT DEFAULT 1, -- 1成功 0失败
error_msg TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_sys_log_user_id ON sys_log(user_id);
CREATE INDEX idx_sys_log_created_at ON sys_log(created_at);
```
#### 1.2.2 物联网设备表 (wm-iot)
```sql
-- 设备模型定义
CREATE TABLE iot_device_model (
id BIGSERIAL PRIMARY KEY,
model_key VARCHAR(50) UNIQUE NOT NULL, -- e.g. water_meter_dn15
model_name VARCHAR(100) NOT NULL, -- DN15远传水表
vendor VARCHAR(100), -- 厂商名称
protocol VARCHAR(30) NOT NULL, -- MQTT/Modbus/CoAP/HTTP
properties JSONB NOT NULL DEFAULT '[]', -- [{key, name, unit, data_type, range_min, range_max}]
commands JSONB DEFAULT '[]', -- [{key, name, params:[{key,type,required}]}]
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- 协议适配器配置
CREATE TABLE iot_protocol_adapter (
id BIGSERIAL PRIMARY KEY,
adapter_key VARCHAR(30) UNIQUE NOT NULL, -- mqtt/modbus/coap/http
adapter_name VARCHAR(50) NOT NULL,
config JSONB NOT NULL DEFAULT '{}', -- 连接参数 e.g. {host, port, keepalive}
enabled SMALLINT DEFAULT 1,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 设备实例
CREATE TABLE iot_device (
id BIGSERIAL PRIMARY KEY,
device_sn VARCHAR(100) UNIQUE NOT NULL, -- 设备序列号
device_name VARCHAR(200) NOT NULL,
model_id BIGINT REFERENCES iot_device_model(id),
device_type VARCHAR(30) NOT NULL, -- flow_meter/pressure/valve/water_quality/level
position_type VARCHAR(30), -- water_plant/pressure_station/pipe_network/village
area VARCHAR(50) NOT NULL, -- 一体化水厂/精芒片区/八家户片区/托里片区/大镇阿合其/托托片区
station_id BIGINT, -- 所属站点
loc_lng DOUBLE PRECISION, -- 经度
loc_lat DOUBLE PRECISION, -- 纬度
geom GEOMETRY(Point, 4326), -- PostGIS 空间坐标
status VARCHAR(20) DEFAULT 'offline', -- online/offline/maintenance/fault
firmware_ver VARCHAR(30),
last_report_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_iot_device_area ON iot_device(area);
CREATE INDEX idx_iot_device_type ON iot_device(device_type);
CREATE INDEX idx_iot_device_status ON iot_device(status);
CREATE INDEX idx_iot_device_geom ON iot_device USING GIST(geom);
-- 设备遥测数据 (实时缓存表,最终入TDengine)
CREATE TABLE iot_device_telemetry (
id BIGSERIAL PRIMARY KEY,
device_id BIGINT REFERENCES iot_device(id),
device_sn VARCHAR(100) NOT NULL,
metric_key VARCHAR(50) NOT NULL, -- flow_rate/pressure/turbidity/ph/chlorine/level...
metric_value DOUBLE PRECISION,
reported_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_iot_telemetry_device ON iot_device_telemetry(device_sn, metric_key, reported_at);
-- 设备影子 (Redis 为主,PostgreSQL 为灾备持久化)
CREATE TABLE iot_device_shadow (
id BIGSERIAL PRIMARY KEY,
device_sn VARCHAR(100) UNIQUE NOT NULL,
reported_state JSONB DEFAULT '{}', -- 设备上报的最新状态
desired_state JSONB DEFAULT '{}', -- 期望状态(待下发)
version BIGINT DEFAULT 1, -- 乐观锁版本号
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- OTA 固件版本
CREATE TABLE iot_firmware (
id BIGSERIAL PRIMARY KEY,
model_key VARCHAR(50) NOT NULL,
version VARCHAR(30) NOT NULL,
file_url VARCHAR(500) NOT NULL, -- MinIO 存储路径
file_size BIGINT,
checksum VARCHAR(64), -- SHA256
changelog TEXT,
status VARCHAR(20) DEFAULT 'draft', -- draft/published/deprecated
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- OTA 升级任务
CREATE TABLE iot_ota_task (
id BIGSERIAL PRIMARY KEY,
firmware_id BIGINT REFERENCES iot_firmware(id),
device_sn VARCHAR(100) NOT NULL,
status VARCHAR(20) DEFAULT 'pending', -- pending/downloading/installing/success/failed
progress SMALLINT DEFAULT 0, -- 0-100
error_msg TEXT,
started_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
```
#### 1.2.3 时序数据库 (TDengine)
```sql
-- 超级表:设备遥测时序数据
CREATE STABLE iot_telemetry (
ts TIMESTAMP,
device_sn NCHAR(100),
metric_key NCHAR(50),
metric_value DOUBLE,
quality TINYINT -- 数据质量 0异常 1正常
) TAGS (
device_type NCHAR(30),
area NCHAR(50)
);
-- 按设备类型创建子表
CREATE TABLE flow_meter_data USING iot_telemetry TAGS('flow_meter', 'default');
CREATE TABLE pressure_data USING iot_telemetry TAGS('pressure', 'default');
CREATE TABLE level_data USING iot_telemetry TAGS('level', 'default');
CREATE TABLE water_quality_data USING iot_telemetry TAGS('water_quality', 'default');
CREATE TABLE valve_status_data USING iot_telemetry TAGS('valve', 'default');
-- 小时级聚合表
CREATE TABLE iot_telemetry_hourly (
ts TIMESTAMP,
device_sn NCHAR(100),
metric_key NCHAR(50),
avg_value DOUBLE,
min_value DOUBLE,
max_value DOUBLE,
sample_count INT
) TAGS (
device_type NCHAR(30),
area NCHAR(50)
);
```
#### 1.2.4 营业收费表 (wm-revenue)
```sql
-- 用水户档案
CREATE TABLE rev_customer (
id BIGSERIAL PRIMARY KEY,
customer_no VARCHAR(30) UNIQUE NOT NULL, -- 户号
customer_name VARCHAR(100) NOT NULL,
customer_type VARCHAR(20) NOT NULL, -- residential/business/enterprise/institution
area VARCHAR(50) NOT NULL,
address VARCHAR(300),
phone VARCHAR(20),
id_card VARCHAR(18),
contract_no VARCHAR(50),
status VARCHAR(20) DEFAULT 'active', -- active/inactive/cancelled
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_rev_customer_area ON rev_customer(area);
CREATE INDEX idx_rev_customer_type ON rev_customer(customer_type);
-- 水表档案 (全生命周期)
CREATE TABLE rev_meter (
id BIGSERIAL PRIMARY KEY,
meter_no VARCHAR(50) UNIQUE NOT NULL,
customer_id BIGINT REFERENCES rev_customer(id),
device_id BIGINT, -- 关联IoT设备(远传表)
caliber VARCHAR(10) NOT NULL, -- DN15/DN20/DN40/DN50/DN80/DN100...
meter_type VARCHAR(20) NOT NULL, -- mechanical/ultrasonic/electromagnetic/remote
initial_reading DECIMAL(12,3),
install_date DATE,
warranty_expire DATE,
status VARCHAR(20) DEFAULT 'in_stock', -- in_stock/installed/dismantled/scrapped/repairing/repaired
prev_status VARCHAR(20), -- 前状态(用于拆换追溯)
status_changed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
COMMENT ON TABLE rev_meter IS '水表全生命周期档案表';
CREATE INDEX idx_rev_meter_customer ON rev_meter(customer_id);
CREATE INDEX idx_rev_meter_status ON rev_meter(status);
-- 水表操作流水 (入库/出库/安装/拆换/报废/翻新/校表)
CREATE TABLE rev_meter_log (
id BIGSERIAL PRIMARY KEY,
meter_id BIGINT REFERENCES rev_meter(id),
operation VARCHAR(20) NOT NULL, -- stock_in/stock_out/install/dismantle/swap/scrap/renovate/calibrate
old_reading DECIMAL(12,3),
new_reading DECIMAL(12,3),
old_meter_no VARCHAR(50), -- 换表时的旧表编号
new_meter_no VARCHAR(50), -- 换表时的新表编号
operator_id BIGINT,
remark TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 抄表记录
CREATE TABLE rev_reading (
id BIGSERIAL PRIMARY KEY,
meter_id BIGINT REFERENCES rev_meter(id),
reading_date DATE NOT NULL,
prev_reading DECIMAL(12,3),
curr_reading DECIMAL(12,3),
consumption DECIMAL(12,3), -- 用水量 = curr - prev
read_type VARCHAR(20) NOT NULL, -- manual/remote/estimate
reader_id BIGINT,
photo_url VARCHAR(500),
verified SMALLINT DEFAULT 0, -- 0未核实 1已核实 2异常
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_rev_reading_date ON rev_reading(reading_date);
CREATE INDEX idx_rev_reading_meter ON rev_reading(meter_id, reading_date);
-- 水费账单
CREATE TABLE rev_bill (
id BIGSERIAL PRIMARY KEY,
customer_id BIGINT REFERENCES rev_customer(id),
meter_id BIGINT REFERENCES rev_meter(id),
bill_no VARCHAR(50) UNIQUE NOT NULL,
bill_period VARCHAR(10) NOT NULL, -- 2026-06
consumption DECIMAL(12,3),
water_fee DECIMAL(12,2),
sewage_fee DECIMAL(12,2),
other_fee DECIMAL(12,2) DEFAULT 0,
total_fee DECIMAL(12,2),
paid_fee DECIMAL(12,2) DEFAULT 0,
status VARCHAR(20) DEFAULT 'pending', -- pending/partial/paid/overdue/cancelled
due_date DATE,
paid_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_rev_bill_customer ON rev_bill(customer_id, bill_period);
CREATE INDEX idx_rev_bill_status ON rev_bill(status);
-- 支付记录
CREATE TABLE rev_payment (
id BIGSERIAL PRIMARY KEY,
bill_id BIGINT REFERENCES rev_bill(id),
pay_no VARCHAR(100) UNIQUE, -- 第三方支付单号
pay_channel VARCHAR(20) NOT NULL, -- counter/pos/alipay/wechat
pay_amount DECIMAL(12,2) NOT NULL,
pay_status VARCHAR(20) DEFAULT 'processing', -- processing/success/failed/refunded
pay_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 电子发票
CREATE TABLE rev_invoice (
id BIGSERIAL PRIMARY KEY,
bill_id BIGINT REFERENCES rev_bill(id),
invoice_no VARCHAR(50) UNIQUE NOT NULL,
invoice_type VARCHAR(20) DEFAULT 'electronic', -- paper/electronic/vat
amount DECIMAL(12,2) NOT NULL,
tax_amount DECIMAL(12,2) DEFAULT 0,
status VARCHAR(20) DEFAULT 'issued', -- issued/voided/red_invoiced
issued_at TIMESTAMPTZ DEFAULT NOW(),
voided_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 应收应付账务
CREATE TABLE rev_account (
id BIGSERIAL PRIMARY KEY,
customer_id BIGINT REFERENCES rev_customer(id),
account_type VARCHAR(20) NOT NULL, -- receivable/payable
amount DECIMAL(12,2) NOT NULL,
balance DECIMAL(12,2) NOT NULL,
period VARCHAR(10),
status VARCHAR(20) DEFAULT 'open', -- open/partial/closing/closed
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 水价阶梯配置
CREATE TABLE rev_water_price (
id BIGSERIAL PRIMARY KEY,
customer_type VARCHAR(20) NOT NULL, -- residential/business/enterprise
tier SMALLINT NOT NULL, -- 1/2/3
min_usage DECIMAL(12,3), -- 下限(m³)
max_usage DECIMAL(12,3), -- 上限(m³) NULL=无上限
unit_price DECIMAL(8,4) NOT NULL, -- 水价(元/m³)
sewage_price DECIMAL(8,4) DEFAULT 0, -- 污水处理费
effective_date DATE NOT NULL,
expired_date DATE,
created_at TIMESTAMPTZ DEFAULT NOW()
);
```
#### 1.2.5 报装管理表
```sql
-- 报装申请
CREATE TABLE rev_install (
id BIGSERIAL PRIMARY KEY,
app_no VARCHAR(30) UNIQUE NOT NULL, -- 申请编号
applicant_name VARCHAR(50) NOT NULL,
applicant_phone VARCHAR(20) NOT NULL,
customer_type VARCHAR(20) NOT NULL,
area VARCHAR(50),
address VARCHAR(300),
caliber VARCHAR(10), -- 申请管径
usage_type VARCHAR(30), -- 生活/商业/工业/消防
status VARCHAR(20) DEFAULT 'pre_accept', -- pre_accept/ site_survey/ engineering/ dispatched/ constructing/ completed/ cancelled
survey_result TEXT, -- 现场踏勘结论
designer_id BIGINT, -- 设计人员
constructor_id BIGINT, -- 施工人员
completed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_rev_install_status ON rev_install(status);
```
#### 1.2.6 客服与微信网厅
```sql
-- 知识库
CREATE TABLE cs_kb_article (
id BIGSERIAL PRIMARY KEY,
title VARCHAR(200) NOT NULL,
category VARCHAR(50) NOT NULL, -- policy/guide/common_qa/water_knowledge
content TEXT NOT NULL, -- Markdown
tags VARCHAR(500), -- 逗号分隔
view_count INT DEFAULT 0,
like_count INT DEFAULT 0,
status SMALLINT DEFAULT 1,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_cs_kb_category ON cs_kb_article(category);
-- 公告板
CREATE TABLE cs_announcement (
id BIGSERIAL PRIMARY KEY,
title VARCHAR(200) NOT NULL,
ann_type VARCHAR(30) NOT NULL, -- water_outage/water_quality/service/notice/news
content TEXT NOT NULL,
target_scope VARCHAR(50) DEFAULT 'all', -- all/{area}
publish_status VARCHAR(20) DEFAULT 'draft', -- draft/published/revoked
published_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 微信支付订单
CREATE TABLE rev_wx_pay_order (
id BIGSERIAL PRIMARY KEY,
bill_id BIGINT REFERENCES rev_bill(id),
wx_order_no VARCHAR(100) UNIQUE,
wx_transaction_id VARCHAR(100),
amount DECIMAL(12,2) NOT NULL,
status VARCHAR(20) DEFAULT 'created', -- created/paying/paid/refunding/refunded/closed
paid_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- FAQ
CREATE TABLE cs_faq (
id BIGSERIAL PRIMARY KEY,
question VARCHAR(500) NOT NULL,
answer TEXT NOT NULL,
category VARCHAR(50),
keywords VARCHAR(300),
priority INT DEFAULT 0,
view_count INT DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW()
);
```
#### 1.2.7 工单管理
```sql
CREATE TABLE wo_template (
id BIGSERIAL PRIMARY KEY,
template_name VARCHAR(100) NOT NULL,
wo_type VARCHAR(30) NOT NULL, -- repair/alert/meter_change/quality/scheduling
form_schema JSONB DEFAULT '{}', -- 表单字段定义
flow_def_key VARCHAR(100), -- 流程定义KEY
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE wo_order (
id BIGSERIAL PRIMARY KEY,
wo_no VARCHAR(30) UNIQUE NOT NULL,
template_id BIGINT REFERENCES wo_template(id),
wo_type VARCHAR(30) NOT NULL,
title VARCHAR(200) NOT NULL,
description TEXT,
priority VARCHAR(10) DEFAULT 'normal', -- low/normal/high/urgent
source VARCHAR(30), -- manual/alert_rules/patrol/scheduling
source_ref_id BIGINT, -- 来源关联ID (报警/巡检/Schedule)
assignee_id BIGINT,
creator_id BIGINT,
status VARCHAR(20) DEFAULT 'created', -- created/assigned/processing/completed/closed
resolved_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_wo_status ON wo_order(status);
CREATE INDEX idx_wo_assignee ON wo_order(assignee_id);
-- 工单处理记录
CREATE TABLE wo_process_log (
id BIGSERIAL PRIMARY KEY,
wo_id BIGINT REFERENCES wo_order(id),
action VARCHAR(30) NOT NULL, -- create/assign/accept/process/complete/close/reject
operator_id BIGINT,
comment TEXT,
attachments JSONB DEFAULT '[]', -- [{url, name, type}]
created_at TIMESTAMPTZ DEFAULT NOW()
);
```
#### 1.2.8 巡检系统
```sql
-- 巡检区域
CREATE TABLE patrol_area (
id BIGSERIAL PRIMARY KEY,
area_name VARCHAR(100) NOT NULL,
area_code VARCHAR(30) UNIQUE,
parent_id BIGINT DEFAULT 0,
sort_order INT DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 巡检路线
CREATE TABLE patrol_route (
id BIGSERIAL PRIMARY KEY,
route_name VARCHAR(100) NOT NULL,
area_id BIGINT REFERENCES patrol_area(id),
checkpoints JSONB NOT NULL, -- [{seq, name, lng, lat, device_ids:[], check_items:[{item,standard}]}]
estim_duration INT, -- 预计时长(分钟)
status SMALLINT DEFAULT 1,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 巡检表单模板
CREATE TABLE patrol_form_template (
id BIGSERIAL PRIMARY KEY,
template_name VARCHAR(100) NOT NULL,
category VARCHAR(30), -- device/water_quality/safety/hygiene
items JSONB NOT NULL, -- [{item, type(text/photo/number/select), required, options}]
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 巡检任务
CREATE TABLE patrol_task (
id BIGSERIAL PRIMARY KEY,
route_id BIGINT REFERENCES patrol_route(id),
assignee_id BIGINT,
plan_date DATE NOT NULL,
plan_start TIME,
plan_end TIME,
actual_start TIMESTAMPTZ,
actual_end TIMESTAMPTZ,
status VARCHAR(20) DEFAULT 'pending', -- pending/in_progress/completed/expired
distance DECIMAL(8,2), -- 实际里程(km)
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- GPS轨迹点
CREATE TABLE patrol_gps_track (
id BIGSERIAL PRIMARY KEY,
task_id BIGINT REFERENCES patrol_task(id),
lng DOUBLE PRECISION NOT NULL,
lat DOUBLE PRECISION NOT NULL,
altitude DOUBLE PRECISION,
accuracy DOUBLE PRECISION,
recorded_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_patrol_gps_task ON patrol_gps_track(task_id, recorded_at);
-- 巡检记录
CREATE TABLE patrol_record (
id BIGSERIAL PRIMARY KEY,
task_id BIGINT REFERENCES patrol_task(id),
checkpoint_seq INT NOT NULL, -- 路线第几个检查点
device_id BIGINT,
form_data JSONB NOT NULL, -- [{item, result, value, photo_url, remark}]
gps_lng DOUBLE PRECISION,
gps_lat DOUBLE PRECISION,
recorded_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- 巡检问题上报
CREATE TABLE patrol_issue (
id BIGSERIAL PRIMARY KEY,
task_id BIGINT REFERENCES patrol_task(id),
patrol_record_id BIGINT REFERENCES patrol_record(id),
issue_type VARCHAR(30) NOT NULL, -- device_fault/leak/quality/safety/other
severity VARCHAR(10) DEFAULT 'normal', -- normal/important/urgent
description TEXT NOT NULL,
photo_urls TEXT, -- JSON数组
voice_url VARCHAR(500),
wo_id BIGINT REFERENCES wo_order(id), -- 生成工单ID
status VARCHAR(20) DEFAULT 'reported', -- reported/assigned/processing/resolved
resolved_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 巡检统计缓存表
CREATE TABLE patrol_stats_daily (
id BIGSERIAL PRIMARY KEY,
stat_date DATE NOT NULL,
assignee_id BIGINT,
area_id BIGINT,
task_count INT DEFAULT 0,
completed_count INT DEFAULT 0,
total_distance DECIMAL(8,2),
total_duration INT, -- 分钟
issue_count INT DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE UNIQUE INDEX idx_patrol_stats_uk ON patrol_stats_daily(stat_date, assignee_id, area_id);
```
#### 1.2.9 供水生产管理
```sql
-- 能耗记录
CREATE TABLE prod_energy (
id BIGSERIAL PRIMARY KEY,
energy_type VARCHAR(20) NOT NULL, -- electricity/water_consumption
station_id BIGINT,
metric_value DOUBLE PRECISION,
unit VARCHAR(10) DEFAULT 'kWh',
recorded_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 药耗记录
CREATE TABLE prod_chemical (
id BIGSERIAL PRIMARY KEY,
chemical_type VARCHAR(30) NOT NULL, -- coagulant/disinfectant/flocculant
station_id BIGINT,
dosage_rate DOUBLE PRECISION, -- 投加速率
consumption DOUBLE PRECISION, -- 消耗量(kg)
recorded_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 水质检测点位
CREATE TABLE prod_quality_point (
id BIGSERIAL PRIMARY KEY,
point_name VARCHAR(100) NOT NULL,
point_type VARCHAR(30) NOT NULL, -- source/plant/pipe_end/network
area VARCHAR(50),
water_plant VARCHAR(100),
location VARCHAR(200),
sample_freq VARCHAR(20), -- daily/weekly/monthly
status SMALLINT DEFAULT 1,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 水质检测记录
CREATE TABLE prod_quality_record (
id BIGSERIAL PRIMARY KEY,
point_id BIGINT REFERENCES prod_quality_point(id),
test_date DATE NOT NULL,
test_type VARCHAR(20) DEFAULT 'manual', -- manual/auto
turbidity DOUBLE PRECISION, -- NTU
ph DOUBLE PRECISION,
residual_cl DOUBLE PRECISION, -- 余氯 mg/L
cod_mn DOUBLE PRECISION, -- 高锰酸盐指数
coliform INT, -- 总大肠菌群
qualified SMALLINT, -- 合格判定 (GB5749-2022)
tester_id BIGINT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 调度指令
CREATE TABLE prod_dispatch_cmd (
id BIGSERIAL PRIMARY KEY,
cmd_no VARCHAR(30) UNIQUE NOT NULL,
cmd_type VARCHAR(30) NOT NULL, -- valve_control/pump_start/flow_adjust/alert_response
cmd_content TEXT NOT NULL,
target_device VARCHAR(100),
priority VARCHAR(10) DEFAULT 'normal',
issuer_id BIGINT,
issued_at TIMESTAMPTZ DEFAULT NOW(),
deadline TIMESTAMPTZ,
status VARCHAR(20) DEFAULT 'issued', -- issued/received/executing/completed/rejected
executed_by BIGINT,
executed_at TIMESTAMPTZ,
result TEXT
);
-- 应急推演方案
CREATE TABLE prod_emergency_plan (
id BIGSERIAL PRIMARY KEY,
plan_name VARCHAR(100) NOT NULL,
scenario VARCHAR(30) NOT NULL, -- pipe_burst/water_quality/fire_supply/equipment_fault
steps JSONB NOT NULL, -- [{seq, action, target, duration, responsible_role}]
status SMALLINT DEFAULT 1,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 值班表
CREATE TABLE prod_duty_schedule (
id BIGSERIAL PRIMARY KEY,
schedule_date DATE NOT NULL,
shift VARCHAR(20) DEFAULT 'day', -- day/night
user_id BIGINT,
dept_id BIGINT,
phone VARCHAR(20),
status VARCHAR(20) DEFAULT 'on_duty',
handover_note TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
```
#### 1.2.10 报警管理
```sql
-- 报警规则
CREATE TABLE alert_rule (
id BIGSERIAL PRIMARY KEY,
rule_name VARCHAR(100) NOT NULL,
device_type VARCHAR(30),
metric_key VARCHAR(50) NOT NULL,
alert_level VARCHAR(10) NOT NULL, -- info/warning/critical/emergency
condition_expr VARCHAR(200) NOT NULL, -- "> 0.5" 或 "ABS_DELTA > 10" 或 "<= 0.2"
threshold_value VARCHAR(50),
debounce_sec INT DEFAULT 300, -- 去重窗口(秒)
notify_scheme JSONB DEFAULT '{"sms":false,"push":true,"ws":true}',
enabled SMALLINT DEFAULT 1,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 报警事件
CREATE TABLE alert_event (
id BIGSERIAL PRIMARY KEY,
rule_id BIGINT REFERENCES alert_rule(id),
device_id BIGINT,
device_sn VARCHAR(100),
metric_key VARCHAR(50),
metric_value DOUBLE PRECISION,
threshold VARCHAR(50),
alert_level VARCHAR(10),
message VARCHAR(500),
area VARCHAR(50),
confirmed_by BIGINT,
confirmed_at TIMESTAMPTZ,
dispatched SMALLINT DEFAULT 0, -- 是否生成工单
resolved_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_alert_level ON alert_event(alert_level, created_at);
CREATE INDEX idx_alert_device ON alert_event(device_sn, created_at);
```
#### 1.2.11 配置中心
```sql
-- 系统配置
CREATE TABLE sys_config (
id BIGSERIAL PRIMARY KEY,
config_key VARCHAR(100) UNIQUE NOT NULL,
config_value TEXT NOT NULL,
config_type VARCHAR(30) DEFAULT 'string', -- string/number/json/bool
description VARCHAR(300),
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- 阈值配置
CREATE TABLE sys_threshold (
id BIGSERIAL PRIMARY KEY,
metric_key VARCHAR(50) NOT NULL,
area VARCHAR(50) DEFAULT 'all',
min_value DOUBLE PRECISION,
max_value DOUBLE PRECISION,
unit VARCHAR(20),
description VARCHAR(200),
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 文档管理
CREATE TABLE sys_document (
id BIGSERIAL PRIMARY KEY,
doc_name VARCHAR(200) NOT NULL,
doc_category VARCHAR(50), -- manual/report/standard/other
file_url VARCHAR(500) NOT NULL,
file_size BIGINT,
file_type VARCHAR(20),
version INT DEFAULT 1,
tags VARCHAR(300),
uploader_id BIGINT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_sys_doc_category ON sys_document(doc_category);
```
### 1.3 认证授权系统设计
> **对应 Issue**:#19 认证授权系统
**职责边界**:提供统一认证(登录/JWT签发/Token校验/刷新)、RBAC角色权限管理、数据权限(按片区/部门隔离)。
#### 认证流程
```
用户 → POST /api/auth/login {username, password}
→ Sa-Token 校验密码 (BCrypt)
→ 签发 JWT (含 userId, roleKeys[])
→ 返回 {token, refreshToken, userInfo}
后续请求 → Header: Authorization: Bearer <token>
→ Sa-Token 拦截器校验
→ @SaCheckPermission 注解校验菜单权限
→ AOP 切面注入数据权限 scope → SQL 自动拼接 WHERE area IN (...)
```
#### API 端点
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | /api/auth/login | 用户名密码登录,返回 JWT |
| POST | /api/auth/refresh | 刷新 Token |
| POST | /api/auth/logout | 退出登录 |
| GET | /api/auth/current | 获取当前用户信息+权限列表 |
| POST | /api/oauth2/token | OAuth2 Token端点 |
| POST | /api/oauth2/authorize | OAuth2 授权端点 |
| POST | /api/sso/clients | 注册 SSO 客户端应用 |
| GET | /api/sso/clients | 查询已注册应用列表 |
| DELETE | /api/sso/clients/{id} | 注销应用 |
#### 数据权限策略
```java
// 数据权限注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataScope {
String column() default "area"; // 需要过滤的字段
String deptColumn() default "dept_id";
}
// SQL 拦截器自动拼接: AND area IN (用户的片区列表)
```
**模块依赖**:
- 依赖:wm-base(用户/角色表)
- 提供:给所有微服务提供 JWT 认证 + 数据权限
### 1.4 GIS 引擎集成
> **对应 Issue**:#21 GIS引擎集成
**部署架构**:
```
GeoServer 2.25 (Tomcat)
├── Workspace: water_supply
│ ├── DataStore: postgis_water (连接 PostgreSQL)
│ ├── Layer: iot_device (监测点位 WMS)
│ ├── Layer: pipeline (管网矢量 WMS)
│ ├── Layer: dma_zones (DMA 分区面 WMS)
│ └── Layer group: 综合管网图
└── Cache: GeoWebCache (WMTS 瓦片加速)
```
**前端分层**:
```
Leaflet (2D)
├── 底图: 高德/天地图 WMTS
├── 管网图层: GeoServer WMS
├── 监测点位: GeoJSON 动态图层 (轮询 30s)
│ ├── Marker: 流量/压力/液位/水质/阀门 (不同图标+颜色)
│ └── Popup: 实时数据面板
├── 热力图: leaflet.heat (点位密度)
├── DMA 分区: GeoJSON 面图层 (漏损率颜色)
└── 轨迹回放: Leaflet.MovingMarker (巡检GPS)
```
**关键 API**:
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | /api/gis/points | 获取监测点位列表 (含坐标/类型/实时值) |
| GET | /api/gis/points/heatmap | 获取热力图数据 |
| GET | /api/gis/pipelines | 获取管网数据 |
| GET | /api/gis/spatial-query | 矩形/圆形空间查询 |
| GET | /api/gis/wms-proxy/** | GeoServer WMS 代理 |
**模块依赖**:
- 依赖:wm-iot(设备坐标数据)、PostGIS 扩展
- 提供:给供水生产(地图监测)、巡检(路线/GPS轨迹)、DMA(分区面图层)
### 1.5 IoT 设备接入层
> **对应 Issue**:#22 IoT接入层
**EMQX 配置要点**:
```
# emqx.conf
listener.tcp.external = 1883
listener.ssl.external = 8883
# 设备认证
listener.tcp.external.enable_authn = true
# HTTP 回调认证
auth.http.auth_req.url = "http://wm-iot:8082/api/iot/auth/device"
# Kafka 桥接
bridges.kafka.iot_raw.servers = "kafka:9092"
bridges.kafka.iot_raw.topic = "iot.raw.${device_type}"
```
**Kafka Topic 规划**:
| Topic | 用途 | 分区数 | 保留时间 |
|-------|------|:---:|:---:|
| iot.raw.flow_meter | 流量计原始数据 | 6 | 7d |
| iot.raw.pressure | 压力传感器原始数据 | 3 | 7d |
| iot.raw.water_quality | 水质传感器原始数据 | 3 | 7d |
| iot.raw.level | 液位计原始数据 | 3 | 7d |
| iot.raw.valve | 阀门状态数据 | 3 | 7d |
| iot.cmd.downlink | 下行指令 | 3 | 1d |
| iot.event.online | 设备上下线事件 | 1 | 30d |
| iot.event.alarm | 报警事件 | 3 | 90d |
**API 端点**:
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | /api/iot/device | 注册设备 |
| GET | /api/iot/device | 设备列表(分页/按类型/片区/状态过滤) |
| GET | /api/iot/device/{sn} | 设备详情 |
| PUT | /api/iot/device/{sn} | 更新设备信息 |
| DELETE | /api/iot/device/{sn} | 注销设备 |
| POST | /api/iot/device/{sn}/cmd | 下发指令 |
| GET | /api/iot/telemetry/{sn} | 查询设备实时遥测数据 |
### 1.6 消息通知模块
> **对应 Issue**:#26 消息通知模块
**多渠道策略**:
| 渠道 | 技术 | 适用场景 |
|------|------|----------|
| WebSocket | Spring WebSocket + STOMP | 实时报警弹窗、网页端通知 |
| APP 推送 | 极光推送 (JPush) | 移动端报警、待办提醒 |
| 短信 | 阿里云 SMS | 紧急报警、停水通知 |
| 邮件 | JavaMail + 模板 | 日报周报推送 |
**通知模板表**:
```sql
CREATE TABLE notify_template (
id BIGSERIAL PRIMARY KEY,
template_code VARCHAR(50) UNIQUE NOT NULL,
template_name VARCHAR(100),
channels VARCHAR(50) DEFAULT 'ws', -- ws,sms,push,email 逗号分隔
title_tpl VARCHAR(200),
content_tpl TEXT NOT NULL, -- 支持变量 ${device_sn} ${metric_key} ${value}
created_at TIMESTAMPTZ DEFAULT NOW()
);
```
---
## 第2章 物联网平台
### 2.1 多协议适配器
> **对应 Issue**:#28 MQTT 协议适配器 + 设备注册/发现, #29 Modbus/CoAP/HTTP 协议适配
**适配器接口**:
```java
public interface DeviceAdapter {
String getProtocol(); // "MQTT"/"Modbus"/"CoAP"/"HTTP"
void onMessage(byte[] payload); // 处理设备上行数据
void sendCommand(String deviceSn, Command cmd); // 发送下行指令
DeviceInfo parseDeviceInfo(byte[] payload); // 解析设备注册信息
}
```
**AdapterFactory**:
```java
@Component
public class AdapterFactory {
private final Map<String, DeviceAdapter> adapters = new HashMap<>();
public void register(String protocol, DeviceAdapter adapter) { ... }
public DeviceAdapter get(String protocol) { ... }
}
```
**MQTT 适配器核心逻辑**:
```
订阅 iot.raw.* → 解析 JSON payload → DeviceModelMapper.convert()
→ 写入 Kafka (iot.raw.{device_type})
→ 写入 Redis (最新值缓存)
→ 判断报警规则 → 触发报警
```
**数据格式转换(DeviceModelMapper)**:
```java
// 统一设备上报格式
{
"deviceSn": "FLOW_001",
"ts": 1718340000000,
"metrics": [
{"key": "flow_rate", "value": 12.5, "unit": "m³/h"},
{"key": "total_flow", "value": 15230.8, "unit": "m³"}
]
}
```
### 2.2 设备影子服务
> **对应 Issue**:#30 设备影子服务 + OTA 固件升级
**Redis 存储结构**:
```
Key: shadow:{deviceSn}
Value: {
"reported": {"flow_rate": 12.5, "status": "online"},
"desired": {"report_interval": 60},
"version": 5,
"lastSync": 1718340000000
}
```
**影子同步流程**:
```
设备上报 → adapter.onMessage()
→ Redis: shadow.{sn}.reported = 最新状态
→ 检查 desired 是否有待下发指令 → 下发
→ 指令下发后 → iot.cmd.downlink → EMQX → 设备
设备离线 → 指令缓存到 Redis desired
设备上线 → 拉取 desired → 逐条下发 → 确认后清除
```
### 2.3 IoT 前端页面
> **对应 Issue**:#31 设备管理前端页面
**路由**:
```
/iot/devices → DeviceListView.vue (表格+筛选)
/iot/devices/:sn → DeviceDetailView.vue (详情+遥测图表)
/iot/devices/map → DeviceMapView.vue (GIS地图叠加)
/iot/ota → FirmwareListView.vue (固件管理)
```
**DeviceListView** 组件树:
```
DeviceListView
├── SearchBar (片区下拉/设备类型下拉/状态多选/关键词输入)
├── DeviceTable (分页表格)
│ └── columns: SN/名称/类型/片区/状态/最后上报时间/操作
└── DeviceFormDialog (新增/编辑 弹窗)
```
---
## 第3章 流程引擎
> **对应 Issue**:#32 BPM流程核心, #33 BPMN.js设计器, #34 流程统计, #35 跨系统编排
### 3.1 Camunda 集成
**职责边界**:基于 Camunda 7 提供 BPMN 2.0 流程引擎,封装流程定义、启动、任务处理 API。
**Camunda 配置**:
```yaml
camunda.bpm:
admin-user:
id: admin
password: ${CAMUNDA_PASSWORD}
database:
type: postgres
history-level: full
auto-deployment-enabled: true
deployment-resource-pattern: classpath:/processes/*.bpmn
```
**API 端点**:
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | /api/bpm/process/deploy | 部署 BPMN 流程定义 |
| POST | /api/bpm/process/start | 启动流程实例 |
| GET | /api/bpm/task/todo | 查询待办任务 (分页/筛选) |
| GET | /api/bpm/task/done | 查询已办任务 |
| POST | /api/bpm/task/{taskId}/approve | 审批通过 |
| POST | /api/bpm/task/{taskId}/reject | 驳回 |
| POST | /api/bpm/task/{taskId}/transfer | 转办 |
| POST | /api/bpm/task/{taskId}/delegate | 委派 |
| POST | /api/bpm/task/{taskId}/add-sign | 加签 |
| GET | /api/bpm/instance/{id}/diagram | 获取流程进度图 |
| GET | /api/bpm/stats/efficiency | 流程效率统计 |
| GET | /api/bpm/stats/bottleneck | 瓶颈节点分析 |
**BPMN.js 设计器集成**:
```
前端: bpmn-js (BPMN 2.0 建模器)
├── 自定义属性面板 (扩展 assignee/candidateGroups/deadline)
├── 流程模板保存/加载 (bpmn XML → 后端存储)
└── 模板发布 → Camunda 自动部署
```
**跨系统编排**:
```java
// Webhook 回调机制
@Component
public class ProcessWebhookService {
// 流程节点执行完成后回调外部系统
public void fireEvent(String processInstanceId, String activityId) {
// 查询该节点的 Webhook 配置 → HTTP POST 回调
}
}
```
---
## 第4章 数据引擎
### 4.1 实时数据采集
> **对应 Issue**:#41 实时流数据采集
**Kafka Consumer 设计**:
```java
@KafkaListener(topics = "iot.raw.#{__listener.topicPattern}")
public void consumeIotData(ConsumerRecord<String, String> record) {
TelemetryData data = parseMessage(record.value());
// 1. 写入 TDengine (批量写入 1000条/5秒)
tdengineBuffer.add(data);
// 2. 写入 Redis (最新值)
redisTemplate.opsForHash().put("telemetry:latest", data.getDeviceSn()+":"+data.getMetricKey(), data);
// 3. 触发报警规则检查
alertChecker.check(data);
}
```
### 4.2 数据接入层
> **对应 Issue**:#42 数据接入层
**多种接入方式**:
| 方式 | 端点 | 说明 |
|------|------|------|
| REST Push | POST /api/data/ingest/rest | 第三方系统推送 JSON |
| WebSocket | ws://host/data/ws | 实时数据流双向通信 |
| 批量导入 | POST /api/data/ingest/batch | CSV/Excel 文件上传解析 |
| 数据库直连 | JDBC Source Connector | 定时拉取外部数据库 |
**批量导入处理**:
```
上传文件 → 格式校验(文件头/列映射) → 数据预览(前100行)
→ 用户确认映射 → 异步处理(Spring @Async)
→ 逐行校验(水利标准字段映射) → 写入 TDengine/PostgreSQL
→ 返回处理结果(成功X条/失败Y条)
```
### 4.3 数据存储层
> **对应 Issue**:#43 数据存储层
**TDengine 存储策略**:
- 原始数据:保留 90 天 (iot_telemetry)
- 小时聚合:保留 3 年 (iot_telemetry_hourly)
- 日聚合:保留 10 年 (iot_telemetry_daily)
**MinIO 存储策略**:
- 文件按 `{bucket}/{yyyy}/{MM}/{dd}/{uuid}.{ext}` 命名
- 视频录像:保留 30 天 (lifecycle policy)
- 照片附件:保留 1 年
- 固件文件:永久保留
### 4.4 数据治理
> **对应 Issue**:#44 数据治理
**水利数据对象标准映射表**:
```sql
CREATE TABLE data_field_mapping (
id BIGSERIAL PRIMARY KEY,
source_field VARCHAR(100) NOT NULL, -- 原始字段名
source_system VARCHAR(50), -- 来源系统
target_field VARCHAR(100) NOT NULL, -- 标准字段名
data_type VARCHAR(20), -- VARCHAR/INT/DOUBLE/DATE
unit VARCHAR(20),
transform_rule VARCHAR(200), -- 转换规则 (e.g. *1000, DATE_FORMAT)
created_at TIMESTAMPTZ DEFAULT NOW()
);
```
**清洗规则链**:
```
原始数据 → 空值检查(填充/丢弃)→ 异常值检测(IQR/3σ) → 格式转换
→ 去重(时间窗口内) → 质量评分(0-100) → 入仓
```
---
## 第5章 供水生产管理
### 5.1 生产总览大屏
> **对应 Issue**:#61 总览大屏
**数据聚合 API**:
```
GET /api/production/dashboard/summary?area={area}
Response:
{
"area": "一体化水厂",
"todayInflow": 15230.5, // m³
"todayOutflow": 14890.2,
"yesterdayTotalSupply": 31000.0,
"sourceWaterQuality": { "turbidity": 3.2, "ph": 7.8 },
"treatedWaterQuality": { "turbidity": 0.3, "residualCl": 0.5, "qualified": true },
"activeAlerts": [...],
"deviceStats": { "total": 156, "online": 142, "fault": 3 },
"energy": { "electricity": 1250.5, "waterUse": 320.0 },
"chemical": { "coagulant": 85.2, "disinfectant": 12.5 }
}
```
**前端 ECharts 组件**:
```
DashboardView.vue
├── AreaSelector (片区下拉,系统管理员看全部)
├── SummaryCards (4个数字卡片: 进水/出水/昨日供水/设备在线率)
├── WaterInOutChart (进出水量趋势折线图,24h)
├── WaterQualityChart (水质指标仪表盘)
├── AlertList (实时报警滚动列表)
├── DeviceStatusPie (设备状态饼图)
├── EnergyBar (能耗柱状图)
└── ChemicalBar (药耗柱状图)
```
### 5.2 在线监测列表
> **对应 Issue**:#62 在线监测列表
**路由**:`/production/monitor`
```
MonitorListView.vue
├── FilterPanel
│ ├── 片区 (select: 6个片区)
│ ├── 位置类型 (select: 水厂/调压站/管网/村队)
│ ├── 设备类型 (select: 全部/流量计/压力/液位/水质/阀门)
│ ├── 状态 (checkbox: 在线/离线/故障)
│ └── 关键词搜索
├── MonitorTable
│ └── columns: 站点名称/片区/位置类型/设备SN/实时值(Multi-metric)/采集时间/状态
├── ExportButton (导出 Excel)
└── AutoRefresh (5s/10s/30s 切换)
```
### 5.3 视频监控与 AI
> **对应 Issue**:#63 视频监控+AI闯入检测
**视频流接入**:
```
摄像头(RTSP/GB28181) → NVR/视频平台 → RTSP 流地址
→ wm-production 代理 → 前端 HLS/WebRTC 播放
```
**AI 闯入检测**:
```
视频帧 → YOLOv8 推理 → 人员检测 → 闯入区域判断
→ 触发 Alert (level=emergency) → WebSocket 实时弹窗
→ 录制 30s 视频快照 → MinIO 存储
```
### 5.4 GIS 地图展示
> **对应 Issue**:#64 GIS地图展示
**点位分类展示**:
```
图层:
├── 流量计 (蓝色水滴)
├── 压力传感器 (绿色圆形)
├── 液位计 (青色三角形)
├── 水质传感器 (紫色菱形)
├── 阀门 (红色方形: 开/绿: 关)
└── 管网 (蓝色线条 → WMS 瓦片)
```
### 5.5 水质管控
> **对应 Issue**:#65 药剂投加监控, #66 水质检测台账
**全工艺监控参数**:
```
混凝: 进水浊度 → 絮凝剂投加速率
沉淀: 沉淀池液位、出水浊度
过滤: 滤池液位、过滤速度
消毒: 消毒剂投加速率 → 出厂水余氯
```
**合格判定 (GB5749-2022)**:
```java
public boolean isQualified(QualityRecord record) {
return record.getTurbidity() <= 1.0 // NTU
&& record.getPh() >= 6.5 && record.getPh() <= 8.5
&& record.getResidualCl() >= 0.3 // mg/L
&& record.getColiform() == 0;
}
```
### 5.6 调度工作台
> **对应 Issue**:#68 值班管理, #69 调度指令, #70 应急推演
**值班流程图**:
```
排班(按月) → 值班开始 → 实时监测+指令处理 → 交接班(记录日志) → 值班结束
```
**应急推演四场景**:
1. 爆管模拟:选择管线位置 → 分析阀门关闭策略 → 生成受影响区域 → 应急供水方案
2. 水质异常:选择污染源 → 扩散范围模拟 → 排放/关闭方案
3. 设备故障:选择设备 → 切换备用方案
4. 停水预案:区域选择 → 影响用户统计 → 通知方案
### 5.7 报警管理
> **对应 Issue**:#65(含药剂报警), #67(报警管理中心)
**报警生命周期**:
```
规则命中 → 创建 Alert(去重检查) → WebSocket推送 + APP推送
→ 未确认(持续提醒) → 确认 → 派单(生成工单) → 处理 → 恢复 → 归档
```
### 5.8 系统管理
> **对应 Issue**:#74 系统管理
**路由**:
```
/system/roles → 角色管理 (CRUD + 权限分配树)
/system/users → 用户管理 (CRUD + 角色分配)
/system/depts → 部门管理 (树形表格 + 拖拽排序)
/system/menus → 菜单管理 (树形表格)
/system/logs → 日志查看 (搜索 + 导出)
/system/dict → 字典管理 (CRUD)
```
---
## 第6章 营业收费
### 6.1 营收统一管理
> **对应 Issue**:#46 SSO 单点登录, #47 平台运维审计 + 用户授权
**OAuth2 授权码流程**:
```
用户访问客户端 → 重定向 /oauth2/authorize → 登录 → 授权确认
→ 回调 redirect_uri?code=xxx → 客户端换取 access_token
→ POST /oauth2/token {grant_type: authorization_code, code, client_id, client_secret}
→ {access_token, refresh_token, expires_in}
```
### 6.2 报装管理系统
> **对应 Issue**:#48 报装核心流程, #49 报装查询统计
**状态机**:
```
pre_accept → site_survey → engineering → dispatched → constructing → completed
↘ 驳回 → pre_accept (重新提交)
```
**报装首页概览 API**:
```
GET /api/revenue/install/overview
→ { total, pending, inProgress, completedThisMonth, avgDays }
```
### 6.3 抄表与收费
> **对应 Issue**:#50 抄表管理, #51 账单生成+多支付渠道
**阶梯水价计算公式**:
```java
// 以居民用水为例 (三阶梯):
// Tier 1: 0-15 m³ → 2.80 元/m³
// Tier 2: 15-25 m³ → 4.20 元/m³
// Tier 3: > 25 m³ → 8.40 元/m³
BigDecimal calcWaterFee(BigDecimal usage, String customerType) {
List<PriceTier> tiers = priceService.getTiers(customerType);
BigDecimal fee = BigDecimal.ZERO;
BigDecimal remaining = usage;
for (PriceTier tier : tiers) {
BigDecimal tierUsage = min(remaining, tier.getRange());
fee = fee.add(tierUsage.multiply(tier.getUnitPrice()));
remaining = remaining.subtract(tierUsage);
if (remaining.compareTo(BigDecimal.ZERO) <= 0) break;
}
return fee;
}
```
**支付渠道集成**:
| 渠道 | 接口 | 回调 |
|------|------|------|
| 支付宝 | /api/revenue/payment/alipay/create | /api/revenue/payment/alipay/notify |
| 微信支付 | /api/revenue/payment/wechat/create | /api/revenue/payment/wechat/notify |
| 柜台 | /api/revenue/payment/counter/pay | 无需回调 |
| POS | /api/revenue/payment/pos/pay | 无需回调 |
### 6.4 表务管理
> **对应 Issue**:#53 表务管理
**水表全生命周期状态机**:
```
采购入库(in_stock) → 安装出库 → 已安装(installed)
→ 暂拆(dismantled) → 复接(installed)
→ 故障换表 - 旧表(scrapped) + 新表(installed)
→ 周期换表 - 旧表(scrapped/renovated) + 新表(installed)
→ 销户撤表(scrapped)
已安装 → 校表(calibrating) → 已校表 → 重新安装 或 报废
```
**12 个操作**:入库、出库、安装、暂拆、复接、故障换表、周期换表、销户撤表、新表交回、旧表交回、报废、翻新。
### 6.5 集抄系统
> **对应 Issue**:#58 远传集抄, #59 DMA分区计量+漏损分析
**批量抄表**:
```
定时任务 (每日0点) → MQTT 批量抄表指令 → 远传水表响应
→ Kafka → 数据引擎解析 → 写入 rev_reading
→ 标记异常读数 (负值/突变/零值超过N天) → 生成核查工单
```
**DMA 分区漏损分析**:
```
DMA 入口流量计 - Σ(分区内所有户表) = 漏损量
夜间最小流量法 (02:00-04:00) → 排除正常用水 → 估算物理漏损
ECharts:
├── 分区漏损率柱状图
├── 漏损量趋势折线 (日/周/月)
├── 夜间最小流量趋势 (7天)
└── 供售水量对比 (桑基图 or 并排柱状)
```
### 6.6 工单管理
> **对应 Issue**:#60 工单管理
**工单流转**:
```
创建(手动/报警触发/巡检上报) → 分派(按片区/技能) → 接受 → 处理中
→ 处理完成(填写结果+拍照) → 审核 → 关闭
→ 驳回 → 处理中
```
**跨模块联动**:
- 报警中心 → 自动创建报警工单
- 巡检问题上报 → 自动创建巡检工单
- 设备故障 → 自动创建维修工单
- BPM 流程引擎 → 复杂审批流程
---
## 第7章 巡检管理系统
> **对应 Issue**:#75 巡检路线+任务, #76 巡检执行+GPS, #77 问题上报+工单, #78 统计分析
### 7.1 巡检配置
**路线设计**:
```json
{
"routeName": "托里片区巡检路线A",
"area": "托里片区",
"checkpoints": [
{"seq": 1, "name": "托里水厂进水口", "lng": 82.123, "lat": 44.567,
"deviceIds": [101,102], "checkItems": [{"item":"流量计读数","standard":"正常范围"},
{"item":"管道外观","standard":"无泄漏"}]}
]
}
```
### 7.2 巡检执行
**GPS 轨迹记录策略**:
- 巡检开始 → 启动 GPS 采集 (每 10s 上报一次坐标)
- 到达检查点 → 系统自动判断 (距离 < 30m)
- 填写检查项 → 拍照 → 签字确认
- 异常 → 问题上报 (拍照+文字+语音)
- 巡检结束 → 停止 GPS → 计算总里程
### 7.3 统计分析
**统计维度**:
```sql
-- 每日汇总 (由 cron 凌晨执行)
INSERT INTO patrol_stats_daily
SELECT
CURRENT_DATE, assignee_id, area_id,
COUNT(*),
SUM(CASE WHEN status='completed' THEN 1 ELSE 0 END),
SUM(distance),
SUM(EXTRACT(EPOCH FROM actual_end - actual_start)/60),
(SELECT COUNT(*) FROM patrol_issue WHERE task_id IN (...))
FROM patrol_task WHERE plan_date = CURRENT_DATE - 1
GROUP BY assignee_id, area_id
```
**统计 API**:
```
GET /api/patrol/stats/completion-rate?start=2026-06-01&end=2026-06-14
GET /api/patrol/stats/mileage?assigneeId={id}&month=2026-06
GET /api/patrol/stats/attendance?month=2026-06
GET /api/patrol/stats/issues?category=device_fault&start=2026-06-01
GET /api/patrol/rank?type=mileage&start=2026-06-01&end=2026-06-14&limit=20
```
---
## 第8章 移动APP
> **对应 Issue**:#79 Flutter统一入口, #80 供水管理移动端, #81 巡检+营收移动端
### 8.1 Flutter 项目结构
```
mobile/lib/
├── main.dart # App入口,路由配置
├── app.dart # MaterialApp + Provider 注入
├── pages/
│ ├── login/login_page.dart # 统一登录页 (SSO)
│ ├── home/home_page.dart # 三合一底部导航首页
│ ├── production/ # 供水管理
│ │ ├── monitor_page.dart # 实时监测列表
│ │ ├── alert_page.dart # 报警列表
│ │ └── dispatch_page.dart # 调度指令
│ ├── patrol/ # 巡检
│ │ ├── task_list_page.dart # 任务列表
│ │ ├── patrol_page.dart # 巡检执行 (GPS+拍照)
│ │ ├── gps_track_page.dart # 轨迹查看
│ │ └── issue_report_page.dart # 问题上报
│ └── revenue/ # 营收
│ ├── meter_read_page.dart # 移动抄表
│ ├── bill_query_page.dart # 账单查询
│ └── install_progress_page.dart # 报装进度
├── services/ # API 服务层
│ ├── auth_service.dart
│ ├── production_service.dart
│ ├── patrol_service.dart
│ └── revenue_service.dart
├── models/ # 数据模型
└── widgets/ # 公共组件
```
### 8.2 底部 Tab 导航
```
供水管理 | 巡检 | 营收
🏭 🔍 💰
```
### 8.3 离线缓存策略
巡检场景(山区/偏远站点可能无网络):
```dart
// 使用 Hive 本地存储
Future<void> syncTaskData() async {
if (await hasNetwork()) {
var tasks = await patrolService.fetchTasks();
await hiveBox.put('tasks', tasks);
}
}
// 离线巡检:数据暂存本地,网络恢复后自动上传
Future<void> reportIssue(Issue issue) async {
if (await hasNetwork()) {
await patrolService.reportIssue(issue);
} else {
await offlineBox.add(issue.toJson());
}
}
```
---
## 第9章 大数据分析
> **对应 Issue**:#36 ETL管道, #37 BI看板, #38 运营仪表盘, #39 需水量预测, #40 报告生成
### 9.1 ETL 数据管道
**Flink 流式处理**:
```
Kafka(iot.raw.*) → Flink 作业
→ 数据解析 → 异常过滤 → 聚合(1min窗口)
→ Sink: TDengine (时序) + PostgreSQL (汇总)
```
### 9.2 BI 看板集成
**推荐方案**:Apache Superset 嵌入
```
前端 iframe 嵌入 Superset Dashboard (SSO token 透传)
├── 供水运营总览
├── 营收分析
├── 巡检统计
└── 自定义 SQL 查询
```
### 9.3 专供水专题大屏
**大屏布局 (1920×1080)**:
```
┌─────────────────────────────────────────────────────┐
│ 精河县智慧水务综合管理平台 2026-06-14 14:30 │
├──────────┬──────────┬──────────┬──────────────────────┤
│ 今日进水 │ 今日出水 │ 昨日供水 │ 设备在线率 │
├──────────┴──────────┴──────────┴──────────────────────┤
│ 进出水量趋势 (折线图) │ 供水区域 GIS 地图 │
│ │ (监测点位+报警闪烁) │
├───────────────────────────┤ │
│ 水质达标率 (仪表盘) │ │
│ 报警分布 (饼图) │ │
│ 能耗药耗 (柱状图) │ │
├──────────────────────────────────────────────────────┤
│ 实时报警滚动 | 巡检任务统计 | 营收总额统计 │
└──────────────────────────────────────────────────────┘
```
### 9.4 需水量预测
**模型选择**:
- 短期 (24h):ARIMA 时间序列 (数据量要求低,可快速部署)
- 长期 (月):LSTM 神经网络 (需积累 > 12个月历史数据后启用)
**API**:
```
GET /api/bi/predict/water-demand?area={area}&hours=24
Response: [{ts, predicted, lowerBound, upperBound}, ...]
```
---
## 文档版本
| 版本 | 日期 | 修订内容 | 修订人 |
|------|------|----------|--------|
| v2.0 | 2026-06-14 | 详细设计规格书,含全部模块DB/API/组件/数据流 | bot_pm |
---
## 附录:Issue ↔ 章节对照表
| Issue # | 标题 | 设计文档章节 |
|:---:|------|------|
| 17 | 项目框架搭建 | §1.1 Maven模块结构 |
| 18 | 数据库设计 | §1.2 数据库完整DDL |
| 19 | 认证授权系统 | §1.3 认证授权系统设计 |
| 20 | 通用模块 | §1.1 wm-common + §1.3 统一响应 |
| 21 | GIS引擎集成 | §1.4 GIS引擎集成 |
| 22 | IoT设备接入层 | §1.5 IoT设备接入层 |
| 23 | 前端框架搭建 | §1.1 frontend/ 结构 + §5.1 组件树 |
| 24 | Flutter移动端框架 | §8.1 Flutter项目结构 |
| 25 | DevOps | §1.1 docker/ + §1.2 技术栈 |
| 26 | 消息通知模块 | §1.6 消息通知模块 |
| 28 | MQTT协议适配器 | §2.1 多协议适配器 |
| 29 | Modbus/CoAP/HTTP适配 | §2.1 AdapterFactory |
| 30 | 设备影子+OTA | §2.2 设备影子服务 |
| 31 | 设备管理前端 | §2.3 IoT前端页面 |
| 32 | BPM流程引擎核心 | §3.1 Camunda集成 |
| 33 | BPMN.js设计器 | §3.1 BPMN.js集成 |
| 34 | BPM统计评估 | §3.1 API端点 (stats) |
| 35 | BPM跨系统编排 | §3.1 Webhook回调 |
| 36 | ETL数据管道 | §9.1 ETL数据管道 |
| 37 | BI看板 | §9.2 BI看板集成 |
| 38 | 运营仪表盘+大屏 | §9.3 供水专题大屏 |
| 39 | 需水量预测 | §9.4 需水量预测 |
| 40 | 报告生成 | §9.4 报告生成 |
| 41 | 实时流数据采集 | §4.1 实时数据采集 |
| 42 | 数据接入层 | §4.2 数据接入层 |
| 43 | 数据存储层 | §4.3 数据存储层 |
| 44 | 数据治理 | §4.4 数据治理 |
| 46 | SSO单点登录 | §6.1 营收统一管理 |
| 48 | 报装核心流程 | §6.2 报装管理系统 |
| 49 | 报装查询统计 | §6.2 状态机+概览 |
| 50 | 抄表管理 | §6.3 抄表与收费 |
| 51 | 账单+多支付 | §6.3 阶梯水价+支付 |
| 53 | 表务管理 | §6.4 表务管理 |
| 54 | 客服工作台 | §1.2.6 cs_kb_article |
| 56 | 微信网厅前端 | §1.2.6 rev_wx_pay_order |
| 58 | 远传集抄 | §6.5 批量抄表 |
| 60 | 工单管理 | §6.6 工单管理 |
| 61 | 总览大屏 | §5.1 生产总览大屏 |
| 62 | 在线监测列表 | §5.2 在线监测列表 |
| 63 | 视频监控+AI | §5.3 视频监控与AI |
| 64 | GIS地图展示 | §5.4 GIS地图展示 |
| 65 | 药剂投加监控 | §5.5 水质管控 |
| 66 | 水质检测台账 | §5.5 合格判定 |
| 68 | 值班管理 | §5.6 调度工作台 |
| 69 | 调度指令 | §5.6 指令台账 |
| 70 | 应急推演 | §5.6 应急推演四场景 |
| 74 | 系统管理 | §5.8 系统管理 |
| 75 | 巡检路线+任务 | §7.1 巡检配置 |
| 76 | 巡检执行+GPS | §7.2 巡检执行 |
| 77 | 问题上报+工单 | §1.2.8 patrol_issue |
| 78 | 统计分析 | §7.3 统计分析 |
| 79 | Flutter统一入口 | §8.2 底部Tab导航 |
| 80 | 供水管理移动端 | §8.1 production/ 页面 |
| 81 | 巡检+营收移动端 | §8.1 patrol/ + revenue/ |
> 📌 已关闭 Issue 也列入对照表,方便追溯。