diff --git a/backend/src/main/java/com/stdproject/controller/ChartDataController.java b/backend/src/main/java/com/stdproject/controller/ChartDataController.java index 3720c0d..40b74a5 100644 --- a/backend/src/main/java/com/stdproject/controller/ChartDataController.java +++ b/backend/src/main/java/com/stdproject/controller/ChartDataController.java @@ -17,6 +17,7 @@ import jakarta.annotation.Resource; import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -34,7 +35,7 @@ public class ChartDataController { @Operation(summary = "获取图表数据") @OperationLog(type = "01", module = "数据可视化", description = "获取图表数据") @PostMapping("getData") - public ResponseResult getData(ChartViewDTO chartViewDTO) throws Exception { + public ResponseResult getData(@RequestBody ChartViewDTO chartViewDTO) throws Exception { try { // 从模板数据获取 DatasetUtils.viewDecode(chartViewDTO); diff --git a/backend/src/main/java/com/stdproject/controller/UserController.java b/backend/src/main/java/com/stdproject/controller/UserController.java index 6ebc503..7de06c5 100644 --- a/backend/src/main/java/com/stdproject/controller/UserController.java +++ b/backend/src/main/java/com/stdproject/controller/UserController.java @@ -8,11 +8,13 @@ import com.stdproject.config.WebConfig; import com.stdproject.entity.AppOptLog; import com.stdproject.entity.LoginUser; import com.stdproject.entity.User; +import com.stdproject.entity.vo.CurIpVO; import com.stdproject.service.IAppOptLogService; import com.stdproject.service.IUserService; import com.stdproject.utils.JwtUtils; import com.stdproject.utils.RsaUtils; import com.stdproject.utils.commonUtils; +import io.gisbi.utils.IPUtils; import io.micrometer.common.util.StringUtils; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; @@ -276,4 +278,13 @@ private Long jwtExpirationMs; sysLogService.save(sysLog); return ResponseResult.success(); } + + @GetMapping("/ipInfo") + public CurIpVO ipInfo() { + CurIpVO curIpVO = new CurIpVO(); + curIpVO.setAccount("admin"); + curIpVO.setName("管理员"); + curIpVO.setIp(IPUtils.get()); + return curIpVO; + } } diff --git a/backend/src/main/java/com/stdproject/controller/VisualizationLinkJumpController.java b/backend/src/main/java/com/stdproject/controller/VisualizationLinkJumpController.java new file mode 100644 index 0000000..53eeabd --- /dev/null +++ b/backend/src/main/java/com/stdproject/controller/VisualizationLinkJumpController.java @@ -0,0 +1,68 @@ +package com.stdproject.controller; + +import com.stdproject.common.OperationLog; +import com.stdproject.config.ResponseResult; +import com.stdproject.entity.dto.LinkageInfoDTO; +import com.stdproject.entity.dto.VisualizationLinkJumpDTO; +import com.stdproject.entity.dto.VisualizationLinkJumpInfoDTO; +import com.stdproject.mapper.ExtVisualizationLinkJumpMapper; +import com.stdproject.mapper.ExtVisualizationLinkageMapper; +import com.stdproject.response.VisualizationLinkJumpBaseResponse; +import io.gisbi.constant.CommonConstants; +import io.gisbi.utils.AuthUtils; +import io.gisbi.utils.ModelUtils; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.annotation.Resource; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.*; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/linkJump") +public class VisualizationLinkJumpController { + + @Resource + private ExtVisualizationLinkJumpMapper extVisualizationLinkJumpMapper; + + @Operation(summary = "根据可视化资源ID查询跳转信息") + @OperationLog(type = "01", module = "数据可视化", description = "根据可视化资源ID查询跳转信息") + @GetMapping("/queryVisualizationJumpInfo/{dvId}/{resourceTable}") + public ResponseResult queryVisualizationJumpInfo(@PathVariable Long dvId,@PathVariable String resourceTable) throws Exception { + Map resultBase = new HashMap<>(); + List resultLinkJumpList = null; +// String userid=com.stdproject.utils.AuthUtils.getUserId(); + String userid="1922852335260831746"; + if (CommonConstants.RESOURCE_TABLE.SNAPSHOT.equals(resourceTable)) { + resultLinkJumpList = extVisualizationLinkJumpMapper.queryWithDvIdSnapshot(dvId, Long.parseLong(userid), false); + } else { + resultLinkJumpList = extVisualizationLinkJumpMapper.queryWithDvId(dvId,Long.parseLong(userid), false); + } + Optional.ofNullable(resultLinkJumpList).orElse(new ArrayList<>()).forEach(resultLinkJump -> { + if (resultLinkJump.getChecked()) { + Long sourceViewId = resultLinkJump.getSourceViewId(); + Optional.ofNullable(resultLinkJump.getLinkJumpInfoArray()).orElse(new ArrayList<>()).forEach(linkJumpInfo -> { + if (linkJumpInfo.getChecked()) { + String sourceJumpInfo = sourceViewId + "#" + linkJumpInfo.getSourceFieldId(); + // 内部仪表板跳转 需要设置好仪表板ID + if ("inner".equals(linkJumpInfo.getLinkType())) { + if (linkJumpInfo.getTargetDvId() != null) { + resultBase.put(sourceJumpInfo, linkJumpInfo); + } + } else { + // 外部跳转 + resultBase.put(sourceJumpInfo, linkJumpInfo); + } + } + }); + } + }); + VisualizationLinkJumpBaseResponse result = new VisualizationLinkJumpBaseResponse(resultBase, null); + return ResponseResult.successData(result); + } + + +} \ No newline at end of file diff --git a/backend/src/main/java/com/stdproject/controller/VisualizationLinkageController.java b/backend/src/main/java/com/stdproject/controller/VisualizationLinkageController.java new file mode 100644 index 0000000..8b82b52 --- /dev/null +++ b/backend/src/main/java/com/stdproject/controller/VisualizationLinkageController.java @@ -0,0 +1,56 @@ +package com.stdproject.controller; + +import com.stdproject.common.OperationLog; +import com.stdproject.config.ResponseResult; +import com.stdproject.entity.dto.LinkageInfoDTO; +import com.stdproject.mapper.ExtVisualizationLinkageMapper; +import com.stdproject.service.manage.ChartDataManage; +import com.stdproject.utils.DatasetUtils; +import io.gisbi.constant.CommonConstants; +import io.gisbi.exception.DEException; +import io.gisbi.extensions.view.dto.ChartViewDTO; +import io.gisbi.result.ResultCode; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/linkage") +public class VisualizationLinkageController { + + @Resource + private ExtVisualizationLinkageMapper extVisualizationLinkageMapper; + + @Operation(summary = "根据资源ID查询联动信息") + @OperationLog(type = "01", module = "数据可视化", description = "根据资源ID查询联动信息") + @GetMapping("/getVisualizationAllLinkageInfo/{dvId}/{resourceTable}") + public ResponseResult getVisualizationAllLinkageInfo(@PathVariable Long dvId,@PathVariable String resourceTable) throws Exception { + List info = null; + if (CommonConstants.RESOURCE_TABLE.SNAPSHOT.equals(resourceTable)) { + info = extVisualizationLinkageMapper.getPanelAllLinkageInfoSnapshot(dvId); + }else{ + info = extVisualizationLinkageMapper.getPanelAllLinkageInfo(dvId); + } + info = info.stream().map(item -> { + item.setTargetInfoList(item.getTargetInfoList().stream().map(targetInfo -> { + if (targetInfo.contains(".")) { + String[] split = targetInfo.split("\\."); + if (split.length == 2) { + targetInfo = split[1]; + } + } + return targetInfo; + }).collect(Collectors.toList())); + return item; + }).collect(Collectors.toList()); + return ResponseResult.successData( info); + } + + +} \ No newline at end of file diff --git a/backend/src/main/java/com/stdproject/entity/vo/CurIpVO.java b/backend/src/main/java/com/stdproject/entity/vo/CurIpVO.java new file mode 100644 index 0000000..ac83fb4 --- /dev/null +++ b/backend/src/main/java/com/stdproject/entity/vo/CurIpVO.java @@ -0,0 +1,18 @@ +package com.stdproject.entity.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +@Data +public class CurIpVO implements Serializable { + @Serial + private static final long serialVersionUID = -3025566841330382707L; + + private String account; + + private String name; + + private String ip; +} diff --git a/backend/src/main/java/com/stdproject/mapper/ExtVisualizationLinkJumpMapper.java b/backend/src/main/java/com/stdproject/mapper/ExtVisualizationLinkJumpMapper.java index 1c28437..84a701c 100644 --- a/backend/src/main/java/com/stdproject/mapper/ExtVisualizationLinkJumpMapper.java +++ b/backend/src/main/java/com/stdproject/mapper/ExtVisualizationLinkJumpMapper.java @@ -17,41 +17,5 @@ public interface ExtVisualizationLinkJumpMapper { List queryWithDvIdSnapshot(@Param("dvId") Long dvId,@Param("uid") Long uid,@Param("isDesktop") Boolean isDesktop); - VisualizationLinkJumpDTO queryWithViewId(@Param("dvId") Long dvId,@Param("viewId") Long viewId,@Param("uid") Long uid,@Param("isDesktop") Boolean isDesktop); - void deleteJumpTargetViewInfoSnapshot(@Param("dvId") Long dvId,@Param("viewId") Long viewId); - - void deleteJumpInfoSnapshot(@Param("dvId") Long dvId,@Param("viewId") Long viewId); - - void deleteJumpSnapshot(@Param("dvId") Long dvId,@Param("viewId") Long viewId); - - void deleteJumpTargetViewInfoWithVisualization(@Param("dvId") Long dvId); - - void deleteJumpInfoWithVisualization(@Param("dvId") Long dvId); - - void deleteJumpWithVisualization(@Param("dvId") Long dvId); - - void deleteJumpTargetViewInfoWithVisualizationSnapshot(@Param("dvId") Long dvId); - - void deleteJumpInfoWithVisualizationSnapshot(@Param("dvId") Long dvId); - - void deleteJumpWithVisualizationSnapshot(@Param("dvId") Long dvId); - - List getTargetVisualizationJumpInfo(@Param("request") VisualizationLinkJumpBaseRequest request); - - List getTargetVisualizationJumpInfoSnapshot(@Param("request") VisualizationLinkJumpBaseRequest request); - - void copyLinkJump(@Param("copyId")Long copyId); - - void copyLinkJumpInfo(@Param("copyId")Long copyId); - - void copyLinkJumpTarget(@Param("copyId")Long copyId); - - List findLinkJumpWithDvId(@Param("dvId")Long dvId); - - List findLinkJumpInfoWithDvId(@Param("dvId")Long dvId); - - List getViewTableDetails(@Param("dvId")Long dvId); - - List queryOutParamsTargetWithDvId(@Param("dvId")Long dvId); } diff --git a/backend/src/main/java/com/stdproject/mapper/ExtVisualizationLinkageMapper.java b/backend/src/main/java/com/stdproject/mapper/ExtVisualizationLinkageMapper.java index 4d6e6f9..dbad192 100644 --- a/backend/src/main/java/com/stdproject/mapper/ExtVisualizationLinkageMapper.java +++ b/backend/src/main/java/com/stdproject/mapper/ExtVisualizationLinkageMapper.java @@ -13,32 +13,6 @@ import java.util.List; @Mapper public interface ExtVisualizationLinkageMapper { - - List getViewLinkageGather(@Param("dvId") Long dvId, @Param("sourceViewId") Long sourceViewId, @Param("targetViewIds") List targetViewIds); - - List getPanelAllLinkageInfo(@Param("dvId") Long dvId); - - List getViewLinkageGatherSnapshot(@Param("dvId") Long dvId, @Param("sourceViewId") Long sourceViewId, @Param("targetViewIds") List targetViewIds); - - List getPanelAllLinkageInfoSnapshot(@Param("dvId") Long dvId); - - List queryTableField(@Param("table_id") Long tableId); - - List queryTableFieldWithViewId(@Param("viewId") Long viewId); - - void deleteViewLinkage(@Param("dvId") Long dvId,@Param("sourceViewId") Long sourceViewId); - - void deleteViewLinkageField(@Param("dvId") Long dvId,@Param("sourceViewId") Long sourceViewId); - - void deleteViewLinkageSnapshot(@Param("dvId") Long dvId,@Param("sourceViewId") Long sourceViewId); - - void deleteViewLinkageFieldSnapshot(@Param("dvId") Long dvId,@Param("sourceViewId") Long sourceViewId); - - void copyViewLinkage(@Param("copyId") Long copyId); - - void copyViewLinkageField(@Param("copyId") Long copyId); - - List findLinkageWithDvId(@Param("dvId") Long dvId); - - List findLinkageFieldWithDvId(@Param("dvId") Long dvId); + List getPanelAllLinkageInfo(@Param("dvId") Long dvId); + List getPanelAllLinkageInfoSnapshot(@Param("dvId") Long dvId); } diff --git a/backend/src/main/java/com/stdproject/service/manage/ChartViewManege.java b/backend/src/main/java/com/stdproject/service/manage/ChartViewManege.java index 041cc01..9684f07 100644 --- a/backend/src/main/java/com/stdproject/service/manage/ChartViewManege.java +++ b/backend/src/main/java/com/stdproject/service/manage/ChartViewManege.java @@ -251,9 +251,7 @@ public class ChartViewManege { ChartFieldCompareDTO chartFieldCompareDTO = new ChartFieldCompareDTO(); chartFieldCompareDTO.setType("none"); dto.setCompareCalc(chartFieldCompareDTO); - - dto.setFormatterCfg(new FormatterCfgDTO().setUnitLanguage(Lang.isChinese() ? "ch" : "en")); - + dto.setFormatterCfg(new FormatterCfgDTO()); dto.setSort("none"); dto.setFilter(Collections.emptyList()); return dto; diff --git a/backend/src/main/java/com/stdproject/service/manage/DatasetSQLManage.java b/backend/src/main/java/com/stdproject/service/manage/DatasetSQLManage.java index e86de99..6c54fd5 100644 --- a/backend/src/main/java/com/stdproject/service/manage/DatasetSQLManage.java +++ b/backend/src/main/java/com/stdproject/service/manage/DatasetSQLManage.java @@ -8,10 +8,7 @@ import com.stdproject.entity.CoreDatasource; import com.stdproject.entity.union.*; import com.stdproject.mapper.CoreDatasetGroupMapper; import com.stdproject.mapper.CoreDatasourceMapper; -import com.stdproject.utils.DatasetTableTypeConstants; -import com.stdproject.utils.SqlUtils; -import com.stdproject.utils.SqlparserUtils; -import com.stdproject.utils.TableUtils; +import com.stdproject.utils.*; import io.gisbi.constant.SQLConstants; import io.gisbi.exception.DEException; import io.gisbi.extensions.datasource.api.PluginManageApi; @@ -480,8 +477,8 @@ public class DatasetSQLManage { if (coreDatasource.getType().contains(DatasourceConfiguration.DatasourceType.Excel.name()) || coreDatasource.getType().contains(DatasourceConfiguration.DatasourceType.API.name())) { coreDatasource = engineManage.getDeEngine(); } - - Map map = JsonUtil.parseObject(coreDatasource.getConfiguration(), Map.class); + String config= String.valueOf(EncryptUtils.aesDecrypt(coreDatasource.getConfiguration())); + Map map=JsonUtil.parseObject(config, Map.class); if (!isCross && ObjectUtils.isNotEmpty(map.get("schema"))) { schemaAlias = (String) map.get("schema"); } else { diff --git a/backend/src/main/java/com/stdproject/service/provider/CalciteProvider.java b/backend/src/main/java/com/stdproject/service/provider/CalciteProvider.java index af0eb1c..9136aa2 100644 --- a/backend/src/main/java/com/stdproject/service/provider/CalciteProvider.java +++ b/backend/src/main/java/com/stdproject/service/provider/CalciteProvider.java @@ -478,8 +478,8 @@ public class CalciteProvider extends Provider { public Map jdbcFetchResultField(DatasourceRequest datasourceRequest) throws BusinessException { DatasourceSchemaDTO value = datasourceRequest.getDsList().entrySet().iterator().next().getValue(); datasourceRequest.setDatasource(value); - - DatasourceConfiguration datasourceConfiguration = JsonUtil.parseObject(datasourceRequest.getDatasource().getConfiguration(), DatasourceConfiguration.class); + String config=String.valueOf(EncryptUtils.aesDecrypt(datasourceRequest.getDatasource().getConfiguration())); + DatasourceConfiguration datasourceConfiguration = JsonUtil.parseObject(config, DatasourceConfiguration.class); Map map = new LinkedHashMap<>(); List fieldList = new ArrayList<>(); diff --git a/backend/src/main/java/com/stdproject/utils/RsaUtils.java b/backend/src/main/java/com/stdproject/utils/RsaUtils.java index 805d29f..00c3e5c 100644 --- a/backend/src/main/java/com/stdproject/utils/RsaUtils.java +++ b/backend/src/main/java/com/stdproject/utils/RsaUtils.java @@ -1,8 +1,14 @@ package com.stdproject.utils; import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang3.StringUtils; import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; import java.security.*; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; @@ -15,8 +21,11 @@ import java.security.spec.X509EncodedKeySpec; * @date 2020-05-18 **/ public class RsaUtils { - + public static final String IV_KEY = "0000000000000000"; private static final String SRC = "123456"; + private static final String ALGORITHM = "AES"; + public static String symmetricKey = null; + private static final int KEY_SIZE = 128; public static void main(String[] args) throws Exception { System.out.println("\n"); @@ -30,6 +39,7 @@ public class RsaUtils { System.out.println("\n"); + } /** @@ -69,6 +79,8 @@ public class RsaUtils { System.out.println("***************** 私钥加密公钥解密结束 *****************"); } + + /** * 公钥解密 * @@ -156,8 +168,61 @@ public class RsaUtils { String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded()); return new RsaKeyPair(publicKeyString, privateKeyString); } + /** + * 生成AES对称加密密钥 + * @return Base64编码的密钥字符串 + */ + public static String generateSymmetricKey() { + try { + if (StringUtils.isEmpty(symmetricKey)) { + KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM); + keyGenerator.init(KEY_SIZE, new SecureRandom()); + SecretKey secretKey = keyGenerator.generateKey(); + symmetricKey = java.util.Base64.getEncoder().encodeToString(secretKey.getEncoded()); + } + return symmetricKey; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + /** + * 使用AES对称密钥进行加密 + * @param data 待加密的数据 + * @return 加密后的Base64编码字符串 + */ + public static String symmetricEncrypt(String data) { + try { + byte[] iv = IV_KEY.getBytes(StandardCharsets.UTF_8); + IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + SecretKeySpec secretKeySpec = new SecretKeySpec(java.util.Base64.getDecoder().decode(generateSymmetricKey()), ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec); + byte[] ciphertext = cipher.doFinal(data.getBytes("UTF-8")); + return java.util.Base64.getEncoder().encodeToString(ciphertext); + } catch (Exception e) { + throw new RuntimeException(e); + } + } - + /** + * 使用AES对称密钥进行解密 + * @param data 待解密的Base64编码字符串 + * @return 解密后的原始数据 + */ + public static String symmetricDecrypt(String data) { + try { + byte[] iv = IV_KEY.getBytes(StandardCharsets.UTF_8); + IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); + SecretKeySpec secretKeySpec = new SecretKeySpec(java.util.Base64.getDecoder().decode(generateSymmetricKey()), ALGORITHM); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); + byte[] decodedCiphertext = java.util.Base64.getDecoder().decode(data); + byte[] decryptedText = cipher.doFinal(decodedCiphertext); + return new String(decryptedText, "UTF-8"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } /** * RSA密钥对对象 */ diff --git a/backend/src/main/java/com/stdproject/utils/Utils.java b/backend/src/main/java/com/stdproject/utils/Utils.java index 65ad66a..d3e505d 100644 --- a/backend/src/main/java/com/stdproject/utils/Utils.java +++ b/backend/src/main/java/com/stdproject/utils/Utils.java @@ -279,7 +279,8 @@ public class Utils { public static String replaceSchemaAlias(String sql, Map dsMap) { DatasourceSchemaDTO value = dsMap.entrySet().iterator().next().getValue(); - Map map = JsonUtil.parseObject(value.getConfiguration(), Map.class); + String config= String.valueOf(EncryptUtils.aesDecrypt(value.getConfiguration())); + Map map = JsonUtil.parseObject(config, Map.class); if (ObjectUtils.isNotEmpty(map.get("schema"))) { return sql; } diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index 11060a5..168beca 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -19,9 +19,9 @@ spring: main: allow-bean-definition-overriding: true datasource: - url: ${DB_URL:jdbc:mysql://121.37.111.42:3306/gisbi-demodb?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true} + url: ${DB_URL:jdbc:mysql://192.168.1.58:3306/gisbi-demodb?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true} username: ${DB_USERNAME:root} - password: ${DB_PASSWORD:mysql_F8ysiK@2024} + password: ${DB_PASSWORD:123456} driver-class-name: com.mysql.cj.jdbc.Driver # HikariCP连接池配置 hikari: diff --git a/backend/src/main/resources/mybatis/ExtDataVisualizationMapper.xml b/backend/src/main/resources/mybatis/ExtDataVisualizationMapper.xml index 7bfd40b..b9a300a 100644 --- a/backend/src/main/resources/mybatis/ExtDataVisualizationMapper.xml +++ b/backend/src/main/resources/mybatis/ExtDataVisualizationMapper.xml @@ -124,7 +124,7 @@ - + diff --git a/backend/src/main/resources/mybatis/ExtVisualizationLinkJumpMapper.xml b/backend/src/main/resources/mybatis/ExtVisualizationLinkJumpMapper.xml new file mode 100644 index 0000000..458b11b --- /dev/null +++ b/backend/src/main/resources/mybatis/ExtVisualizationLinkJumpMapper.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/src/main/resources/mybatis/ExtVisualizationLinkageMapper.xml b/backend/src/main/resources/mybatis/ExtVisualizationLinkageMapper.xml new file mode 100644 index 0000000..5397083 --- /dev/null +++ b/backend/src/main/resources/mybatis/ExtVisualizationLinkageMapper.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + +