mirror of
http://192.168.1.205:9980/cf_devdept2/cf_imes_server.git
synced 2026-08-14 05:42:06 +08:00
报表基础模块:代码规范修复
This commit is contained in:
+8
-8
@@ -11,16 +11,16 @@ import com.cf.imes.framework.common.exception.ErrorCode;
|
|||||||
public final class ErrorCodeConstants {
|
public final class ErrorCodeConstants {
|
||||||
|
|
||||||
// ========== UREPORT template模块 1-003-001-000 ==========
|
// ========== UREPORT template模块 1-003-001-000 ==========
|
||||||
public static ErrorCode TEMPLATE_NOT_EXISTS = new ErrorCode(1_003_001_001, "报表模板信息不存在");
|
public static final ErrorCode TEMPLATE_NOT_EXISTS = new ErrorCode(1_003_001_001, "报表模板信息不存在");
|
||||||
|
|
||||||
// ========== UREPORT datasource模块 1-003-002-000 ==========
|
// ========== UREPORT datasource模块 1-003-002-000 ==========
|
||||||
public static ErrorCode DATASOURCE_NOT_EXISTS = new ErrorCode(1_003_002_001, "报表数据源不存在");
|
public static final ErrorCode DATASOURCE_NOT_EXISTS = new ErrorCode(1_003_002_001, "报表数据源不存在");
|
||||||
public static ErrorCode DATASOURCE_CONNECT_FAIL = new ErrorCode(1_003_002_001, "报表数据源连接失败");
|
public static final ErrorCode DATASOURCE_CONNECT_FAIL = new ErrorCode(1_003_002_001, "报表数据源连接失败");
|
||||||
|
|
||||||
// ========== UREPORT dataset模块 1-003-003-000 ==========
|
// ========== UREPORT dataset模块 1-003-003-000 ==========
|
||||||
public static ErrorCode DATASET_NOT_EXISTS = new ErrorCode(1_003_003_001, "报表数据集不存在");
|
public static final ErrorCode DATASET_NOT_EXISTS = new ErrorCode(1_003_003_001, "报表数据集不存在");
|
||||||
public static ErrorCode DATASET_SQL_INJECTION_RISK = new ErrorCode(1_003_003_002, "存在SQL注入风险");
|
public static final ErrorCode DATASET_SQL_INJECTION_RISK = new ErrorCode(1_003_003_002, "存在SQL注入风险");
|
||||||
public static ErrorCode DATASET_SQL_REQUIRED = new ErrorCode(1_003_003_003, "SQL语句不能为空");
|
public static final ErrorCode DATASET_SQL_REQUIRED = new ErrorCode(1_003_003_003, "SQL语句不能为空");
|
||||||
public static ErrorCode DATASET_SQL_ILLEGAL = new ErrorCode(1_003_003_004, "SQL语句非法");
|
public static final ErrorCode DATASET_SQL_ILLEGAL = new ErrorCode(1_003_003_004, "SQL语句非法");
|
||||||
public static ErrorCode DATASET_GET_FIELDS_ERROR = new ErrorCode(1_003_003_005, "获取表字段");
|
public static final ErrorCode DATASET_GET_FIELDS_ERROR = new ErrorCode(1_003_003_005, "获取表字段");
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -18,11 +18,11 @@ import io.swagger.v3.oas.annotations.Parameter;
|
|||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ import static com.cf.imes.framework.common.pojo.CommonResult.success;
|
|||||||
@Validated
|
@Validated
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ReportTemplateController {
|
public class ReportTemplateController {
|
||||||
@Autowired
|
@Resource
|
||||||
private ReportTemplateService templateService;
|
private ReportTemplateService templateService;
|
||||||
|
|
||||||
@PutMapping("/template")
|
@PutMapping("/template")
|
||||||
|
|||||||
-29
@@ -1,29 +0,0 @@
|
|||||||
package com.cf.imes.module.report.framework.ureport.bean;
|
|
||||||
|
|
||||||
import com.cf.imes.module.report.framework.ureport.annotation.CfReportSpringbeanDatasource;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Gqr
|
|
||||||
* @since 2024/7/11 17:15
|
|
||||||
*/
|
|
||||||
@CfReportSpringbeanDatasource(value = "MockBeanDatasource", name = "模拟bean数据源")
|
|
||||||
public class MockBeanDatasource {
|
|
||||||
|
|
||||||
public List<Map<String, Object>> mockList(String dsName, String datasetName, Map<String, Object> parameters) {
|
|
||||||
List<Map<String, Object>> testList = new ArrayList<>();
|
|
||||||
testList.add(new HashMap<String, Object>() {{
|
|
||||||
put("a", "11111");
|
|
||||||
put("b", "一一一一一一");
|
|
||||||
}});
|
|
||||||
testList.add(new HashMap<String, Object>() {{
|
|
||||||
put("a", "22222");
|
|
||||||
put("b", "二二二二二二");
|
|
||||||
}});
|
|
||||||
return testList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+32
-29
@@ -44,6 +44,7 @@ public class OrderSummaryBeanDatasource {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public List<Map<String, Object>> orderInfo(String dsName, String datasetName, Map<String, Object> parameters) {
|
public List<Map<String, Object>> orderInfo(String dsName, String datasetName, Map<String, Object> parameters) {
|
||||||
|
log.info("报表数据源[OrderSummaryBeanDatasource][orderInfo][{}][{}]调用开始", dsName, datasetName);
|
||||||
List<Map<String, Object>> resultList = new ArrayList<>();
|
List<Map<String, Object>> resultList = new ArrayList<>();
|
||||||
|
|
||||||
Object orderId = parameters.get(ORDER_ID_FIELD_NAME);
|
Object orderId = parameters.get(ORDER_ID_FIELD_NAME);
|
||||||
@@ -57,20 +58,20 @@ public class OrderSummaryBeanDatasource {
|
|||||||
|
|
||||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
if (orderDTO != null) {
|
if (orderDTO != null) {
|
||||||
// 把orderInfo转map并put到resultList中
|
HashMap<String, Object> resultMap = new HashMap<>();
|
||||||
resultList.add(new HashMap<>() {{
|
// 构造报表所需结构
|
||||||
// 构造报表所需结构
|
resultMap.put(ORDER_ID_FIELD_NAME, orderDTO.getId());
|
||||||
put(ORDER_ID_FIELD_NAME, orderDTO.getId());
|
resultMap.put("customOrderNo", orderDTO.getCustomOrderNo());
|
||||||
put("customOrderNo", orderDTO.getCustomOrderNo());
|
resultMap.put("dealer", orderDTO.getDealer());
|
||||||
put("dealer", orderDTO.getDealer());
|
LocalDateTime orderDate = orderDTO.getOrderDate();
|
||||||
LocalDateTime orderDate = orderDTO.getOrderDate();
|
resultMap.put("orderDate", ObjectUtil.isNotNull(orderDate) ? formatter.format(orderDate) : null);
|
||||||
put("orderDate", ObjectUtil.isNotNull(orderDate) ? formatter.format(orderDate) : null);
|
LocalDateTime deliveryDate = orderDTO.getDeliveryDate();
|
||||||
LocalDateTime deliveryDate = orderDTO.getDeliveryDate();
|
resultMap.put("deliveryDate", ObjectUtil.isNotNull(deliveryDate) ? formatter.format(deliveryDate) : null);
|
||||||
put("deliveryDate", ObjectUtil.isNotNull(deliveryDate) ? formatter.format(deliveryDate) : null);
|
resultMap.put("customer", orderDTO.getCustomer());
|
||||||
put("customer", orderDTO.getCustomer());
|
resultMap.put("phoneNumber", orderDTO.getPhoneNumber());
|
||||||
put("phoneNumber", orderDTO.getPhoneNumber());
|
resultList.add(resultMap);
|
||||||
}});
|
|
||||||
}
|
}
|
||||||
|
log.info("报表数据源[OrderSummaryBeanDatasource][orderInfo][{}][{}]调用结束", dsName, datasetName);
|
||||||
return resultList;
|
return resultList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +84,7 @@ public class OrderSummaryBeanDatasource {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public List<Map<String, Object>> orderPlateSummaryByPlate(String dsName, String datasetName, Map<String, Object> parameters) {
|
public List<Map<String, Object>> orderPlateSummaryByPlate(String dsName, String datasetName, Map<String, Object> parameters) {
|
||||||
|
log.info("报表数据源[OrderSummaryBeanDatasource][orderPlateSummaryByPlate][{}][{}]调用开始", dsName, datasetName);
|
||||||
List<Map<String, Object>> resultList = new ArrayList<>();
|
List<Map<String, Object>> resultList = new ArrayList<>();
|
||||||
|
|
||||||
Object orderId = parameters.get(ORDER_ID_FIELD_NAME);
|
Object orderId = parameters.get(ORDER_ID_FIELD_NAME);
|
||||||
@@ -92,24 +94,25 @@ public class OrderSummaryBeanDatasource {
|
|||||||
|
|
||||||
if (CollUtil.isNotEmpty(plateGoodsRespDTOS)) {
|
if (CollUtil.isNotEmpty(plateGoodsRespDTOS)) {
|
||||||
plateGoodsRespDTOS.forEach(p -> {
|
plateGoodsRespDTOS.forEach(p -> {
|
||||||
resultList.add(new HashMap<>() {{
|
Map<String, Object> resultMap = new HashMap<>();
|
||||||
BigDecimal area = p.getArea();
|
BigDecimal area = p.getArea();
|
||||||
Integer count = p.getCount();
|
Integer count = p.getCount();
|
||||||
// 构造报表所需结构
|
// 构造报表所需结构
|
||||||
put("roomName", p.getRoomName());
|
resultMap.put("roomName", p.getRoomName());
|
||||||
put("bodyName", p.getBodyName());
|
resultMap.put("bodyName", p.getBodyName());
|
||||||
put("thickness", p.getThickness());
|
resultMap.put("thickness", p.getThickness());
|
||||||
put("name", p.getName());
|
resultMap.put("name", p.getName());
|
||||||
put("height", p.getHeight());
|
resultMap.put("height", p.getHeight());
|
||||||
put("width", p.getWidth());
|
resultMap.put("width", p.getWidth());
|
||||||
put("count", count);
|
resultMap.put("count", count);
|
||||||
put("area", area);
|
resultMap.put("area", area);
|
||||||
put("openDoorType", p.getOpenDoorType());
|
resultMap.put("openDoorType", p.getOpenDoorType());
|
||||||
put("remark", p.getRemark());
|
resultMap.put("remark", p.getRemark());
|
||||||
put("areaAll", area.multiply(BigDecimal.valueOf(count)));
|
resultMap.put("areaAll", area.multiply(BigDecimal.valueOf(count)));
|
||||||
}});
|
resultList.add(resultMap);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
log.info("报表数据源[OrderSummaryBeanDatasource][orderPlateSummaryByPlate][{}][{}]调用结束", dsName, datasetName);
|
||||||
return resultList;
|
return resultList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-15
@@ -31,21 +31,22 @@ public class CfReportSpringbeanRegistrar implements ImportBeanDefinitionRegistra
|
|||||||
Set<BeanDefinition> candidates = scanner.findCandidateComponents("com.cf.imes.module.report.framework.ureport.bean");
|
Set<BeanDefinition> candidates = scanner.findCandidateComponents("com.cf.imes.module.report.framework.ureport.bean");
|
||||||
|
|
||||||
for (BeanDefinition candidate : candidates) {
|
for (BeanDefinition candidate : candidates) {
|
||||||
if (candidate instanceof AnnotatedBeanDefinition) {
|
AnnotatedBeanDefinition beanDef = (AnnotatedBeanDefinition) candidate;
|
||||||
AnnotatedBeanDefinition beanDef = (AnnotatedBeanDefinition) candidate;
|
Map<String, Object> attributes = beanDef.getMetadata().getAnnotationAttributes(CfReportSpringbeanDatasource.class.getName());
|
||||||
Map<String, Object> attributes = beanDef.getMetadata().getAnnotationAttributes(CfReportSpringbeanDatasource.class.getName());
|
if (attributes != null) {
|
||||||
if (attributes != null) {
|
// 校验注解上的value
|
||||||
// 校验注解上的value
|
String value = (String) attributes.get("value");
|
||||||
String value = (String) attributes.get("value");
|
if (!StringUtils.hasText(value)) {
|
||||||
if (!StringUtils.hasText(value)) {
|
throw new IllegalArgumentException("@CfReportSpringbean requires a non-empty 'value' attribute.");
|
||||||
throw new IllegalArgumentException("@CfReportSpringbean requires a non-empty 'value' attribute.");
|
}
|
||||||
}
|
// 校验注解上的name
|
||||||
// 校验注解上的name
|
String name = (String) attributes.get("name");
|
||||||
String name = (String) attributes.get("name");
|
if (!StringUtils.hasText(name)) {
|
||||||
if (!StringUtils.hasText(name)) {
|
throw new IllegalArgumentException("@CfReportSpringbean requires a non-empty 'name' attribute.");
|
||||||
throw new IllegalArgumentException("@CfReportSpringbean requires a non-empty 'name' attribute.");
|
}
|
||||||
}
|
String beanClassName = candidate.getBeanClassName();
|
||||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(candidate.getBeanClassName());
|
if (StringUtils.hasText(beanClassName)) {
|
||||||
|
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClassName);
|
||||||
registry.registerBeanDefinition(value, builder.getBeanDefinition());
|
registry.registerBeanDefinition(value, builder.getBeanDefinition());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -2,6 +2,7 @@ package com.cf.imes.module.report.service.datasource;
|
|||||||
|
|
||||||
import cn.hutool.core.annotation.AnnotationUtil;
|
import cn.hutool.core.annotation.AnnotationUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.sql.SqlInjectionUtils;
|
||||||
import com.bstek.ureport.definition.dataset.Field;
|
import com.bstek.ureport.definition.dataset.Field;
|
||||||
import com.bstek.ureport.definition.dataset.Parameter;
|
import com.bstek.ureport.definition.dataset.Parameter;
|
||||||
import com.bstek.ureport.definition.dataset.SqlDatasetDefinition;
|
import com.bstek.ureport.definition.dataset.SqlDatasetDefinition;
|
||||||
@@ -222,13 +223,14 @@ public class ReportDatasourceServiceImpl implements ReportDatasourceService {
|
|||||||
}
|
}
|
||||||
// 获取数据库连接
|
// 获取数据库连接
|
||||||
conn = buildConn(reqVO);
|
conn = buildConn(reqVO);
|
||||||
// 校验sql
|
// 自定义工具校验sql
|
||||||
if (SqlInjectionUtil.checkEditSql(sql)) {
|
if (SqlInjectionUtil.checkEditSql(sql)) {
|
||||||
throw exception(DATASET_SQL_ILLEGAL);
|
throw exception(DATASET_SQL_ILLEGAL);
|
||||||
}
|
}
|
||||||
// 检查参数sql注入
|
// 检查参数sql注入
|
||||||
for (ReportDatasetParameterVO parameterVO : parameters) {
|
for (ReportDatasetParameterVO parameterVO : parameters) {
|
||||||
if (SqlInjectionUtil.checkParam(parameterVO.getDefaultValue())) {
|
// mybatis-plus util检查参数
|
||||||
|
if (SqlInjectionUtils.check(parameterVO.getDefaultValue())) {
|
||||||
throw exception(DATASET_SQL_INJECTION_RISK);
|
throw exception(DATASET_SQL_INJECTION_RISK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-27
@@ -16,12 +16,6 @@ public class SqlInjectionUtil {
|
|||||||
private static final Pattern SQL_EDIT_PATTERN = Pattern.compile("(insert|delete|update|create|drop|truncate|grant|alter|deny|revoke|call|execute|exec|declare|show|rename|set)" +
|
private static final Pattern SQL_EDIT_PATTERN = Pattern.compile("(insert|delete|update|create|drop|truncate|grant|alter|deny|revoke|call|execute|exec|declare|show|rename|set)" +
|
||||||
"\\s+.*(into|from|set|where|table|database|view|index|on|cursor|procedure|trigger|for|password|union|and|or)", Pattern.CASE_INSENSITIVE);
|
"\\s+.*(into|from|set|where|table|database|view|index|on|cursor|procedure|trigger|for|password|union|and|or)", Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
/**
|
|
||||||
* SQL语法检查正则:符合两个关键字(有先后顺序)才算匹配
|
|
||||||
*/
|
|
||||||
private static final Pattern SQL_SYNTAX_PATTERN = Pattern.compile("(insert|delete|update|select|create|drop|truncate|grant|alter|deny|revoke|call|execute|exec|declare|show|rename|set)" +
|
|
||||||
"\\s+.*(into|from|set|where|table|database|view|index|on|cursor|procedure|trigger|for|password|union|and|or)|(select\\s*\\*\\s*from\\s+)|(and|or)\\s+.*(like|=|>|<|in|between|is|not|exists)", Pattern.CASE_INSENSITIVE);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 使用'、;或注释截断SQL检查正则
|
* 使用'、;或注释截断SQL检查正则
|
||||||
*/
|
*/
|
||||||
@@ -38,26 +32,6 @@ public class SqlInjectionUtil {
|
|||||||
return SQL_COMMENT_PATTERN.matcher(sql).find() || SQL_EDIT_PATTERN.matcher(sql).find();
|
return SQL_COMMENT_PATTERN.matcher(sql).find() || SQL_EDIT_PATTERN.matcher(sql).find();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private SqlInjectionUtil() {
|
||||||
* 检查参数是否存在 SQL 注入
|
|
||||||
*
|
|
||||||
* @param value 检查参数
|
|
||||||
* @return true 非法 false 合法
|
|
||||||
*/
|
|
||||||
public static boolean checkParam(String value) {
|
|
||||||
Objects.requireNonNull(value);
|
|
||||||
// 处理是否包含SQL注释字符 || 检查是否包含SQL注入敏感字符
|
|
||||||
return SQL_COMMENT_PATTERN.matcher(value).find() || SQL_SYNTAX_PATTERN.matcher(value).find();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 刪除字段转义符单引号双引号
|
|
||||||
*
|
|
||||||
* @param text 待处理字段
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static String removeEscapeCharacter(String text) {
|
|
||||||
Objects.nonNull(text);
|
|
||||||
return text.replace("\"", "").replace("'", "");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -88,7 +88,7 @@ public class ReportDatasetServiceImplTest extends ReportCommonServiceImplTest{
|
|||||||
List<ReportDatasetDO> datasetList = reportDatasetService.getDatasetList(reqVO);
|
List<ReportDatasetDO> datasetList = reportDatasetService.getDatasetList(reqVO);
|
||||||
|
|
||||||
assertNotNull(datasetList);
|
assertNotNull(datasetList);
|
||||||
assertNotEquals(datasetList.size(), 0);
|
assertNotEquals(0, datasetList.size());
|
||||||
|
|
||||||
// 删除
|
// 删除
|
||||||
reportTemplateService.deleteReportTemplate(templateId);
|
reportTemplateService.deleteReportTemplate(templateId);
|
||||||
|
|||||||
+1
-1
@@ -73,7 +73,7 @@ public class ReportDatasourceServiceImplTest extends ReportCommonServiceImplTest
|
|||||||
List<ReportDatasourceDO> templateDatasourceList = reportDatasourceService.getTemplateDatasourceList(reqVO);
|
List<ReportDatasourceDO> templateDatasourceList = reportDatasourceService.getTemplateDatasourceList(reqVO);
|
||||||
|
|
||||||
assertNotNull(templateDatasourceList);
|
assertNotNull(templateDatasourceList);
|
||||||
assertNotEquals(templateDatasourceList.size(),0);
|
assertNotEquals(0, templateDatasourceList.size());
|
||||||
|
|
||||||
// 删除
|
// 删除
|
||||||
reportTemplateService.deleteReportTemplate(templateId);
|
reportTemplateService.deleteReportTemplate(templateId);
|
||||||
|
|||||||
+3
-3
@@ -44,7 +44,7 @@ public class ReportTemplateServiceImplTest extends ReportCommonServiceImplTest {
|
|||||||
String templateUnZip = template.getContent();
|
String templateUnZip = template.getContent();
|
||||||
System.out.println("从库中读取template大小:" + templateUnZip.length());
|
System.out.println("从库中读取template大小:" + templateUnZip.length());
|
||||||
// 校验解压内容是否正确
|
// 校验解压内容是否正确
|
||||||
assertEquals(JsonUtil.unzipString(templateUnZip),TEMPLATE);
|
assertEquals(TEMPLATE, JsonUtil.unzipString(templateUnZip));
|
||||||
// 删除
|
// 删除
|
||||||
reportTemplateService.deleteReportTemplate(templateId);
|
reportTemplateService.deleteReportTemplate(templateId);
|
||||||
}
|
}
|
||||||
@@ -120,8 +120,8 @@ public class ReportTemplateServiceImplTest extends ReportCommonServiceImplTest {
|
|||||||
List<ReportTemplateDO> reportTemplateList = reportTemplateService.getReportTemplateList(reqVO);
|
List<ReportTemplateDO> reportTemplateList = reportTemplateService.getReportTemplateList(reqVO);
|
||||||
|
|
||||||
assertNotNull(reportTemplateList);
|
assertNotNull(reportTemplateList);
|
||||||
assertNotEquals(reportTemplateList.size(),0);
|
assertNotEquals(0, reportTemplateList.size());
|
||||||
assertEquals(reportTemplateList.get(0).getOrganId(),2L);
|
assertEquals(2L, reportTemplateList.get(0).getOrganId());
|
||||||
|
|
||||||
// 删除
|
// 删除
|
||||||
reportTemplateService.deleteReportTemplate(templateId);
|
reportTemplateService.deleteReportTemplate(templateId);
|
||||||
|
|||||||
Reference in New Issue
Block a user