版本:v2.0 | 日期:2026-06-14 | 作者:bot_pm
基于:requirements.md (需求) + architecture.md (架构)
用途:直接指导开发实现,每个 Issue 标注对应章节
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 项目框架搭建
对应 Issue:#18 数据库设计
-- 部门表
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);
-- 设备模型定义
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()
);
-- 超级表:设备遥测时序数据
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)
);
-- 用水户档案
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()
);
-- 报装申请
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);
-- 知识库
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()
);
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()
);
-- 巡检区域
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);
-- 能耗记录
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()
);
-- 报警规则
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);
-- 系统配置
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);
对应 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 (...)
| 方法 | 路径 | 说明 |
|---|---|---|
| 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} | 注销应用 |
// 数据权限注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataScope {
String column() default "area"; // 需要过滤的字段
String deptColumn() default "dept_id";
}
// SQL 拦截器自动拼接: AND area IN (用户的片区列表)
模块依赖:
对应 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 代理 |
模块依赖:
对应 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} | 查询设备实时遥测数据 |
对应 Issue:#26 消息通知模块
多渠道策略:
| 渠道 | 技术 | 适用场景 |
|---|---|---|
| WebSocket | Spring WebSocket + STOMP | 实时报警弹窗、网页端通知 |
| APP 推送 | 极光推送 (JPush) | 移动端报警、待办提醒 |
| 短信 | 阿里云 SMS | 紧急报警、停水通知 |
| 邮件 | JavaMail + 模板 | 日报周报推送 |
通知模板表:
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()
);
对应 Issue:#28 MQTT 协议适配器 + 设备注册/发现, #29 Modbus/CoAP/HTTP 协议适配
适配器接口:
public interface DeviceAdapter {
String getProtocol(); // "MQTT"/"Modbus"/"CoAP"/"HTTP"
void onMessage(byte[] payload); // 处理设备上行数据
void sendCommand(String deviceSn, Command cmd); // 发送下行指令
DeviceInfo parseDeviceInfo(byte[] payload); // 解析设备注册信息
}
AdapterFactory:
@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):
// 统一设备上报格式
{
"deviceSn": "FLOW_001",
"ts": 1718340000000,
"metrics": [
{"key": "flow_rate", "value": 12.5, "unit": "m³/h"},
{"key": "total_flow", "value": 15230.8, "unit": "m³"}
]
}
对应 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 → 逐条下发 → 确认后清除
对应 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 (新增/编辑 弹窗)
职责边界:基于 Camunda 7 提供 BPMN 2.0 流程引擎,封装流程定义、启动、任务处理 API。
Camunda 配置:
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 自动部署
跨系统编排:
// Webhook 回调机制
@Component
public class ProcessWebhookService {
// 流程节点执行完成后回调外部系统
public void fireEvent(String processInstanceId, String activityId) {
// 查询该节点的 Webhook 配置 → HTTP POST 回调
}
}
对应 Issue:#41 实时流数据采集
Kafka Consumer 设计:
@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);
}
对应 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条)
对应 Issue:#43 数据存储层
TDengine 存储策略:
MinIO 存储策略:
{bucket}/{yyyy}/{MM}/{dd}/{uuid}.{ext} 命名对应 Issue:#44 数据治理
水利数据对象标准映射表:
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) → 入仓
对应 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 (药耗柱状图)
对应 Issue:#62 在线监测列表
路由:/production/monitor
MonitorListView.vue
├── FilterPanel
│ ├── 片区 (select: 6个片区)
│ ├── 位置类型 (select: 水厂/调压站/管网/村队)
│ ├── 设备类型 (select: 全部/流量计/压力/液位/水质/阀门)
│ ├── 状态 (checkbox: 在线/离线/故障)
│ └── 关键词搜索
├── MonitorTable
│ └── columns: 站点名称/片区/位置类型/设备SN/实时值(Multi-metric)/采集时间/状态
├── ExportButton (导出 Excel)
└── AutoRefresh (5s/10s/30s 切换)
对应 Issue:#63 视频监控+AI闯入检测
视频流接入:
摄像头(RTSP/GB28181) → NVR/视频平台 → RTSP 流地址
→ wm-production 代理 → 前端 HLS/WebRTC 播放
AI 闯入检测:
视频帧 → YOLOv8 推理 → 人员检测 → 闯入区域判断
→ 触发 Alert (level=emergency) → WebSocket 实时弹窗
→ 录制 30s 视频快照 → MinIO 存储
对应 Issue:#64 GIS地图展示
点位分类展示:
图层:
├── 流量计 (蓝色水滴)
├── 压力传感器 (绿色圆形)
├── 液位计 (青色三角形)
├── 水质传感器 (紫色菱形)
├── 阀门 (红色方形: 开/绿: 关)
└── 管网 (蓝色线条 → WMS 瓦片)
对应 Issue:#65 药剂投加监控, #66 水质检测台账
全工艺监控参数:
混凝: 进水浊度 → 絮凝剂投加速率
沉淀: 沉淀池液位、出水浊度
过滤: 滤池液位、过滤速度
消毒: 消毒剂投加速率 → 出厂水余氯
合格判定 (GB5749-2022):
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;
}
值班流程图:
排班(按月) → 值班开始 → 实时监测+指令处理 → 交接班(记录日志) → 值班结束
应急推演四场景:
对应 Issue:#65(含药剂报警), #67(报警管理中心)
报警生命周期:
规则命中 → 创建 Alert(去重检查) → WebSocket推送 + APP推送
→ 未确认(持续提醒) → 确认 → 派单(生成工单) → 处理 → 恢复 → 归档
对应 Issue:#74 系统管理
路由:
/system/roles → 角色管理 (CRUD + 权限分配树)
/system/users → 用户管理 (CRUD + 角色分配)
/system/depts → 部门管理 (树形表格 + 拖拽排序)
/system/menus → 菜单管理 (树形表格)
/system/logs → 日志查看 (搜索 + 导出)
/system/dict → 字典管理 (CRUD)
对应 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}
对应 Issue:#48 报装核心流程, #49 报装查询统计
状态机:
pre_accept → site_survey → engineering → dispatched → constructing → completed
↘ 驳回 → pre_accept (重新提交)
报装首页概览 API:
GET /api/revenue/install/overview
→ { total, pending, inProgress, completedThisMonth, avgDays }
对应 Issue:#50 抄表管理, #51 账单生成+多支付渠道
阶梯水价计算公式:
// 以居民用水为例 (三阶梯):
// 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 | 无需回调 |
对应 Issue:#53 表务管理
水表全生命周期状态机:
采购入库(in_stock) → 安装出库 → 已安装(installed)
→ 暂拆(dismantled) → 复接(installed)
→ 故障换表 - 旧表(scrapped) + 新表(installed)
→ 周期换表 - 旧表(scrapped/renovated) + 新表(installed)
→ 销户撤表(scrapped)
已安装 → 校表(calibrating) → 已校表 → 重新安装 或 报废
12 个操作:入库、出库、安装、暂拆、复接、故障换表、周期换表、销户撤表、新表交回、旧表交回、报废、翻新。
对应 Issue:#58 远传集抄, #59 DMA分区计量+漏损分析
批量抄表:
定时任务 (每日0点) → MQTT 批量抄表指令 → 远传水表响应
→ Kafka → 数据引擎解析 → 写入 rev_reading
→ 标记异常读数 (负值/突变/零值超过N天) → 生成核查工单
DMA 分区漏损分析:
DMA 入口流量计 - Σ(分区内所有户表) = 漏损量
夜间最小流量法 (02:00-04:00) → 排除正常用水 → 估算物理漏损
ECharts:
├── 分区漏损率柱状图
├── 漏损量趋势折线 (日/周/月)
├── 夜间最小流量趋势 (7天)
└── 供售水量对比 (桑基图 or 并排柱状)
对应 Issue:#60 工单管理
工单流转:
创建(手动/报警触发/巡检上报) → 分派(按片区/技能) → 接受 → 处理中
→ 处理完成(填写结果+拍照) → 审核 → 关闭
→ 驳回 → 处理中
跨模块联动:
路线设计:
{
"routeName": "托里片区巡检路线A",
"area": "托里片区",
"checkpoints": [
{"seq": 1, "name": "托里水厂进水口", "lng": 82.123, "lat": 44.567,
"deviceIds": [101,102], "checkItems": [{"item":"流量计读数","standard":"正常范围"},
{"item":"管道外观","standard":"无泄漏"}]}
]
}
GPS 轨迹记录策略:
统计维度:
-- 每日汇总 (由 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
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/ # 公共组件
供水管理 | 巡检 | 营收
🏭 🔍 💰
巡检场景(山区/偏远站点可能无网络):
// 使用 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());
}
}
对应 Issue:#36 ETL管道, #37 BI看板, #38 运营仪表盘, #39 需水量预测, #40 报告生成
Flink 流式处理:
Kafka(iot.raw.*) → Flink 作业
→ 数据解析 → 异常过滤 → 聚合(1min窗口)
→ Sink: TDengine (时序) + PostgreSQL (汇总)
推荐方案:Apache Superset 嵌入
前端 iframe 嵌入 Superset Dashboard (SSO token 透传)
├── 供水运营总览
├── 营收分析
├── 巡检统计
└── 自定义 SQL 查询
大屏布局 (1920×1080):
┌─────────────────────────────────────────────────────┐
│ 精河县智慧水务综合管理平台 2026-06-14 14:30 │
├──────────┬──────────┬──────────┬──────────────────────┤
│ 今日进水 │ 今日出水 │ 昨日供水 │ 设备在线率 │
├──────────┴──────────┴──────────┴──────────────────────┤
│ 进出水量趋势 (折线图) │ 供水区域 GIS 地图 │
│ │ (监测点位+报警闪烁) │
├───────────────────────────┤ │
│ 水质达标率 (仪表盘) │ │
│ 报警分布 (饼图) │ │
│ 能耗药耗 (柱状图) │ │
├──────────────────────────────────────────────────────┤
│ 实时报警滚动 | 巡检任务统计 | 营收总额统计 │
└──────────────────────────────────────────────────────┘
模型选择:
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 # | 标题 | 设计文档章节 |
|---|---|---|
| 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 也列入对照表,方便追溯。