From f035fa274f81d6577519c2978adcb0434a4b872c Mon Sep 17 00:00:00 2001 From: bot_pm Date: Sun, 14 Jun 2026 17:48:01 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20v2.0=20=E8=AF=A6=E7=BB=86=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E8=A7=84=E6=A0=BC=E4=B9=A6=20(=E5=90=AB=E5=85=A8?= =?UTF-8?q?=E9=83=A8=E6=A8=A1=E5=9D=97DB/API/=E7=BB=84=E4=BB=B6/=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=B5=81/Issue=E5=AF=B9=E7=85=A7=E8=A1=A8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/design-spec.md | 1781 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1781 insertions(+) create mode 100644 docs/design-spec.md diff --git a/docs/design-spec.md b/docs/design-spec.md new file mode 100644 index 00000000..6ec8e0ff --- /dev/null +++ b/docs/design-spec.md @@ -0,0 +1,1781 @@ +# 智慧水务管理系统 — 详细设计规格书 + +> 版本: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 + → 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 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 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 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 syncTaskData() async { + if (await hasNetwork()) { + var tasks = await patrolService.fetchTasks(); + await hiveBox.put('tasks', tasks); + } +} +// 离线巡检:数据暂存本地,网络恢复后自动上传 +Future 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 也列入对照表,方便追溯。