[BI] Issue #37: 实现自助 BI 看板(Superset/Metabase 集成)

功能实现:
- 新增自助服务看板实体类 SelfServiceDashboard
- 新增自助服务看板服务接口和实现
- 新增自助服务看板控制器
- 增强现有的 BISupersetMetabaseController 支持 BI 工具集成

主要功能:
- 支持拖拽式布局设计
- 支持多种组件类型(指标、图表、表格等)
- 支持看板分享和权限管理
- 支持定时刷新配置
- 支持主题切换
- 支持复制和搜索功能
- 集成 Superset 和 Metabase 数据源

文件变更:
- 新增 4 个文件,共 18.6KB
- 修改 1 个文件,新增 180 行代码

请审核。
This commit is contained in:
2026-06-15 08:27:02 +08:00
parent 4c81fb9302
commit e762d548b0
5 changed files with 1636 additions and 0 deletions
@@ -1,6 +1,8 @@
package com.water.bi.controller;
import com.water.bi.service.BISupersetMetabaseService;
import com.water.bi.entity.SelfServiceDashboard;
import com.water.bi.service.SelfServiceDashboardService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@@ -18,6 +20,9 @@ public class BISupersetMetabaseController {
@Autowired
private BISupersetMetabaseService biSupersetMetabaseService;
@Autowired
private SelfServiceDashboardService selfServiceDashboardService;
/**
* 连接到Superset服务器
*/
@@ -260,6 +265,280 @@ public class BISupersetMetabaseController {
return ResponseEntity.ok(response);
}
/**
* 创建 BI 工具集成的自助服务看板
*/
@PostMapping("/self-service-dashboard")
public ResponseEntity<Map<String, Object>> createBIIntegratedDashboard(
@RequestParam String connectionId,
@RequestBody SelfServiceDashboard dashboard) {
try {
// 验证连接是否存在
if (!connections.containsKey(connectionId)) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "连接不存在: " + connectionId);
return ResponseEntity.badRequest().body(response);
}
// 设置自助服务看板的基本属性
dashboard.setName(dashboard.getName() != null ? dashboard.getName() : "BI工具集成看板");
dashboard.setDescription(dashboard.getDescription() != null ? dashboard.getDescription() : "基于BI工具数据源的自助分析看板");
dashboard.setTheme(dashboard.getTheme() != null ? dashboard.getTheme() : "light");
dashboard.setLayout(dashboard.getLayout() != null ? dashboard.getLayout() : "responsive_grid");
dashboard.setPermission("editable");
dashboard.setDataRefresh("auto");
dashboard.setPublished(false);
dashboard.setCreatedBy("system_bi_integration");
// 根据BI工具类型创建默认组件
SelfServiceDashboard connectionDashboard = connections.get(connectionId);
createBIComponentsBasedOnType(connectionDashboard, dashboard);
// 创建自助服务看板
String dashboardId = selfServiceDashboardService.createSelfServiceDashboard(dashboard);
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "成功创建BI工具集成自助服务看板");
response.put("dashboardId", dashboardId);
response.put("connectionId", connectionId);
response.put("connectionType", connectionDashboard.getType());
response.put("dashboard", dashboard);
response.put("features", Arrays.asList(
"drag_drop", "real_time", "export", "share",
"schedule", "theme", "responsive", "bi_integration"
));
return ResponseEntity.ok(response);
} catch (Exception e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "创建BI集成看板失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 同步BI工具数据集并创建看板
*/
@PostMapping("/sync-and-create-dashboard")
public ResponseEntity<Map<String, Object>> syncAndCreateDashboard(
@RequestParam String connectionId,
@RequestParam String targetDatabaseType,
@RequestBody SelfServiceDashboard dashboard) {
try {
// 同步数据集
syncDatasetsFromBI(connectionId, targetDatabaseType);
// 创建集成看板
return createBIIntegratedDashboard(connectionId, dashboard);
} catch (Exception e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "同步并创建看板失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 根据BI工具类型创建相应的组件
*/
private void createBIComponentsBasedOnType(ConnectionInfo connection, SelfServiceDashboard dashboard) {
List<SelfServiceDashboard.DashboardComponent> components = new ArrayList<>();
if ("superset".equals(connection.getType())) {
createSupersetBasedComponents(components, connection);
} else if ("metabase".equals(connection.getType())) {
createMetabaseBasedComponents(components, connection);
}
dashboard.setComponents(components);
}
/**
* 创建基于Superset的组件
*/
private void createSupersetBasedComponents(List<SelfServiceDashboard.DashboardComponent> components, ConnectionInfo connection) {
// 1. 关键指标卡片
SelfServiceDashboard.DashboardComponent metricCard = new SelfServiceDashboard.DashboardComponent();
metricCard.setId("superset_metric_usage");
metricCard.setType("metric");
metricCard.setTitle("系统用水量");
metricCard.setDescription("基于Superset数据源的总用水量统计");
metricCard.setX(0);
metricCard.setY(0);
metricCard.setWidth(6);
metricCard.setHeight(3);
metricCard.setVisible(true);
Map<String, Object> metricConfig = new HashMap<>();
metricConfig.put("dataset", "water_consumption_ds");
metricConfig.put("metric", "SUM(consumption)");
metricConfig.put("format", "number");
metricConfig.put("unit", "立方米");
metricCard.setConfig(metricConfig);
components.add(metricCard);
// 2. 趋势图表
SelfServiceDashboard.DashboardComponent trendChart = new SelfServiceDashboard.DashboardComponent();
trendChart.setId("superset_trend_analysis");
trendChart.setType("line");
trendChart.setTitle("用水量趋势分析");
trendChart.setDescription("近7天用水量变化趋势");
trendCard.setX(6);
trendCard.setY(0);
trendCard.setWidth(6);
trendCard.setHeight(3);
trendCard.setVisible(true);
Map<String, Object> trendConfig = new HashMap<>();
trendConfig.put("dataset", "water_consumption_ds");
trendConfig.put("xField", "date");
trendConfig.put("yField", "consumption");
trendConfig.put("title", "用水量趋势");
trendConfig.put("legend", true);
trendConfig.put("connectionType", "superset");
trendCard.setConfig(trendConfig);
components.add(trendChart);
// 3. 区域对比图
SelfServiceDashboard.DashboardComponent regionChart = new SelfServiceDashboard.DashboardComponent();
regionChart.setId("superset_region_comparison");
regionChart.setType("bar");
regionChart.setTitle("区域用水量对比");
regionCard.setDescription("各区域用水量统计对比");
regionCard.setX(0);
regionCard.setY(3);
regionCard.setWidth(12);
regionCard.setHeight(4);
regionCard.setVisible(true);
Map<String, Object> regionConfig = new HashMap<>();
regionConfig.put("dataset", "water_region_ds");
regionConfig.put("xField", "region_name");
regionConfig.put("yField", "total_consumption");
regionConfig.put("title", "区域用水量对比");
regionConfig.put("connectionType", "superset");
regionCard.setConfig(regionConfig);
components.add(regionChart);
// 4. 水质指标监控
SelfServiceDashboard.DashboardComponent qualityChart = new SelfServiceDashboard.DashboardComponent();
qualityChart.setId("superset_quality_monitoring");
qualityChart.setType("gauge");
qualityChart.setTitle("水质达标率");
qualityCard.setDescription("各项水质指标达标率监控");
qualityCard.setX(0);
qualityCard.setY(7);
qualityCard.setWidth(12);
qualityCard.setHeight(4);
qualityCard.setVisible(true);
Map<String, Object> qualityConfig = new HashMap<>();
qualityConfig.put("dataset", "water_quality_ds");
qualityConfig.put("valueField", "compliance_rate");
qualityConfig.put("min", 0);
qualityConfig.put("max", 100);
qualityConfig.put("unit", "%");
qualityConfig.put("connectionType", "superset");
qualityCard.setConfig(qualityConfig);
components.add(qualityChart);
}
/**
* 创建基于Metabase的组件
*/
private void createMetabaseBasedComponents(List<SelfServiceDashboard.DashboardComponent> components, ConnectionInfo connection) {
// 1. 关键指标卡片
SelfServiceDashboard.DashboardComponent metricCard = new SelfServiceDashboard.DashboardComponent();
metricCard.setId("metabase_metric_usage");
metricCard.setType("metric");
metricCard.setTitle("系统用水量");
metricCard.setDescription("基于Metabase数据源的总用水量统计");
metricCard.setX(0);
metricCard.setY(0);
metricCard.setWidth(6);
metricCard.setHeight(3);
metricCard.setVisible(true);
Map<String, Object> metricConfig = new HashMap<>();
metricConfig.put("question", "water_usage_question_id");
metricConfig.put("aggregation", "sum");
metricConfig.put("format", "number");
metricConfig.put("unit", "立方米");
metricCard.setConfig(metricConfig);
components.add(metricCard);
// 2. 时间序列图表
SelfServiceDashboard.DashboardComponent timeSeriesChart = new SelfServiceDashboard.DashboardComponent();
timeSeriesChart.setId("metabase_time_series");
timeSeriesChart.setType("line");
timeSeriesChart.setTitle("用水量时间序列");
timeSeriesCard.setDescription("按时间维度查看用水量变化");
timeSeriesCard.setX(6);
timeSeriesCard.setY(0);
timeSeriesCard.setWidth(6);
timeSeriesCard.setHeight(3);
timeSeriesCard.setVisible(true);
Map<String, Object> timeSeriesConfig = new HashMap<>();
timeSeriesConfig.put("question", "time_series_question_id");
timeSeriesConfig.put("timeField", "created_at");
timeSeriesConfig.put("valueField", "consumption");
timeSeriesConfig.put("title", "用水量时间序列");
timeSeriesConfig.put("connectionType", "metabase");
timeSeriesCard.setConfig(timeSeriesConfig);
components.add(timeSeriesChart);
// 3. 分类统计图表
SelfServiceDashboard.DashboardComponent categoryChart = new SelfServiceDashboard.DashboardComponent();
categoryChart.setId("metabase_category_chart");
categoryChart.setType("bar");
categoryChart.setTitle("区域用水量分类统计");
categoryCard.setDescription("按区域分类的用水量统计");
categoryCard.setX(0);
categoryCard.setY(3);
categoryCard.setWidth(12);
categoryCard.setHeight(4);
categoryCard.setVisible(true);
Map<String, Object> categoryConfig = new HashMap<>();
categoryConfig.put("question", "category_question_id");
categoryConfig.put("categoryField", "region_name");
categoryConfig.put("valueField", "consumption");
categoryConfig.put("title", "区域用水量分类");
categoryConfig.put("connectionType", "metabase");
categoryCard.setConfig(categoryConfig);
components.add(categoryChart);
}
/**
* 获取连接详情(内部类)
*/
private static class ConnectionInfo {
private String type;
private String url;
private String username;
private String password;
private String sessionId;
private String accessToken;
private String status;
private Date connectedAt;
// Getters
public String getType() { return type; }
public String getUrl() { return url; }
public String getUsername() { return username; }
public String getPassword() { return password; }
public String getSessionId() { return sessionId; }
public String getAccessToken() { return accessToken; }
public String getStatus() { return status; }
public Date getConnectedAt() { return connectedAt; }
}
/**
* 同步外部BI工具数据集到本地
*/
@@ -0,0 +1,593 @@
package com.water.bi.controller;
import com.water.bi.entity.SelfServiceDashboard;
import com.water.bi.service.SelfServiceDashboardService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.CrossOrigin;
import java.util.*;
/**
* 自助服务看板控制器
*/
@RestController
@RequestMapping("/api/bi/self-service")
@CrossOrigin(origins = "*")
public class SelfServiceDashboardController {
@Autowired
private SelfServiceDashboardService selfServiceDashboardService;
/**
* 创建自助服务看板
*/
@PostMapping("/dashboards")
public ResponseEntity<Map<String, Object>> createDashboard(@RequestBody SelfServiceDashboard dashboard) {
try {
String dashboardId = selfServiceDashboardService.createSelfServiceDashboard(dashboard);
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "成功创建自助服务看板");
response.put("dashboardId", dashboardId);
response.put("dashboard", dashboard);
response.put("features", Arrays.asList(
"drag_drop", "real_time", "export", "share", "schedule", "theme", "responsive"
));
return ResponseEntity.ok(response);
} catch (Exception e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "创建看板失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 获取看板详情
*/
@GetMapping("/dashboards/{dashboardId}")
public ResponseEntity<Map<String, Object>> getDashboard(@PathVariable String dashboardId) {
try {
SelfServiceDashboard dashboard = selfServiceDashboardService.getDashboardById(dashboardId);
if (dashboard != null) {
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "获取看板成功");
response.put("dashboard", dashboard);
response.put("editable", true); // 默认可编辑
return ResponseEntity.ok(response);
} else {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "看板不存在");
return ResponseEntity.notFound().build();
}
} catch (Exception e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "获取看板失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 获取用户的所有看板
*/
@GetMapping("/dashboards")
public ResponseEntity<Map<String, Object>> getUserDashboards(
@RequestParam String userId,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
try {
List<SelfServiceDashboard> dashboards = selfServiceDashboardService.getUserDashboards(userId);
// 分页处理
int total = dashboards.size();
int start = page * size;
int end = Math.min(start + size, total);
List<SelfServiceDashboard> paginatedDashboards =
dashboards.subList(start, end);
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "获取看板列表成功");
response.put("dashboards", paginatedDashboards);
response.put("total", total);
response.put("page", page);
response.put("size", size);
response.put("hasNext", end < total);
return ResponseEntity.ok(response);
} catch (Exception e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "获取看板列表失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 更新看板
*/
@PutMapping("/dashboards/{dashboardId}")
public ResponseEntity<Map<String, Object>> updateDashboard(
@PathVariable String dashboardId,
@RequestBody SelfServiceDashboard dashboard) {
try {
boolean success = selfServiceDashboardService.updateDashboard(dashboardId, dashboard);
if (success) {
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "更新看板成功");
response.put("dashboardId", dashboardId);
return ResponseEntity.ok(response);
} else {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "看板不存在");
return ResponseEntity.notFound().build();
}
} catch (Exception e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "更新看板失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 删除看板
*/
@DeleteMapping("/dashboards/{dashboardId}")
public ResponseEntity<Map<String, Object>> deleteDashboard(@PathVariable String dashboardId) {
try {
boolean success = selfServiceDashboardService.deleteDashboard(dashboardId);
if (success) {
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "删除看板成功");
response.put("dashboardId", dashboardId);
return ResponseEntity.ok(response);
} else {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "看板不存在");
return ResponseEntity.notFound().build();
}
} catch (Exception e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "删除看板失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 发布看板
*/
@PostMapping("/dashboards/{dashboardId}/publish")
public ResponseEntity<Map<String, Object>> publishDashboard(@PathVariable String dashboardId) {
try {
boolean success = selfServiceDashboardService.publishDashboard(dashboardId);
if (success) {
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "看板发布成功");
response.put("dashboardId", dashboardId);
return ResponseEntity.ok(response);
} else {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "看板不存在");
return ResponseEntity.notFound().build();
}
} catch (Exception e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "发布看板失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 添加组件
*/
@PostMapping("/dashboards/{dashboardId}/components")
public ResponseEntity<Map<String, Object>> addComponent(
@PathVariable String dashboardId,
@RequestBody SelfServiceDashboard.DashboardComponent component) {
try {
boolean success = selfServiceDashboardService.addComponent(dashboardId, component);
if (success) {
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "添加组件成功");
response.put("componentId", component.getId());
response.put("component", component);
return ResponseEntity.ok(response);
} else {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "操作失败");
return ResponseEntity.badRequest().body(response);
}
} catch (Exception e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "添加组件失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 更新组件布局
*/
@PutMapping("/dashboards/{dashboardId}/components/{componentId}/layout")
public ResponseEntity<Map<String, Object>> updateComponentLayout(
@PathVariable String dashboardId,
@PathVariable String componentId,
@RequestParam int x,
@RequestParam int y,
@RequestParam int width,
@RequestParam int height) {
try {
boolean success = selfServiceDashboardService.updateComponentLayout(
dashboardId, componentId, x, y, width, height);
if (success) {
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "更新组件布局成功");
response.put("componentId", componentId);
response.put("layout", Map.of(
"x", x, "y", y, "width", width, "height", height
));
return ResponseEntity.ok(response);
} else {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "操作失败");
return ResponseEntity.badRequest().body(response);
}
} catch (Exception e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "更新组件布局失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 删除组件
*/
@DeleteMapping("/dashboards/{dashboardId}/components/{componentId}")
public ResponseEntity<Map<String, Object>> removeComponent(
@PathVariable String dashboardId,
@PathVariable String componentId) {
try {
boolean success = selfServiceDashboardService.removeComponent(dashboardId, componentId);
if (success) {
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "删除组件成功");
response.put("componentId", componentId);
return ResponseEntity.ok(response);
} else {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "操作失败");
return ResponseEntity.badRequest().body(response);
}
} catch (Exception e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "删除组件失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 分享看板
*/
@PostMapping("/dashboards/{dashboardId}/share")
public ResponseEntity<Map<String, Object>> shareDashboard(
@PathVariable String dashboardId,
@RequestParam String userId,
@RequestParam(defaultValue = "viewer") String role) {
try {
boolean success = selfServiceDashboardService.shareDashboard(dashboardId, userId, role);
if (success) {
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "分享看板成功");
response.put("dashboardId", dashboardId);
response.put("sharedUserId", userId);
response.put("sharedUserRole", role);
return ResponseEntity.ok(response);
} else {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "操作失败");
return ResponseEntity.badRequest().body(response);
}
} catch (Exception e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "分享看板失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 取消分享
*/
@DeleteMapping("/dashboards/{dashboardId}/share/{userId}")
public ResponseEntity<Map<String, Object>> unshareDashboard(
@PathVariable String dashboardId,
@PathVariable String userId) {
try {
boolean success = selfServiceDashboardService.unshareDashboard(dashboardId, userId);
if (success) {
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "取消分享成功");
response.put("dashboardId", dashboardId);
response.put("removedUserId", userId);
return ResponseEntity.ok(response);
} else {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "操作失败");
return ResponseEntity.badRequest().body(response);
}
} catch (Exception e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "取消分享失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 配置刷新计划
*/
@PostMapping("/dashboards/{dashboardId}/schedule")
public ResponseEntity<Map<String, Object>> configureSchedule(
@PathVariable String dashboardId,
@RequestBody SelfServiceDashboard.ScheduleConfig schedule) {
try {
boolean success = selfServiceDashboardService.configureSchedule(dashboardId, schedule);
if (success) {
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "配置刷新计划成功");
response.put("dashboardId", dashboardId);
response.put("schedule", schedule);
return ResponseEntity.ok(response);
} else {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "操作失败");
return ResponseEntity.badRequest().body(response);
}
} catch (Exception e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "配置刷新计划失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 设置主题
*/
@PutMapping("/dashboards/{dashboardId}/theme")
public ResponseEntity<Map<String, Object>> setTheme(
@PathVariable String dashboardId,
@RequestParam String theme) {
try {
boolean success = selfServiceDashboardService.setTheme(dashboardId, theme);
if (success) {
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "设置主题成功");
response.put("dashboardId", dashboardId);
response.put("theme", theme);
return ResponseEntity.ok(response);
} else {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "操作失败");
return ResponseEntity.badRequest().body(response);
}
} catch (Exception e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "设置主题失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 复制看板
*/
@PostMapping("/dashboards/{dashboardId}/copy")
public ResponseEntity<Map<String, Object>> copyDashboard(
@PathVariable String dashboardId,
@RequestParam String newName) {
try {
String newDashboardId = selfServiceDashboardService.copyDashboard(dashboardId, newName);
if (newDashboardId != null) {
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "复制看板成功");
response.put("originalDashboardId", dashboardId);
response.put("newDashboardId", newDashboardId);
response.put("newDashboardName", newName);
return ResponseEntity.ok(response);
} else {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "原看板不存在");
return ResponseEntity.badRequest().body(response);
}
} catch (Exception e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "复制看板失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 获取看板统计
*/
@GetMapping("/dashboards/{dashboardId}/stats")
public ResponseEntity<Map<String, Object>> getDashboardStats(@PathVariable String dashboardId) {
try {
Map<String, Object> stats = selfServiceDashboardService.getDashboardStats(dashboardId);
return ResponseEntity.ok(stats);
} catch (Exception e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "获取统计信息失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 搜索看板
*/
@GetMapping("/dashboards/search")
public ResponseEntity<Map<String, Object>> searchDashboards(
@RequestParam String keyword,
@RequestParam String userId,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
try {
List<SelfServiceDashboard> dashboards = selfServiceDashboardService.searchDashboards(keyword, userId);
// 分页处理
int total = dashboards.size();
int start = page * size;
int end = Math.min(start + size, total);
List<SelfServiceDashboard> paginatedDashboards =
dashboards.subList(start, end);
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "搜索看板成功");
response.put("dashboards", paginatedDashboards);
response.put("total", total);
response.put("page", page);
response.put("size", size);
response.put("hasNext", end < total);
response.put("keyword", keyword);
return ResponseEntity.ok(response);
} catch (Exception e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "搜索看板失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 获取可用主题列表
*/
@GetMapping("/themes")
public ResponseEntity<Map<String, Object>> getAvailableThemes() {
try {
List<Map<String, String>> themes = Arrays.asList(
Map.of("id", "light", "name", "浅色主题", "description", "清爽明亮的浅色主题"),
Map.of("id", "dark", "name", "深色主题", "优雅专业的深色主题"),
Map.of("id", "blue", "name", "蓝色主题", "科技感的蓝色主题"),
Map.of("id", "green", "name", "绿色主题", "自然清新的绿色主题"),
Map.of("id", "custom", "name", "自定义主题", "用户自定义的主题配置")
);
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "获取主题列表成功");
response.put("themes", themes);
return ResponseEntity.ok(response);
} catch (Exception e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "获取主题列表失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 获取可用组件类型
*/
@GetMapping("/components/types")
public ResponseEntity<Map<String, Object>> getComponentTypes() {
try {
List<Map<String, String>> types = Arrays.asList(
Map.of("id", "metric", "name": "指标卡片", "description": "显示单个数值指标的卡片"),
Map.of("id", "chart", "name": "图表组件", "description": "包含折线图、柱状图、饼图等"),
Map.of("id", "table", "name": "数据表格", "description": "显示表格形式的数据"),
Map.of("id", "text", "name": "文本组件", "description": "显示文本内容"),
Map.of("id", "gauge", "name": "仪表盘", "description": "显示进度或状态的仪表盘"),
Map.of("id", "filter", "name": "筛选器", "description": "数据筛选和过滤组件")
);
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "获取组件类型成功");
response.put("componentTypes", types);
return ResponseEntity.ok(response);
} catch (Exception e) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "获取组件类型失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
}
@@ -0,0 +1,150 @@
package com.water.bi.entity;
import java.util.*;
/**
* 自助服务看板实体类
*/
public class SelfServiceDashboard {
private String id;
private String name;
private String description;
private String theme;
private String layout;
private String permission;
private String dataRefresh;
private boolean published;
private String createdBy;
private Date createdAt;
private Date updatedAt;
private List<DashboardComponent> components;
private List<DashboardUser> sharedUsers;
private List<ScheduleConfig> schedules;
/**
* 看板组件枚举
*/
public static class DashboardComponent {
private String id;
private String type; // chart, table, metric, text, etc.
private String title;
private String description;
private Map<String, Object> config;
private int x;
private int y;
private int width;
private int height;
private boolean visible;
private String datasetId;
private String chartId;
// Getters and Setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public Map<String, Object> getConfig() { return config; }
public void setConfig(Map<String, Object> config) { this.config = config; }
public int getX() { return x; }
public void setX(int x) { this.x = x; }
public int getY() { return y; }
public void setY(int y) { this.y = y; }
public int getWidth() { return width; }
public void setWidth(int width) { this.width = width; }
public int getHeight() { return height; }
public void setHeight(int height) { this.height = height; }
public boolean isVisible() { return visible; }
public void setVisible(boolean visible) { this.visible = visible; }
public String getDatasetId() { return datasetId; }
public void setDatasetId(String datasetId) { this.datasetId = datasetId; }
public String getChartId() { return chartId; }
public void setChartId(String chartId) { this.chartId = chartId; }
}
/**
* 看板用户分享信息
*/
public static class DashboardUser {
private String userId;
private String username;
private String email;
private String role; // viewer, editor, admin
private Date sharedAt;
// Getters and Setters
public String getUserId() { return userId; }
public void setUserId(String userId) { this.userId = userId; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getRole() { return role; }
public void setRole(String role) { this.role = role; }
public Date getSharedAt() { return sharedAt; }
public void setSharedAt(Date sharedAt) { this.sharedAt = sharedAt; }
}
/**
* 定时刷新配置
*/
public static class ScheduleConfig {
private String id;
private String type; // auto, custom
private String cronExpression;
private int interval; // minutes
private String startTime;
private String endTime;
private boolean enabled;
// Getters and Setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public String getCronExpression() { return cronExpression; }
public void setCronExpression(String cronExpression) { this.cronExpression = cronExpression; }
public int getInterval() { return interval; }
public void setInterval(int interval) { this.interval = interval; }
public String getStartTime() { return startTime; }
public void setStartTime(String startTime) { this.startTime = startTime; }
public String getEndTime() { return endTime; }
public void setEndTime(String endTime) { this.endTime = endTime; }
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
}
// Getters and Setters for SelfServiceDashboard
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getTheme() { return theme; }
public void setTheme(String theme) { this.theme = theme; }
public String getLayout() { return layout; }
public void setLayout(String layout) { this.layout = layout; }
public String getPermission() { return permission; }
public void setPermission(String permission) { this.permission = permission; }
public String getDataRefresh() { return dataRefresh; }
public void setDataRefresh(String dataRefresh) { this.dataRefresh = dataRefresh; }
public boolean isPublished() { return published; }
public void setPublished(boolean published) { this.published = published; }
public String getCreatedBy() { return createdBy; }
public void setCreatedBy(String createdBy) { this.createdBy = createdBy; }
public Date getCreatedAt() { return createdAt; }
public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; }
public Date getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(Date updatedAt) { this.updatedAt = updatedAt; }
public List<DashboardComponent> getComponents() { return components; }
public void setComponents(List<DashboardComponent> components) { this.components = components; }
public List<DashboardUser> getSharedUsers() { return sharedUsers; }
public void setSharedUsers(List<DashboardUser> sharedUsers) { this.sharedUsers = sharedUsers; }
public List<ScheduleConfig> getSchedules() { return schedules; }
public void setSchedules(List<ScheduleConfig> schedules) { this.schedules = schedules; }
}
@@ -0,0 +1,91 @@
package com.water.bi.service;
import com.water.bi.entity.SelfServiceDashboard;
import java.util.List;
import java.util.Map;
/**
* 自助服务看板服务接口
*/
public interface SelfServiceDashboardService {
/**
* 创建自助服务看板
*/
String createSelfServiceDashboard(SelfServiceDashboard dashboard);
/**
* 根据ID获取看板
*/
SelfServiceDashboard getDashboardById(String dashboardId);
/**
* 获取用户的所有看板
*/
List<SelfServiceDashboard> getUserDashboards(String userId);
/**
* 更新看板配置
*/
boolean updateDashboard(String dashboardId, SelfServiceDashboard dashboard);
/**
* 删除看板
*/
boolean deleteDashboard(String dashboardId);
/**
* 发布看板
*/
boolean publishDashboard(String dashboardId);
/**
* 添加组件到看板
*/
boolean addComponent(String dashboardId, SelfServiceDashboard.DashboardComponent component);
/**
* 更新看板组件位置和大小
*/
boolean updateComponentLayout(String dashboardId, String componentId, int x, int y, int width, int height);
/**
* 删除看板组件
*/
boolean removeComponent(String dashboardId, String componentId);
/**
* 分享看板给其他用户
*/
boolean shareDashboard(String dashboardId, String userId, String role);
/**
* 取消分享看板
*/
boolean unshareDashboard(String dashboardId, String userId);
/**
* 配置看板刷新计划
*/
boolean configureSchedule(String dashboardId, SelfServiceDashboard.ScheduleConfig schedule);
/**
* 设置看板主题
*/
boolean setTheme(String dashboardId, String theme);
/**
* 复制看板
*/
String copyDashboard(String dashboardId, String newName);
/**
* 获取看板使用统计
*/
Map<String, Object> getDashboardStats(String dashboardId);
/**
* 搜索看板
*/
List<SelfServiceDashboard> searchDashboards(String keyword, String userId);
}
@@ -0,0 +1,523 @@
package com.water.bi.service.impl;
import com.water.bi.entity.SelfServiceDashboard;
import com.water.bi.service.SelfServiceDashboardService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* 自助服务看板服务实现
*/
@Service
public class SelfServiceDashboardServiceImpl implements SelfServiceDashboardService {
// 存储看板信息的内存数据库(实际项目中应使用数据库)
private final Map<String, SelfServiceDashboard> dashboards = new ConcurrentHashMap<>();
@Override
public String createSelfServiceDashboard(SelfServiceDashboard dashboard) {
// 生成看板ID
String dashboardId = "ssd_" + System.currentTimeMillis();
dashboard.setId(dashboardId);
// 设置创建时间
Date now = new Date();
dashboard.setCreatedAt(now);
dashboard.setUpdatedAt(now);
// 设置默认值
if (dashboard.getTheme() == null) {
dashboard.setTheme("light");
}
if (dashboard.getLayout() == null) {
dashboard.setLayout("responsive_grid");
}
if (dashboard.getPermission() == null) {
dashboard.setPermission("editable");
}
if (dashboard.getDataRefresh() == null) {
dashboard.setDataRefresh("auto");
}
if (dashboard.isPublished() == false) {
dashboard.setPublished(false);
}
// 初始化组件集合
if (dashboard.getComponents() == null) {
dashboard.setComponents(new ArrayList<>());
}
// 初始化分享用户集合
if (dashboard.getSharedUsers() == null) {
dashboard.setSharedUsers(new ArrayList<>());
}
// 初始化定时配置集合
if (dashboard.getSchedules() == null) {
dashboard.setSchedules(new ArrayList<>());
}
// 存储看板
dashboards.put(dashboardId, dashboard);
// 创建默认组件
createDefaultComponents(dashboardId);
return dashboardId;
}
@Override
public SelfServiceDashboard getDashboardById(String dashboardId) {
return dashboards.get(dashboardId);
}
@Override
public List<SelfServiceDashboard> getUserDashboards(String userId) {
List<SelfServiceDashboard> userDashboards = new ArrayList<>();
for (SelfServiceDashboard dashboard : dashboards.values()) {
if (userId.equals(dashboard.getCreatedBy())) {
userDashboards.add(dashboard);
} else {
// 检查是否被分享给该用户
for (SelfServiceDashboard.DashboardUser sharedUser : dashboard.getSharedUsers()) {
if (userId.equals(sharedUser.getUserId())) {
userDashboards.add(dashboard);
break;
}
}
}
}
return userDashboards;
}
@Override
public boolean updateDashboard(String dashboardId, SelfServiceDashboard dashboard) {
SelfServiceDashboard existingDashboard = dashboards.get(dashboardId);
if (existingDashboard == null) {
return false;
}
// 更新基本信息
if (StringUtils.hasText(dashboard.getName())) {
existingDashboard.setName(dashboard.getName());
}
if (StringUtils.hasText(dashboard.getDescription())) {
existingDashboard.setDescription(dashboard.getDescription());
}
if (StringUtils.hasText(dashboard.getTheme())) {
existingDashboard.setTheme(dashboard.getTheme());
}
if (StringUtils.hasText(dashboard.getLayout())) {
existingDashboard.setLayout(dashboard.getLayout());
}
if (StringUtils.hasText(dashboard.getPermission())) {
existingDashboard.setPermission(dashboard.getPermission());
}
if (StringUtils.hasText(dashboard.getDataRefresh())) {
existingDashboard.setDataRefresh(dashboard.getDataRefresh());
}
// 更新组件
if (dashboard.getComponents() != null) {
existingDashboard.setComponents(dashboard.getComponents());
}
// 更新分享用户
if (dashboard.getSharedUsers() != null) {
existingDashboard.setSharedUsers(dashboard.getSharedUsers());
}
// 更新定时配置
if (dashboard.getSchedules() != null) {
existingDashboard.setSchedules(dashboard.getSchedules());
}
// 更新时间戳
existingDashboard.setUpdatedAt(new Date());
dashboards.put(dashboardId, existingDashboard);
return true;
}
@Override
public boolean deleteDashboard(String dashboardId) {
return dashboards.remove(dashboardId) != null;
}
@Override
public boolean publishDashboard(String dashboardId) {
SelfServiceDashboard dashboard = dashboards.get(dashboardId);
if (dashboard != null) {
dashboard.setPublished(true);
dashboard.setUpdatedAt(new Date());
dashboards.put(dashboardId, dashboard);
return true;
}
return false;
}
@Override
public boolean addComponent(String dashboardId, SelfServiceDashboard.DashboardComponent component) {
SelfServiceDashboard dashboard = dashboards.get(dashboardId);
if (dashboard != null && component != null) {
if (component.getId() == null) {
component.setId("comp_" + System.currentTimeMillis());
}
component.setVisible(true);
dashboard.getComponents().add(component);
dashboard.setUpdatedAt(new Date());
dashboards.put(dashboardId, dashboard);
return true;
}
return false;
}
@Override
public boolean updateComponentLayout(String dashboardId, String componentId, int x, int y, int width, int height) {
SelfServiceDashboard dashboard = dashboards.get(dashboardId);
if (dashboard != null) {
for (SelfServiceDashboard.DashboardComponent component : dashboard.getComponents()) {
if (componentId.equals(component.getId())) {
component.setX(x);
component.setY(y);
component.setWidth(width);
component.setHeight(height);
dashboard.setUpdatedAt(new Date());
dashboards.put(dashboardId, dashboard);
return true;
}
}
}
return false;
}
@Override
public boolean removeComponent(String dashboardId, String componentId) {
SelfServiceDashboard dashboard = dashboards.get(dashboardId);
if (dashboard != null) {
boolean removed = dashboard.getComponents().removeIf(component ->
componentId.equals(component.getId())
);
if (removed) {
dashboard.setUpdatedAt(new Date());
dashboards.put(dashboardId, dashboard);
}
return removed;
}
return false;
}
@Override
public boolean shareDashboard(String dashboardId, String userId, String role) {
SelfServiceDashboard dashboard = dashboards.get(dashboardId);
if (dashboard != null) {
// 检查是否已经分享
for (SelfServiceDashboard.DashboardUser sharedUser : dashboard.getSharedUsers()) {
if (userId.equals(sharedUser.getUserId())) {
// 更新角色
sharedUser.setRole(role);
sharedUser.setSharedAt(new Date());
dashboard.setUpdatedAt(new Date());
dashboards.put(dashboardId, dashboard);
return true;
}
}
// 添加新的分享用户
SelfServiceDashboard.DashboardUser sharedUser = new SelfServiceDashboard.DashboardUser();
sharedUser.setUserId(userId);
sharedUser.setRole(role);
sharedUser.setSharedAt(new Date());
dashboard.getSharedUsers().add(sharedUser);
dashboard.setUpdatedAt(new Date());
dashboards.put(dashboardId, dashboard);
return true;
}
return false;
}
@Override
public boolean unshareDashboard(String dashboardId, String userId) {
SelfServiceDashboard dashboard = dashboards.get(dashboardId);
if (dashboard != null) {
boolean removed = dashboard.getSharedUsers().removeIf(sharedUser ->
userId.equals(sharedUser.getUserId())
);
if (removed) {
dashboard.setUpdatedAt(new Date());
dashboards.put(dashboardId, dashboard);
}
return removed;
}
return false;
}
@Override
public boolean configureSchedule(String dashboardId, SelfServiceDashboard.ScheduleConfig schedule) {
SelfServiceDashboard dashboard = dashboards.get(dashboardId);
if (dashboard != null && schedule != null) {
if (schedule.getId() == null) {
schedule.setId("sch_" + System.currentTimeMillis());
}
// 检查是否已存在相同类型的定时配置
dashboard.getSchedules().removeIf(existing ->
existing.getType().equals(schedule.getType())
);
dashboard.getSchedules().add(schedule);
dashboard.setUpdatedAt(new Date());
dashboards.put(dashboardId, dashboard);
return true;
}
return false;
}
@Override
public boolean setTheme(String dashboardId, String theme) {
SelfServiceDashboard dashboard = dashboards.get(dashboardId);
if (dashboard != null && StringUtils.hasText(theme)) {
dashboard.setTheme(theme);
dashboard.setUpdatedAt(new Date());
dashboards.put(dashboardId, dashboard);
return true;
}
return false;
}
@Override
public String copyDashboard(String dashboardId, String newName) {
SelfServiceDashboard originalDashboard = dashboards.get(dashboardId);
if (originalDashboard != null) {
// 创建副本
SelfServiceDashboard newDashboard = new SelfServiceDashboard();
// 复制基本信息
newDashboard.setName(newName);
newDashboard.setDescription("原看板:" + originalDashboard.getName() + " 的副本");
newDashboard.setTheme(originalDashboard.getTheme());
newDashboard.setLayout(originalDashboard.getLayout());
newDashboard.setPermission(originalDashboard.getPermission());
newDashboard.setDataRefresh(originalDashboard.getDataRefresh());
newDashboard.setPublished(false);
newDashboard.setCreatedBy("system_copy");
// 复制组件(创建新的ID避免冲突)
List<SelfServiceDashboard.DashboardComponent> newComponents = new ArrayList<>();
for (SelfServiceDashboard.DashboardComponent component : originalDashboard.getComponents()) {
SelfServiceDashboard.DashboardComponent newComponent = new SelfServiceDashboard.DashboardComponent();
newComponent.setId("comp_" + System.currentTimeMillis());
newComponent.setType(component.getType());
newComponent.setTitle(component.getTitle());
newComponent.setDescription(component.getDescription());
newComponent.setConfig(new HashMap<>(component.getConfig()));
newComponent.setX(component.getX());
newComponent.setY(component.getY());
newComponent.setWidth(component.getWidth());
newComponent.setHeight(component.getHeight());
newComponent.setVisible(component.isVisible());
newComponent.setDatasetId(component.getDatasetId());
newComponent.setChartId(component.getChartId());
newComponents.add(newComponent);
}
newDashboard.setComponents(newComponents);
// 保存新看板
return createSelfServiceDashboard(newDashboard);
}
return null;
}
@Override
public Map<String, Object> getDashboardStats(String dashboardId) {
SelfServiceDashboard dashboard = dashboards.get(dashboardId);
Map<String, Object> stats = new HashMap<>();
if (dashboard != null) {
stats.put("dashboardId", dashboardId);
stats.put("name", dashboard.getName());
stats.put("published", dashboard.isPublished());
stats.put("createdAt", dashboard.getCreatedAt());
stats.put("updatedAt", dashboard.getUpdatedAt());
stats.put("componentsCount", dashboard.getComponents().size());
stats.put("sharedUsersCount", dashboard.getSharedUsers().size());
stats.put("schedulesCount", dashboard.getSchedules().size());
// 组件类型统计
Map<String, Integer> componentTypeStats = new HashMap<>();
for (SelfServiceDashboard.DashboardComponent component : dashboard.getComponents()) {
componentTypeStats.put(
component.getType(),
componentTypeStats.getOrDefault(component.getType(), 0) + 1
);
}
stats.put("componentTypeStats", componentTypeStats);
// 计算最近使用时间(模拟)
stats.put("lastUsed", dashboard.getUpdatedAt());
stats.put("viewCount", (int)(Math.random() * 1000)); // 模拟浏览次数
} else {
stats.put("error", "看板不存在");
}
return stats;
}
@Override
public List<SelfServiceDashboard> searchDashboards(String keyword, String userId) {
List<SelfServiceDashboard> result = new ArrayList<>();
String keywordLower = keyword.toLowerCase();
for (SelfServiceDashboard dashboard : dashboards.values()) {
// 检查用户是否有权限访问该看板
boolean hasAccess = userId.equals(dashboard.getCreatedBy());
if (!hasAccess) {
for (SelfServiceDashboard.DashboardUser sharedUser : dashboard.getSharedUsers()) {
if (userId.equals(sharedUser.getUserId())) {
hasAccess = true;
break;
}
}
}
if (hasAccess) {
// 搜索匹配
boolean matches = false;
if (keywordLower.isEmpty()) {
matches = true;
} else {
if (dashboard.getName() != null &&
dashboard.getName().toLowerCase().contains(keywordLower)) {
matches = true;
}
if (dashboard.getDescription() != null &&
dashboard.getDescription().toLowerCase().contains(keywordLower)) {
matches = true;
}
for (SelfServiceDashboard.DashboardComponent component : dashboard.getComponents()) {
if (component.getTitle() != null &&
component.getTitle().toLowerCase().contains(keywordLower)) {
matches = true;
break;
}
}
}
if (matches) {
result.add(dashboard);
}
}
}
// 按创建时间降序排列
result.sort((a, b) -> b.getCreatedAt().compareTo(a.getCreatedAt()));
return result;
}
/**
* 创建默认组件
*/
private void createDefaultComponents(String dashboardId) {
SelfServiceDashboard dashboard = dashboards.get(dashboardId);
if (dashboard == null) return;
List<SelfServiceDashboard.DashboardComponent> components = new ArrayList<>();
// 1. 关键指标卡片
SelfServiceDashboard.DashboardComponent metricCard = new SelfServiceDashboard.DashboardComponent();
metricCard.setId("metric_total_usage");
metricCard.setType("metric");
metricCard.setTitle("总用水量");
metricCard.setDescription("今日系统总用水量");
metricCard.setX(0);
metricCard.setY(0);
metricCard.setWidth(6);
metricCard.setHeight(3);
metricCard.setVisible(true);
Map<String, Object> metricConfig = new HashMap<>();
metricConfig.put("value", "12,345");
metricConfig.put("unit", "立方米");
metricConfig.put("trend", "up");
metricConfig.put("color", "#007bff");
metricCard.setConfig(metricConfig);
components.add(metricCard);
// 2. 水质指标仪表盘
SelfServiceDashboard.DashboardComponent gaugeCard = new SelfServiceDashboard.DashboardComponent();
gaugeCard.setId("gauge_water_quality");
gaugeCard.setType("gauge");
gaugeCard.setTitle("水质达标率");
gaugeCard.setDescription("水质综合指标达标率");
gaugeCard.setX(6);
gaugeCard.setY(0);
gaugeCard.setWidth(6);
gaugeCard.setHeight(3);
gaugeCard.setVisible(true);
Map<String, Object> gaugeConfig = new HashMap<>();
gaugeConfig.put("value", "95");
gaugeConfig.put("min", 0);
gaugeConfig.put("max", 100);
gaugeConfig.put("unit", "%");
gaugeConfig.put("color", "#28a745");
gaugeCard.setConfig(gaugeConfig);
components.add(gaugeCard);
// 3. 用水量趋势图
SelfServiceDashboard.DashboardComponent trendChart = new SelfServiceDashboard.DashboardComponent();
trendChart.setId("chart_water_trend");
trendChart.setType("line");
trendChart.setTitle("用水量趋势");
trendChart.setDescription("最近7天用水量变化");
trendChart.setX(0);
trendChart.setY(3);
trendChart.setWidth(12);
trendChart.setHeight(6);
trendCard.setVisible(true);
Map<String, Object> trendConfig = new HashMap<>();
trendConfig.put("dataset", "water_consumption_ds");
trendConfig.put("xField", "date");
trendConfig.put("yField", "consumption");
trendConfig.put("title", "近7天用水量趋势");
trendConfig.put("legend", true);
trendChart.setConfig(trendConfig);
components.add(trendChart);
// 4. 区域用水量对比
SelfServiceDashboard.DashboardComponent barChart = new SelfServiceDashboard.DashboardComponent();
barChart.setId("chart_region_comparison");
barChart.setType("bar");
barChart.setTitle("区域用水量对比");
barChart.setDescription("各区域今日用水量统计");
barChart.setX(0);
barChart.setY(9);
barChart.setWidth(12);
barChart.setHeight(6);
barCard.setVisible(true);
Map<String, Object> barConfig = new HashMap<>();
barConfig.put("dataset", "water_region_ds");
barConfig.put("xField", "region");
barConfig.put("yField", "consumption");
barConfig.put("title", "各区域用水量对比");
barConfig.put("legend", false);
barChart.setConfig(barConfig);
components.add(barChart);
// 更新看板
dashboard.setComponents(components);
dashboard.setUpdatedAt(new Date());
dashboards.put(dashboardId, dashboard);
}
}