Generate admin chart aggregation endpoints
This commit is contained in:
@@ -34,4 +34,43 @@ public class ChartQueryParameter
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getJavaLiteral()
|
||||
{
|
||||
return toJavaLiteral(value);
|
||||
}
|
||||
|
||||
public static String toJavaLiteral(Object value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return "null";
|
||||
}
|
||||
if (value instanceof Number || value instanceof Boolean)
|
||||
{
|
||||
return String.valueOf(value);
|
||||
}
|
||||
return "\"" + escapeJava(String.valueOf(value)) + "\"";
|
||||
}
|
||||
|
||||
private static String escapeJava(String value)
|
||||
{
|
||||
StringBuilder escaped = new StringBuilder();
|
||||
for (int index = 0; index < value.length(); index++)
|
||||
{
|
||||
char ch = value.charAt(index);
|
||||
switch (ch)
|
||||
{
|
||||
case '\\': escaped.append("\\\\"); break;
|
||||
case '"': escaped.append("\\\""); break;
|
||||
case '\n': escaped.append("\\n"); break;
|
||||
case '\r': escaped.append("\\r"); break;
|
||||
case '\t': escaped.append("\\t"); break;
|
||||
case '\b': escaped.append("\\b"); break;
|
||||
case '\f': escaped.append("\\f"); break;
|
||||
default: escaped.append(ch);
|
||||
}
|
||||
}
|
||||
return escaped.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import com.ruoyi.generator.domain.front.dto.block.BusinessBlockConfigField;
|
||||
import com.ruoyi.generator.domain.front.dto.block.BusinessBlockDefinition;
|
||||
import com.ruoyi.generator.domain.front.dto.block.BusinessBlockInstance;
|
||||
import com.ruoyi.generator.domain.front.dto.block.BusinessBlockTemplateFile;
|
||||
import com.ruoyi.generator.domain.front.dto.block.ChartMetricConfig;
|
||||
import com.ruoyi.generator.domain.front.dto.block.ChartQueryParameter;
|
||||
import com.ruoyi.generator.domain.front.dto.block.ChartQueryRenderModel;
|
||||
import com.ruoyi.generator.domain.front.dto.block.PageBusinessBlockLayout;
|
||||
import com.ruoyi.generator.service.front.BusinessBlockRegistryService;
|
||||
@@ -473,6 +475,11 @@ public class BusinessBlockGenerationService
|
||||
context.put("chartType", file.getDefinition().getChartType());
|
||||
context.put("chartDataset", instance.getDataset());
|
||||
context.put("chartDisplay", instance.getDisplay());
|
||||
context.put("chartMetrics", chartMetricRenderModels(chartQuery.getMetrics()));
|
||||
context.put("chartDateStartLiteral", ChartQueryParameter.toJavaLiteral(
|
||||
chartQuery.getDateRange() == null ? null : chartQuery.getDateRange().getStart()));
|
||||
context.put("chartDateEndLiteral", ChartQueryParameter.toJavaLiteral(
|
||||
chartQuery.getDateRange() == null ? null : chartQuery.getDateRange().getEnd()));
|
||||
context.put("apiBasePath", "/admin/business-blocks/" + file.getInstanceCode());
|
||||
}
|
||||
context.put("cartPkColumn", pkColumnName(project, renderConfig.get("cartTable")));
|
||||
@@ -530,6 +537,22 @@ public class BusinessBlockGenerationService
|
||||
return renderConfig;
|
||||
}
|
||||
|
||||
private List<Map<String, String>> chartMetricRenderModels(List<ChartMetricConfig> metrics)
|
||||
{
|
||||
List<Map<String, String>> result = new ArrayList<Map<String, String>>();
|
||||
for (ChartMetricConfig metric : metrics == null
|
||||
? Collections.<ChartMetricConfig>emptyList() : metrics)
|
||||
{
|
||||
Map<String, String> item = new LinkedHashMap<String, String>();
|
||||
item.put("alias", metric.getAlias());
|
||||
item.put("aliasLiteral", ChartQueryParameter.toJavaLiteral(metric.getAlias()));
|
||||
item.put("labelLiteral", ChartQueryParameter.toJavaLiteral(
|
||||
StringUtils.defaultIfEmpty(metric.getLabel(), metric.getAlias())));
|
||||
result.add(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void putEmbeddedBlockVelocityContext(VelocityContext context, GenProject project,
|
||||
FrontProjectPageDesign pageDesign)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package ${packageName}.controller.block;
|
||||
|
||||
import java.util.Map;
|
||||
import ${packageName}.common.Result;
|
||||
import ${packageName}.service.block.I${InstanceClassName}Service;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("${apiBasePath}")
|
||||
public class ${InstanceClassName}Controller
|
||||
{
|
||||
@Autowired
|
||||
private I${InstanceClassName}Service ${instanceClassName}Service;
|
||||
|
||||
@GetMapping("/data")
|
||||
public Result<Map<String, Object>> data()
|
||||
{
|
||||
return Result.success(${instanceClassName}Service.loadData());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package ${packageName}.mapper.block;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface ${InstanceClassName}Mapper
|
||||
{
|
||||
List<Map<String, Object>> selectData(Map<String, Object> params);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="${packageName}.mapper.block.${InstanceClassName}Mapper">
|
||||
|
||||
<select id="selectData" parameterType="java.util.Map" resultType="java.util.LinkedHashMap">
|
||||
select
|
||||
#if(!$chartQuery.metricOnly)
|
||||
${chartQuery.dimensionSql} as dimensionKey,
|
||||
#end
|
||||
${chartQuery.metricSelectSql}
|
||||
from ${chartQuery.tableName} t
|
||||
#if($chartQuery.whereSql)
|
||||
where ${chartQuery.whereSql}
|
||||
#end
|
||||
#if($chartQuery.groupBySql)
|
||||
group by ${chartQuery.groupBySql}
|
||||
#end
|
||||
#if($chartQuery.orderBySql)
|
||||
order by ${chartQuery.orderBySql}
|
||||
#end
|
||||
limit ${chartQuery.limit}
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -0,0 +1,8 @@
|
||||
package ${packageName}.service.block;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface I${InstanceClassName}Service
|
||||
{
|
||||
Map<String, Object> loadData();
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package ${packageName}.service.block.impl;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import ${packageName}.mapper.block.${InstanceClassName}Mapper;
|
||||
import ${packageName}.service.block.I${InstanceClassName}Service;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class ${InstanceClassName}ServiceImpl implements I${InstanceClassName}Service
|
||||
{
|
||||
@Autowired
|
||||
private ${InstanceClassName}Mapper ${instanceClassName}Mapper;
|
||||
|
||||
@Override
|
||||
public Map<String, Object> loadData()
|
||||
{
|
||||
Map<String, Object> params = new LinkedHashMap<String, Object>();
|
||||
#foreach($parameter in $chartQuery.parameters)
|
||||
params.put("${parameter.name}", ${parameter.javaLiteral});
|
||||
#end
|
||||
putDateRange(params);
|
||||
|
||||
List<Map<String, Object>> rows = ${instanceClassName}Mapper.selectData(params);
|
||||
List<Map<String, Object>> dimensions = new ArrayList<Map<String, Object>>();
|
||||
List<Map<String, Object>> series = new ArrayList<Map<String, Object>>();
|
||||
Map<String, Object> summary = new LinkedHashMap<String, Object>();
|
||||
|
||||
#if($chartQuery.metricOnly)
|
||||
Object value = rows.isEmpty() ? null : rows.get(0).get(${chartMetrics.get(0).aliasLiteral});
|
||||
summary.put("value", value);
|
||||
summary.put("count", Integer.valueOf(rows.size()));
|
||||
#else
|
||||
#foreach($metric in $chartMetrics)
|
||||
series.add(seriesItem(${metric.labelLiteral}, ${metric.aliasLiteral}));
|
||||
#end
|
||||
for (Map<String, Object> row : rows)
|
||||
{
|
||||
Map<String, Object> dimension = new LinkedHashMap<String, Object>();
|
||||
dimension.put("label", row.get("dimensionKey"));
|
||||
dimension.put("value", row.get("dimensionKey"));
|
||||
dimensions.add(dimension);
|
||||
#foreach($metric in $chartMetrics)
|
||||
appendSeriesValue(series, ${foreach.index}, row.get(${metric.aliasLiteral}));
|
||||
#end
|
||||
}
|
||||
summary.put("count", Integer.valueOf(rows.size()));
|
||||
#end
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<String, Object>();
|
||||
result.put("dimensions", dimensions);
|
||||
result.put("series", series);
|
||||
result.put("summary", summary);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, Object> seriesItem(String name, String alias)
|
||||
{
|
||||
Map<String, Object> item = new LinkedHashMap<String, Object>();
|
||||
item.put("name", name);
|
||||
item.put("alias", alias);
|
||||
item.put("values", new ArrayList<Object>());
|
||||
return item;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void appendSeriesValue(List<Map<String, Object>> series, int index, Object value)
|
||||
{
|
||||
((List<Object>) series.get(index).get("values")).add(value);
|
||||
}
|
||||
|
||||
private void putDateRange(Map<String, Object> params)
|
||||
{
|
||||
#if($chartQuery.dateRange)
|
||||
LocalDate today = LocalDate.now();
|
||||
#if($chartQuery.dateRange.preset == "today")
|
||||
params.put("dateStart", today.atStartOfDay());
|
||||
params.put("dateEnd", today.plusDays(1).atStartOfDay());
|
||||
#elseif($chartQuery.dateRange.preset == "last_7_days")
|
||||
params.put("dateStart", today.minusDays(6).atStartOfDay());
|
||||
params.put("dateEnd", today.plusDays(1).atStartOfDay());
|
||||
#elseif($chartQuery.dateRange.preset == "last_30_days")
|
||||
params.put("dateStart", today.minusDays(29).atStartOfDay());
|
||||
params.put("dateEnd", today.plusDays(1).atStartOfDay());
|
||||
#elseif($chartQuery.dateRange.preset == "this_month")
|
||||
params.put("dateStart", today.withDayOfMonth(1).atStartOfDay());
|
||||
params.put("dateEnd", today.withDayOfMonth(1).plusMonths(1).atStartOfDay());
|
||||
#elseif($chartQuery.dateRange.preset == "this_year")
|
||||
params.put("dateStart", today.withDayOfYear(1).atStartOfDay());
|
||||
params.put("dateEnd", today.withDayOfYear(1).plusYears(1).atStartOfDay());
|
||||
#else
|
||||
params.put("dateStart", LocalDate.parse(${chartDateStartLiteral}).atStartOfDay());
|
||||
params.put("dateEnd", LocalDate.parse(${chartDateEndLiteral}).plusDays(1).atStartOfDay());
|
||||
#end
|
||||
#end
|
||||
}
|
||||
}
|
||||
@@ -344,6 +344,29 @@ public class BusinessBlockGenerationServiceTest
|
||||
assertEquals(Integer.valueOf(4), blocks.get(1).get("span"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generatedChartBackendUsesBoundParametersAndUnifiedResponse()
|
||||
{
|
||||
Map<String, String> rendered = renderChartFiles(chartProject());
|
||||
|
||||
assertTrue(rendered.get("mapper").contains(
|
||||
"List<Map<String, Object>> selectData(Map<String, Object> params)"));
|
||||
assertTrue(rendered.get("mapperXml").contains("t.status = #{filter0}"));
|
||||
assertTrue(rendered.get("mapperXml").contains("t.create_time >= #{dateStart}"));
|
||||
assertTrue(rendered.get("mapperXml").contains("t.create_time < #{dateEnd}"));
|
||||
assertFalse(rendered.get("mapperXml").contains("PAID"));
|
||||
assertTrue(rendered.get("mapperXml").contains("limit 100"));
|
||||
assertTrue(rendered.get("serviceImpl").contains("params.put(\"filter0\", \"PAID\")"));
|
||||
assertTrue(rendered.get("serviceImpl").contains("today.minusDays(29).atStartOfDay()"));
|
||||
assertTrue(rendered.get("serviceImpl").contains("seriesItem(\"Sales\", \"salesAmount\")"));
|
||||
assertTrue(rendered.get("serviceImpl").contains("\"dimensions\""));
|
||||
assertTrue(rendered.get("serviceImpl").contains("\"series\""));
|
||||
assertTrue(rendered.get("serviceImpl").contains("\"summary\""));
|
||||
assertTrue(rendered.get("controller").contains(
|
||||
"@RequestMapping(\"/admin/business-blocks/admin_line_chart_001\")"));
|
||||
assertTrue(rendered.get("controller").contains("@GetMapping(\"/data\")"));
|
||||
}
|
||||
|
||||
private void setField(String name, Object value) throws Exception
|
||||
{
|
||||
Field field = BusinessBlockGenerationService.class.getDeclaredField(name);
|
||||
@@ -406,6 +429,19 @@ public class BusinessBlockGenerationServiceTest
|
||||
return count;
|
||||
}
|
||||
|
||||
private Map<String, String> renderChartFiles(GenProject project)
|
||||
{
|
||||
Map<String, String> rendered = new LinkedHashMap<String, String>();
|
||||
for (GeneratedBusinessBlockFile file : service.listFiles(project, "backend"))
|
||||
{
|
||||
if ("admin_line_chart_001".equals(file.getInstanceCode()))
|
||||
{
|
||||
rendered.put(file.getTemplateKey(), service.renderFile(project, file));
|
||||
}
|
||||
}
|
||||
return rendered;
|
||||
}
|
||||
|
||||
private void assertRenderedFiles(GenProject project, List<GeneratedBusinessBlockFile> files)
|
||||
{
|
||||
for (GeneratedBusinessBlockFile file : files)
|
||||
@@ -550,6 +586,7 @@ public class BusinessBlockGenerationServiceTest
|
||||
+ "\"dimension\":{\"field\":\"createTime\",\"timeUnit\":\"day\"},"
|
||||
+ "\"metrics\":[{\"field\":\"totalAmount\",\"aggregate\":\"sum\",\"alias\":\"salesAmount\",\"label\":\"Sales\"}],"
|
||||
+ "\"filters\":[{\"field\":\"status\",\"operator\":\"eq\",\"value\":\"PAID\"}],"
|
||||
+ "\"dateRange\":{\"field\":\"createTime\",\"preset\":\"last_30_days\"},"
|
||||
+ "\"sort\":{\"by\":\"dimension\",\"order\":\"asc\"},\"limit\":100},"
|
||||
+ "\"display\":{\"title\":\"Sales Trend\",\"smooth\":true}},"
|
||||
+ "{\"id\":\"admin_metric_chart_001\",\"blockCode\":\"admin_metric_chart\",\"name\":\"Order Count\",\"span\":4,"
|
||||
|
||||
@@ -53,6 +53,17 @@ public class ChartBlockRenderSupportTest
|
||||
assertFalse(model.getWhereSql().contains("OR 1=1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rendersFilterValuesAsSafeJavaLiteralsForGeneratedServices()
|
||||
{
|
||||
BusinessBlockInstance instance = lineInstance();
|
||||
instance.getDataset().getFilters().get(0).setValue("PAID\" + run();\n//");
|
||||
|
||||
ChartQueryRenderModel model = support.build(orderTable(), definition("line"), instance);
|
||||
|
||||
assertEquals("\"PAID\\\" + run();\\n//\"", model.getParameters().get(0).getJavaLiteral());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formatsDayWeekAndMonthDimensionsFromResolvedColumns()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user