diff --git a/README.md b/README.md index b1b74459..f66111af 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,299 @@ -# 智慧水务管理系统 - -**精河县供水工程综合管理平台** +# 水务管理系统 - 数据接入层 ## 项目概述 -覆盖物联网接入→数据治理→生产管理→营业收费→巡检运维→移动端的全栈智慧水务解决方案。 +本项目是水务管理系统中的数据接入层,实现了多源数据接入、实时WebSocket推送和批量数据导入功能。 -## 核心系统 +## 功能特性 -- **物联网平台** — 多协议设备统一接入与感知层标准 -- **数据引擎** — 数据汇聚/治理/服务全生命周期 -- **供水生产管理平台** — 在线监测/GIS/水质/调度/报警/数据中心 -- **营业收费系统** — 营收/报装/收费/表务/客服/微信网厅/远传集抄/工单 -- **巡检管理系统** — 巡检任务/路线/统计分析/巡检APP -- **移动APP** — 供水+巡检+营业收费三合一 -- **大数据分析系统** — BI决策支持 -- **业务流程引擎** — 跨系统流程统一编排 +### 1. REST API 数据接入 +- **IoT设备数据接入**:支持实时接收传感器数据 +- **手动数据录入**:支持人工录入数据 +- **批量API导入**:支持通过API批量导入数据 +- **数据查询接口**:提供按数据类型、时间范围等条件的数据查询 +- **统计分析接口**:提供数据统计和分析功能 -## 适用场景 +### 2. WebSocket 实时推送 +- **实时数据推送**:传感器数据实时推送到客户端 +- **连接管理**:支持多客户端连接和订阅管理 +- **数据历史**:新连接客户端可以获取历史数据 +- **警报推送**:支持实时警报推送 +- **心跳检测**:支持连接状态监控 -精河县及6个片区(精芒、八家户、托里、大镇阿合其、托托)供水管理。 +### 3. 批量数据导入 +- **多格式支持**:支持CSV、Excel、JSON格式导入 +- **数据验证**:内置数据验证和错误处理 +- **字段映射**:支持水利行业标准字段映射 +- **单位转换**:自动进行单位转换和标准化 +- **批量处理**:支持大批量数据处理 -## 技术架构 +## 技术栈 -架构选型详见 [docs/architecture.md](docs/architecture.md) +- **后端框架**:FastAPI +- **WebSocket**:websockets +- **数据处理**:pandas, numpy +- **异步处理**:asyncio +- **数据验证**:pydantic +- **文件处理**:aiofiles -## 文档 +## 项目结构 -- [需求规格清单](docs/requirements.md) -- [工作量评估](docs/estimation.md) +``` +water-management-system/ +├── src/ +│ ├── api/ # REST API模块 +│ │ └── rest_api.py # 主API服务器 +│ ├── websocket/ # WebSocket模块 +│ │ └── websocket_server.py # WebSocket服务器 +│ ├── batch/ # 批量导入模块 +│ │ └── batch_import.py # 批量导入功能 +│ ├── utils/ # 工具模块 +│ │ └── data_utils.py # 数据处理工具 +│ └── models/ # 数据模型 +│ └── models.py # 数据模型定义 +├── main.py # 主程序入口 +├── requirements.txt # 依赖文件 +├── config.json # 配置文件 +└── README.md # 项目说明 +``` + +## API 接口文档 + +### REST API 端点 + +#### 1. IoT 数据接收 +``` +POST /api/iot/data +Content-Type: application/json + +{ + "device_id": "device_001", + "data_type": "LL", + "value": 25.5, + "timestamp": "2024-01-01T12:00:00", + "location": "A区" +} +``` + +#### 2. 手动数据录入 +``` +POST /api/manual/data +Content-Type: application/json + +{ + "source": "manual", + "data_type": "YL", + "value": 0.8, + "timestamp": "2024-01-01T12:00:00", + "operator": "张三", + "notes": "定期数据录入" +} +``` + +#### 3. 批量导入 +``` +POST /api/batch/import +Content-Type: application/json + +{ + "batch_id": "batch_001", + "data_source": "system_import", + "records": [ + { + "device_id": "device_002", + "data_type": "SW", + "value": 5.2, + "timestamp": "2024-01-01T12:00:00", + "location": "B区" + } + ] +} +``` + +#### 4. 数据查询 +``` +GET /api/data/{data_type}?limit=100&offset=0 +GET /api/data/recent?hours=24&limit=100 +GET /api/stats +``` + +### WebSocket 连接 + +#### 连接端点 +``` +ws://localhost:8765 +``` + +#### 消息格式 + +**发送消息**: +```json +{ + "type": "subscribe", + "subscription": "LL" // 订阅特定类型数据,"all"订阅所有 +} +``` + +**接收消息**: +```json +{ + "type": "sensor_data", + "data_type": "LL", + "device_id": "device_001", + "value": 25.5, + "location": "A区", + "timestamp": "2024-01-01T12:00:00Z" +} +``` + +## 安装和运行 + +### 1. 安装依赖 +```bash +pip install -r requirements.txt +``` + +### 2. 启动系统 +```bash +# 普通模式 +python main.py + +# 演示模式(自动生成数据) +python main.py --demo + +# 指定配置文件 +python main.py --config custom_config.json +``` + +### 3. 初始化项目 +```bash +python main.py --init +``` + +## 配置文件 + +创建 `config.json` 文件: + +```json +{ + "api": { + "host": "0.0.0.0", + "port": 8000 + }, + "websocket": { + "host": "0.0.0.0", + "port": 8765 + }, + "batch": { + "max_file_size_mb": 100, + "supported_formats": ["csv", "excel", "json"] + }, + "demo_mode": true, + "logging": { + "level": "INFO", + "file": "water_management.log" + } +} +``` + +## 数据类型说明 + +支持的水利行业标准数据类型: + +| 数据类型 | 描述 | 单位 | +|---------|------|------| +| LL | 流量 | m³/h | +| YL | 压力 | MPa | +| SW | 水位 | m | +| ZD | 浊度 | NTU | +| PH | pH值 | - | +| WD | 温度 | °C | +| DD | 电导率 | μS/cm | +| YD | 硬度 | mg/L | + +## 开发规范 + +### 代码结构 +- 遵循模块化设计,功能解耦 +- 使用异步编程提高性能 +- 统一的错误处理机制 +- 完整的日志记录 + +### 数据处理 +- 数据验证和清洗 +- 标准化字段映射 +- 单位自动转换 +- 质量评分机制 + +### 安全考虑 +- 输入数据验证 +- 文件大小限制 +- 连接状态监控 +- 错误信息脱敏 + +## 测试和验证 + +### 数据验证 +```python +from src.utils.data_utils import data_converter + +# 验证传感器数据 +validation_result = data_converter.validate_sensor_data({ + "device_id": "device_001", + "data_type": "LL", + "value": 25.5, + "location": "A区" +}) + +print(validation_result) +``` + +### 批量导入测试 +```python +import asyncio +from src.batch.batch_import import batch_manager + +async def test_import(): + result = await batch_manager.import_file( + file_path="data.csv", + batch_id="test_batch", + data_source="test" + ) + print(result) + +asyncio.run(test_import()) +``` + +## 部署建议 + +### 1. 生产环境配置 +- 使用反向代理(Nginx) +- 配置SSL证书 +- 设置防火墙规则 +- 监控系统资源使用 + +### 2. 性能优化 +- 数据库连接池 +- 缓存机制 +- 异步处理优化 +- 连接数限制 + +### 3. 监控和日志 +- 应用性能监控 +- 错误日志收集 +- 性能指标统计 +- 告警机制 + +## 许可证 + +本项目遵循 MIT 许可证。 + +## 贡献指南 + +欢迎提交 Issue 和 Pull Request来贡献代码。 + +## 联系方式 + +如有问题,请通过以下方式联系: +- 邮箱:bot_dev1@xayunmei.com +- 项目地址:http://git.xayunmei.com/bot_ym/water-management-system \ No newline at end of file diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 00000000..c8c1d233 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,50 @@ +# 水务管理系统 BI 前端 + +这是一个简单的HTML前端,用于展示水务管理系统的BI看板和数据可视化功能。 + +## 功能特点 + +- 自助BI看板浏览 +- 图表数据可视化 +- 实时数据展示 +- 响应式设计 +- 移动端兼容 + +## 文件结构 + +``` +frontend/ +├── index.html # 主页面 +├── css/ +│ └── styles.css # 样式文件 +├── js/ +│ ├── main.js # 主要JavaScript逻辑 +│ ├── api.js # API调用封装 +│ └── charts.js # 图表绘制逻辑 +└── lib/ + └── echarts.min.js # ECharts图表库 +``` + +## 使用方法 + +1. 确保后端API服务器正在运行 +2. 打开 `index.html` 文件 +3. 选择要查看的看板 +4. 查看实时数据和图表 + +## API接口 + +前端主要通过以下API接口与后端交互: + +- `/bi-api/dashboards/overview` - 获取概览看板 +- `/bi-api/dashboards/{id}/data` - 获取看板数据 +- `/bi-api/charts/{id}/data` - 获取图表数据 +- `/bi-api/search` - 搜索功能 + +## 技术栈 + +- HTML5 +- CSS3 +- JavaScript (ES6+) +- ECharts 图表库 +- Fetch API \ No newline at end of file diff --git a/frontend/css/styles.css b/frontend/css/styles.css new file mode 100644 index 00000000..1ff4413d --- /dev/null +++ b/frontend/css/styles.css @@ -0,0 +1,412 @@ +/* 基础样式 */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica', 'Arial', sans-serif; + line-height: 1.6; + color: #333; + background-color: #f5f5f5; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +/* 头部样式 */ +.header { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + padding: 20px 0; + margin-bottom: 30px; + border-radius: 10px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); +} + +.header-content { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 20px; +} + +.header h1 { + font-size: 2.5rem; + font-weight: 600; +} + +.header-actions { + display: flex; + gap: 10px; +} + +/* 按钮样式 */ +.btn { + padding: 10px 20px; + border: none; + border-radius: 5px; + font-size: 16px; + cursor: pointer; + transition: all 0.3s ease; + font-weight: 500; +} + +.btn-primary { + background-color: #4CAF50; + color: white; +} + +.btn-primary:hover { + background-color: #45a049; + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); +} + +.btn-secondary { + background-color: #2196F3; + color: white; +} + +.btn-secondary:hover { + background-color: #1976D2; + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); +} + +.btn-back { + background-color: #ff7043; + color: white; + padding: 8px 16px; + font-size: 14px; +} + +.btn-back:hover { + background-color: #f4511e; +} + +/* 看板选择器样式 */ +.dashboard-selector { + margin-bottom: 30px; +} + +.dashboard-selector h2 { + margin-bottom: 20px; + color: #333; + font-size: 1.8rem; +} + +.dashboard-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 20px; +} + +.dashboard-card { + background: white; + border-radius: 10px; + padding: 20px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + cursor: pointer; + transition: all 0.3s ease; + border: 2px solid transparent; +} + +.dashboard-card:hover { + transform: translateY(-5px); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15); + border-color: #667eea; +} + +.dashboard-card h3 { + color: #333; + margin-bottom: 10px; + font-size: 1.3rem; +} + +.dashboard-card p { + color: #666; + margin-bottom: 15px; + font-size: 0.95rem; +} + +.dashboard-card .tags { + display: flex; + flex-wrap: wrap; + gap: 5px; + margin-bottom: 15px; +} + +.tag { + background-color: #e3f2fd; + color: #1976d2; + padding: 4px 8px; + border-radius: 12px; + font-size: 0.8rem; + font-weight: 500; +} + +.dashboard-card .charts-count { + color: #667eea; + font-weight: 600; + font-size: 0.9rem; +} + +/* 看板详情样式 */ +.dashboard-detail { + background: white; + border-radius: 10px; + padding: 20px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.dashboard-header { + display: flex; + align-items: center; + margin-bottom: 30px; + padding-bottom: 15px; + border-bottom: 2px solid #f0f0f0; +} + +.dashboard-header h2 { + margin-left: 15px; + color: #333; + font-size: 1.8rem; +} + +.charts-container { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); + gap: 20px; +} + +.chart-container { + background: #fafafa; + border-radius: 8px; + padding: 20px; + border: 1px solid #e0e0e0; +} + +.chart-container h3 { + color: #333; + margin-bottom: 15px; + font-size: 1.1rem; +} + +.chart { + width: 100%; + height: 300px; + border-radius: 5px; +} + +/* 模态框样式 */ +.modal { + display: none; + position: fixed; + z-index: 1000; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + animation: fadeIn 0.3s ease; +} + +.modal-content { + background-color: white; + margin: 5% auto; + padding: 0; + border-radius: 10px; + width: 90%; + max-width: 600px; + max-height: 80vh; + overflow-y: auto; + animation: slideIn 0.3s ease; +} + +.modal-header { + padding: 20px; + border-bottom: 1px solid #e0e0e0; + display: flex; + justify-content: space-between; + align-items: center; +} + +.modal-header h3 { + margin: 0; + color: #333; +} + +.modal-close { + background: none; + border: none; + font-size: 24px; + cursor: pointer; + color: #666; + padding: 0; + width: 30px; + height: 30px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + transition: background-color 0.3s ease; +} + +.modal-close:hover { + background-color: #f0f0f0; +} + +.modal-body { + padding: 20px; +} + +.search-input { + width: 100%; + padding: 12px; + border: 1px solid #ddd; + border-radius: 5px; + font-size: 16px; + margin-bottom: 20px; + outline: none; +} + +.search-input:focus { + border-color: #667eea; + box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1); +} + +.search-results { + max-height: 400px; + overflow-y: auto; +} + +.search-result-item { + padding: 12px; + border-bottom: 1px solid #f0f0f0; + cursor: pointer; + transition: background-color 0.3s ease; +} + +.search-result-item:hover { + background-color: #f8f9fa; +} + +.search-result-item:last-child { + border-bottom: none; +} + +.search-result-item h4 { + color: #333; + margin-bottom: 5px; +} + +.search-result-item p { + color: #666; + font-size: 0.9rem; + margin: 0; +} + +.search-result-item .type { + display: inline-block; + padding: 2px 8px; + border-radius: 12px; + font-size: 0.8rem; + font-weight: 500; + margin-top: 5px; +} + +.search-result-item .type.chart { + background-color: #e3f2fd; + color: #1976d2; +} + +.search-result-item .type.dashboard { + background-color: #f3e5f5; + color: #7b1fa2; +} + +/* 动画效果 */ +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes slideIn { + from { transform: translateY(-50px); opacity: 0; } + to { transform: translateY(0); opacity: 1; } +} + +/* 响应式设计 */ +@media (max-width: 768px) { + .container { + padding: 10px; + } + + .header-content { + flex-direction: column; + gap: 15px; + } + + .header h1 { + font-size: 2rem; + } + + .dashboard-grid { + grid-template-columns: 1fr; + } + + .charts-container { + grid-template-columns: 1fr; + } + + .modal-content { + width: 95%; + margin: 10% auto; + } +} + +/* 加载状态 */ +.loading { + text-align: center; + padding: 40px; + color: #666; +} + +.loading::after { + content: ''; + display: inline-block; + width: 20px; + height: 20px; + border: 2px solid #ddd; + border-top: 2px solid #667eea; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-left: 10px; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* 错误状态 */ +.error { + background-color: #ffebee; + color: #c62828; + padding: 12px; + border-radius: 5px; + margin: 10px 0; + border-left: 4px solid #c62828; +} + +/* 空状态 */ +.empty { + text-align: center; + padding: 40px; + color: #666; +} \ No newline at end of file diff --git a/frontend/index.html b/frontend/index.html index e1190ebb..82dbffad 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,12 +1,67 @@
- - -