业务场景
前端使用一个data属性接收数据,而Highchart中对于不同的图表类型,接收数据的数据格式是不同的。
柱状图类型需要的是
{
xData:["苹果","香蕉","梨子","橘子"],
yData:[12,3,4,5]
}
那么前端赋值将data.xData、data.yData赋值给对应属性
而饼图需要的数据格式是
{
{
name:"苹果",
y:12
},
{
name:"香蕉",
y:3
},
{
name:"梨子",
y:4
},
{
name:"橘子",
y:5
},
}
这些图表实例的数据都保存在一个数据库表中,从表中取出来的数据的字段属性都是一致的,仅通过一个图表类型进行区分不同的策略类,具体策略会将同样一份数据封装为不同的数据格式供前端使用
实现
(1)定义策略类父接口
public interface ChartStrategy {
Object recordHandleToDisplay(List<ChartRecord> records);
}
(2)具体策略类
柱状图
@Component("singleBaseChartStrategy")
public class SingleBaseChartStrategyImpl implements ChartStrategy{
@Autowired
private PctSeriesService pctSeriesService;
@Override
public Object recordHandleToDisplay(List<ChartRecord> records) {
Map<String,Object> dataMap = new HashMap<>(4);
//x轴信息集合(封装x轴数据)
List<String> categories = new ArrayList<>();
//查询出来该图表下拥有的所有数据列信息
PctSeries pctSeries = pctSeriesService.selectBySingleChartId(records.get(0).getChartId());
//数据列里的数据
List<Double> dataList = new ArrayList<>();
//将ChartRecord集合中的x、y轴数据存入集合
for (ChartRecord chartRecord:records) {
categories.add(chartRecord.getXValue());
dataList.add(chartRecord.getYValue());
}
//封装series数据
List<Map<String,Object>> series = new ArrayList<>();
Map<String,Object> map = new HashMap<>(4);
map.put("name",pctSeries.getName());
map.put("colorByPoint",true);
map.put("data",dataList);
series.add(map);
dataMap.put("categories",categories);
dataMap.put("series",series);
return dataMap;
}
}
这里是使用map进行数据封装,其实最好应该定义一个业务领域模型类来替代Map,可以避免硬编码。这个策略类里data属性是一个List<Double>格式
注意:该类使用@Component注解,以注入到IOC容器中
饼图
@Component("basePieChartStrategy")
public class BasePieChartStrategyImpl implements ChartStrategy{
@Autowired
private PctSeriesService pctSeriesService;
@Override
public Object recordHandleToDisplay(List<ChartRecord> records) {
//指定哈希Map集合长度
int hashMapLength = 2;
//得到该饼图的数据列
PctSeries pctSeries = pctSeriesService.selectBySingleChartId(records.get(0).getChartId());
//定义数据列集合
List<Map<String,Object>> seriesList = new ArrayList<>();
//定义数据列集合中的对象
Map<String, Object> seriesMap = new HashMap<>(hashMapLength);
//定义数据值集合
List<Map<String, Object>> data = new ArrayList<>();
//循环遍历赋值
for (int i = 0; i < records.size(); i++) {
Map<String, Object> map = new HashMap<>(hashMapLength);
map.put("name", records.get(i).getXValue());
map.put("y", records.get(i).getYValue());
data.add(map);
}
seriesMap.put("name", pctSeries.getName());
seriesMap.put("data", data);
seriesList.add(seriesMap);
return seriesList;
}
}
这里将数据分别存储到name、y属性中
(3)策略上下文
它维护所有具体策略类型,向外暴露一个访问的接口,然后决定使用哪一种策略类
@Service("chartStrategyContext")
public class ChartStrategyContext {
private static final String NO_CHART_RECORDS = "未找到图表数据";
private static final String NO_CHART_TYPE_ENUM = "未找到对应ChartTypeEnum实例";
private static final String NO_CHART_STRATEGY = "未找到对应图表策略实例";
/**
* context维护所有策略实现对象集
*/
private final Map<String, ChartStrategy> strategyMap = new ConcurrentHashMap<>();
/**
*
*
* @date 14:15 2020/4/7
* @author 李文龙
* @param strategyMap: 容器中所有实现了ChartStrategy接口的实现类对象都会注入到该形参中
* k=beanId,v=全限定类名-实现类对象
* @return
**/
@Autowired
public ChartStrategyContext(Map<String, ChartStrategy> strategyMap) {
this.strategyMap.clear();
strategyMap.forEach((k, v) -> this.strategyMap.put(k, v));
}
public Map<String, ChartStrategy> getStrategyMap() {
return this.strategyMap;
}
/**
* 根据不同的图表类型-typeEn值,去Context中获取对应的策略实例
* 去对源数据-records进行处理
*
* @date 14:33 2020/4/7
* @author 李文龙
* @param typeEn: Chart.typeEn值。图表类型
* @param records:图表源数据,来自数据库表:PCT_RECORD等表
* @return Object:不同的图表需要不同格式数据
**/
public Object recordHandleToDisplay(Long typeEn, List<ChartRecord> records) {
ChartTypeEnum chartTypeEnum = ChartTypeEnum.getById(typeEn);
ChartStrategy strategy;
if(CollectionUtil.isEmpty(records)){
return NO_CHART_RECORDS;
}
if (chartTypeEnum == null) {
return NO_CHART_TYPE_ENUM;
}
strategy = strategyMap.get(chartTypeEnum.getBeanId());
if (strategy == null) {
return NO_CHART_STRATEGY;
}
return strategy.recordHandleToDisplay(records);
}
}
注意:所有的具体策略类都使用了@Component注解注入到IOC容器中,该上下文类也使用了@Service来将其注入到IOC中。而该类的构造器有一个strategyMap形参,