diff --git a/common/common-security/src/main/java/cn/fateverse/common/security/handle/AuthenticationEntryPointImpl.java b/common/common-security/src/main/java/cn/fateverse/common/security/handle/AuthenticationEntryPointImpl.java index 031db47..bf23331 100644 --- a/common/common-security/src/main/java/cn/fateverse/common/security/handle/AuthenticationEntryPointImpl.java +++ b/common/common-security/src/main/java/cn/fateverse/common/security/handle/AuthenticationEntryPointImpl.java @@ -1,5 +1,6 @@ package cn.fateverse.common.security.handle; +import cn.fateverse.common.security.utils.ResponseRender; import cn.hutool.core.text.StrFormatter; import cn.fateverse.common.core.result.Result; import com.alibaba.fastjson2.JSON; @@ -35,24 +36,9 @@ public class AuthenticationEntryPointImpl implements AccessDeniedHandler, Authen public void accessDenied(HttpServletRequest request, HttpServletResponse response) { String msg = StrFormatter.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI()); - renderString(response,Result.unauthorized(msg)); + ResponseRender.renderString(response,Result.unauthorized(msg)); } - /** - * 将字符串渲染到客户端 - * - * @param response 渲染对象 - * @param result 返回的错误对象 - */ - public static void renderString(HttpServletResponse response, Result result) { - try { - response.setStatus(result.getStatus().value()); - response.setContentType("application/json"); - response.setCharacterEncoding("utf-8"); - response.getWriter().print(JSON.toJSONString(result)); - } catch (IOException e) { - e.printStackTrace(); - } - } + } diff --git a/common/common-security/src/main/java/cn/fateverse/common/security/utils/ResponseRender.java b/common/common-security/src/main/java/cn/fateverse/common/security/utils/ResponseRender.java new file mode 100644 index 0000000..fbdc688 --- /dev/null +++ b/common/common-security/src/main/java/cn/fateverse/common/security/utils/ResponseRender.java @@ -0,0 +1,30 @@ +package cn.fateverse.common.security.utils; + +import cn.fateverse.common.core.result.Result; +import com.alibaba.fastjson2.JSON; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @author Clay + * @date 2024/4/19 9:09 + */ +public class ResponseRender { + /** + * 将字符串渲染到客户端 + * + * @param response 渲染对象 + * @param result 返回的错误对象 + */ + public static void renderString(HttpServletResponse response, Result result) { + try { + response.setStatus(result.getStatus().value()); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().print(JSON.toJSONString(result)); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/custom-query/custom-query-api/src/main/java/cn/fateverse/query/dubbo/DubboDispatchServletPublish.java b/custom-query/custom-query-api/src/main/java/cn/fateverse/query/dubbo/DubboDispatchServletPublish.java new file mode 100644 index 0000000..246ca62 --- /dev/null +++ b/custom-query/custom-query-api/src/main/java/cn/fateverse/query/dubbo/DubboDispatchServletPublish.java @@ -0,0 +1,17 @@ +package cn.fateverse.query.dubbo; + + +/** + * @author Clay + * @date 2024/4/19 10:56 + */ +public interface DubboDispatchServletPublish { + + + Boolean publish(String path, String requestMethod); + + + Boolean unpublish(String path, String requestMethod); + + +} diff --git a/custom-query/custom-query-api/src/main/java/cn/fateverse/query/package-info.java b/custom-query/custom-query-api/src/main/java/cn/fateverse/query/package-info.java new file mode 100644 index 0000000..f6acb9f --- /dev/null +++ b/custom-query/custom-query-api/src/main/java/cn/fateverse/query/package-info.java @@ -0,0 +1,5 @@ +/** + * @author Clay + * @date 2024/4/15 11:49 + */ +package cn.fateverse.query; \ No newline at end of file diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/controller/PortalController.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/controller/PortalController.java index 4c7f9c1..a3fab0e 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/controller/PortalController.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/controller/PortalController.java @@ -7,6 +7,7 @@ import cn.fateverse.common.decrypt.annotation.Encrypt; import cn.fateverse.common.decrypt.annotation.EncryptField; import cn.fateverse.query.entity.dto.PortalDto; import cn.fateverse.query.entity.query.PortalQuery; +import cn.fateverse.query.entity.vo.PortalIdWrapper; import cn.fateverse.query.entity.vo.PortalVo; import cn.fateverse.query.entity.vo.SimplePortalVo; import cn.fateverse.query.service.PortalService; @@ -58,18 +59,18 @@ public class PortalController { @ApiOperation("新增接口") @PostMapping @PreAuthorize("@ss.hasPermission('query:portal:add')") - public Result add(@RequestBody @Validated PortalDto portalDto) { - portalService.save(portalDto); - return Result.ok(); + public Result add(@RequestBody @Validated PortalDto portalDto) { + PortalIdWrapper wrapper = portalService.save(portalDto); + return Result.ok(wrapper); } @ApiOperation("修改接口") @PutMapping @PreAuthorize("@ss.hasPermission('query:portal:edit')") - public Result edit(@RequestBody @Validated PortalDto portalDto) { + public Result edit(@RequestBody @Validated PortalDto portalDto) { ObjectUtils.checkPk(portalDto.getPortalId()); - portalService.edit(portalDto); - return Result.ok(); + PortalIdWrapper wrapper = portalService.edit(portalDto); + return Result.ok(wrapper); } diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/controller/TestController.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/controller/TestController.java deleted file mode 100644 index 1e4a376..0000000 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/controller/TestController.java +++ /dev/null @@ -1,44 +0,0 @@ -package cn.fateverse.query.controller; - -import cn.fateverse.common.security.annotation.Anonymity; -import cn.fateverse.query.portal.config.HandlerMethodService; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; -import javax.servlet.http.HttpServletRequest; - -/** - * @author Clay - * @date 2024/4/12 14:18 - */ -@RestController -@RequestMapping("/test") -public class TestController { - - @Resource - private HandlerMethodService methodService; - - @Anonymity - @GetMapping("/1") - public String test1() { - methodService.registerMapping("/anonymity/mapping", RequestMethod.GET); - return "1"; - } - - @Anonymity - @GetMapping("/2") - public String test2() { - methodService.unregisterMapping("/anonymity/mapping", RequestMethod.GET); - return "2"; - } - - - public String mapping(HttpServletRequest request) { - return "mapping"; - } - - -} diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/dubbo/DubboDispatchServletPublishImpl.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/dubbo/DubboDispatchServletPublishImpl.java new file mode 100644 index 0000000..3eeb9d2 --- /dev/null +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/dubbo/DubboDispatchServletPublishImpl.java @@ -0,0 +1,55 @@ +package cn.fateverse.query.dubbo; + +import cn.fateverse.query.constant.QueryConstant; +import cn.fateverse.query.entity.bo.PortalBo; +import cn.fateverse.query.portal.service.HandlerMethodService; +import lombok.extern.slf4j.Slf4j; +import org.apache.dubbo.config.annotation.DubboService; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.RequestMethod; + +import javax.annotation.Resource; + +/** + * @author Clay + * @date 2024/4/19 11:02 + */ +@Slf4j +@DubboService(scope = "remote") +public class DubboDispatchServletPublishImpl implements DubboDispatchServletPublish { + + + @Resource + private HandlerMethodService methodService; + + + @Resource + private RedisTemplate redisTemplate; + + @Override + public Boolean publish(String path, String requestMethod) { + PortalBo portalBo = redisTemplate.opsForValue().get(QueryConstant.PORTAL_KEY + path + ":" + requestMethod); + if (portalBo == null) { + return Boolean.FALSE; + } + try { + log.info("registerMapping, path:{}, requestMethod:{}", path, requestMethod); + methodService.registerMapping(path, RequestMethod.valueOf(requestMethod), Boolean.FALSE); + } catch (Exception e) { + return Boolean.FALSE; + } + return Boolean.TRUE; + } + + + @Override + public Boolean unpublish(String path, String requestMethod) { + try { + log.info("unregisterMapping, path:{}, requestMethod:{}", path, requestMethod); + methodService.unregisterMapping(path, RequestMethod.valueOf(requestMethod), Boolean.FALSE); + } catch (Exception e) { + return Boolean.FALSE; + } + return Boolean.TRUE; + } +} diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/bo/PortalBo.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/bo/PortalBo.java new file mode 100644 index 0000000..b70e1b6 --- /dev/null +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/bo/PortalBo.java @@ -0,0 +1,50 @@ +package cn.fateverse.query.entity.bo; + +import cn.fateverse.query.entity.Portal; +import cn.fateverse.query.entity.PortalMapping; +import cn.fateverse.query.enums.PortalEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * @author Clay + * @date 2024/4/19 10:04 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PortalBo implements Serializable { + private Long portalId; + private Long queryId; + private Long adapterId; + private PortalEnum type; + private String requestMethod; + private String path; + private String url; + private Integer state; + private List mappings; + + public static PortalBo toPortalBo(Portal portal, List mappings) { + return PortalBo.builder() + .portalId(portal.getPortalId()) + .queryId(portal.getQueryId()) + .adapterId(portal.getAdapterId()) + .type(portal.getType()) + .requestMethod(portal.getRequestMethod()) + .path(portal.getPath()) + .url(portal.getUrl()) + .state(portal.getState()) + .mappings(mappings) + .build(); + } + + +} + + diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/dto/PortalDto.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/dto/PortalDto.java index a5dfc6e..f110fd6 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/dto/PortalDto.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/dto/PortalDto.java @@ -38,7 +38,7 @@ public class PortalDto { /** * 数据适配器id */ - @ApiModelProperty("数据适配器id 为-1 代表需要创建数据适配器") + @ApiModelProperty("数据适配器id") private Long adapterId; /** diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/vo/PortalIdWrapper.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/vo/PortalIdWrapper.java new file mode 100644 index 0000000..21df255 --- /dev/null +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/vo/PortalIdWrapper.java @@ -0,0 +1,20 @@ +package cn.fateverse.query.entity.vo; + +import cn.fateverse.common.decrypt.annotation.EncryptField; +import lombok.Builder; +import lombok.Data; + +/** + * @author Clay + * @date 2024/4/19 15:43 + */ +@Data +@Builder +public class PortalIdWrapper { + + @EncryptField + private String portalId; + @EncryptField + private String adapterId; + +} diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/vo/PortalVo.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/vo/PortalVo.java index c935693..f514e5c 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/vo/PortalVo.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/vo/PortalVo.java @@ -34,6 +34,7 @@ public class PortalVo extends SimplePortalVo { SimplePortalVo simplePortalVo = SimplePortalVo.toPortalVo(portal); PortalVo portalVo = new PortalVo(); BeanUtils.copyProperties(simplePortalVo, portalVo); + portalVo.setCreateDataAdapter(portal.getCreateDataAdapter()); if (!ObjectUtils.isEmpty(mappings)) { portalVo.setMappings(mappings); } diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/adapter/DataAdapterHandler.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/adapter/DataAdapterHandler.java index 60f312c..32d4638 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/adapter/DataAdapterHandler.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/adapter/DataAdapterHandler.java @@ -1,7 +1,7 @@ package cn.fateverse.query.handler.adapter; import cn.fateverse.query.entity.DataAdapter; -import cn.fateverse.query.entity.Portal; +import cn.fateverse.query.entity.bo.PortalBo; /** * @author Clay @@ -16,7 +16,7 @@ public interface DataAdapterHandler { * @param portal * @return 执行结果 */ - Object mockExecute(DataAdapter dataAdapter, Portal portal, Object param); + Object mockExecute(DataAdapter dataAdapter, PortalBo portal, Object param); /** * 真实执行 @@ -25,6 +25,6 @@ public interface DataAdapterHandler { * @param portal * @return 执行结果 */ - Object execute(DataAdapter dataAdapter, Portal portal, Object param); + Object execute(DataAdapter dataAdapter, PortalBo portal, Object param); } diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/adapter/impl/LocalDataAdapterHandler.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/adapter/impl/LocalDataAdapterHandler.java index d15502c..d5cd0c3 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/adapter/impl/LocalDataAdapterHandler.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/adapter/impl/LocalDataAdapterHandler.java @@ -3,8 +3,8 @@ package cn.fateverse.query.handler.adapter.impl; import cn.fateverse.common.core.exception.CustomException; import cn.fateverse.common.core.result.page.TableDataInfo; import cn.fateverse.query.entity.DataAdapter; -import cn.fateverse.query.entity.Portal; import cn.fateverse.query.entity.UniQuery; +import cn.fateverse.query.entity.bo.PortalBo; import cn.fateverse.query.entity.dto.SearchInfo; import cn.fateverse.query.enums.PortalEnum; import cn.fateverse.query.handler.adapter.DataAdapterHandler; @@ -44,7 +44,7 @@ public class LocalDataAdapterHandler implements DataAdapterHandler { @Override - public Object mockExecute(DataAdapter dataAdapter, Portal portal, Object param) { + public Object mockExecute(DataAdapter dataAdapter, PortalBo portal, Object param) { if (portal.getType() != PortalEnum.LOCAL) { return null; } @@ -67,7 +67,7 @@ public class LocalDataAdapterHandler implements DataAdapterHandler { @Override - public Object execute(DataAdapter dataAdapter, Portal portal, Object param) { + public Object execute(DataAdapter dataAdapter, PortalBo portal, Object param) { if (portal.getType() != PortalEnum.LOCAL) { return null; } diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/reader/DataAdapterHandlerReader.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/reader/DataAdapterHandlerReader.java index dbd3705..4975f8c 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/reader/DataAdapterHandlerReader.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/reader/DataAdapterHandlerReader.java @@ -3,7 +3,7 @@ package cn.fateverse.query.handler.reader; import cn.fateverse.common.core.exception.CustomException; import cn.fateverse.query.entity.DataAdapter; -import cn.fateverse.query.entity.Portal; +import cn.fateverse.query.entity.bo.PortalBo; import cn.fateverse.query.handler.adapter.DataAdapterHandler; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -28,9 +28,9 @@ public class DataAdapterHandlerReader { } - public Object mockExecute(DataAdapter dataAdapter, Portal portal, Object params) { + public Object mockExecute(DataAdapter dataAdapter, PortalBo portal, Object params) { for (DataAdapterHandler dataAdapterHandler : handlerList) { - Object result = dataAdapterHandler.mockExecute(dataAdapter,portal , params); + Object result = dataAdapterHandler.mockExecute(dataAdapter, portal, params); if (result != null) { return result; } @@ -39,7 +39,7 @@ public class DataAdapterHandlerReader { } - public Object execute(DataAdapter dataAdapter,Portal portal, Object params) { + public Object execute(DataAdapter dataAdapter, PortalBo portal, Object params) { for (DataAdapterHandler dataAdapterHandler : handlerList) { Object result = dataAdapterHandler.execute(dataAdapter, portal, params); if (result != null) { diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/mapper/PortalMappingMapper.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/mapper/PortalMappingMapper.java index a656b60..bbf72e2 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/mapper/PortalMappingMapper.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/mapper/PortalMappingMapper.java @@ -1,8 +1,10 @@ package cn.fateverse.query.mapper; import cn.fateverse.query.entity.PortalMapping; +import org.apache.ibatis.annotations.MapKey; import java.util.List; +import java.util.Map; /** * @author Clay @@ -35,4 +37,5 @@ public interface PortalMappingMapper { Integer deleteByPortalId(Long portalId); + List selectByPortalIds(List portalIds); } diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/PortalDispatchServlet.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/PortalDispatchServlet.java index ba021d8..73b30c3 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/PortalDispatchServlet.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/PortalDispatchServlet.java @@ -1,17 +1,23 @@ package cn.fateverse.query.portal; +import cn.fateverse.common.core.result.Result; +import cn.fateverse.common.security.utils.ResponseRender; +import cn.fateverse.query.constant.QueryConstant; import cn.fateverse.query.entity.DataAdapter; import cn.fateverse.query.entity.Portal; import cn.fateverse.query.entity.PortalMapping; +import cn.fateverse.query.entity.bo.PortalBo; import cn.fateverse.query.entity.dto.SearchInfo; import cn.fateverse.query.entity.dto.UniConDto; import cn.fateverse.query.handler.reader.DataAdapterHandlerReader; import cn.fateverse.query.mapper.DataAdapterMapper; import cn.fateverse.query.mapper.PortalMapper; import cn.fateverse.query.mapper.PortalMappingMapper; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; +import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; @@ -24,6 +30,9 @@ import java.util.List; @Component public class PortalDispatchServlet { + @Resource + private RedisTemplate redisTemplate; + private final PortalMapper portalMapper; private final DataAdapterMapper dataAdapterMapper; @@ -46,37 +55,46 @@ public class PortalDispatchServlet { private void doDispatch(HttpServletRequest request, HttpServletResponse response) { String path = request.getServletPath(); String method = request.getMethod(); - Portal portal = portalMapper.selectByPath(path, method); - if (portal == null) { - response.setStatus(HttpStatus.NOT_FOUND.value()); - return; + + PortalBo portalBo = redisTemplate.opsForValue().get(QueryConstant.PORTAL_KEY + path + ":" + method); + if (portalBo == null) { + Portal portal = portalMapper.selectByPath(path, method); + if (portal == null) { + response.setStatus(HttpStatus.NOT_FOUND.value()); + return; + } + List portalMappings = portalMappingMapper.selectByPortalId(portal.getPortalId()); + portalBo = PortalBo.toPortalBo(portal, portalMappings); + redisTemplate.opsForValue().set(QueryConstant.PORTAL_KEY + portalBo.getPath() + ":" + portalBo.getRequestMethod(), portalBo); } - List portalMappings = portalMappingMapper.selectByPortalId(portal.getPortalId()); + SearchInfo searchInfo = new SearchInfo(); List uniConList = new ArrayList<>(); - for (PortalMapping portalMapping : portalMappings) { + for (PortalMapping portalMapping : portalBo.getMappings()) { UniConDto uniCon = new UniConDto(); String mappingValue = portalMapping.getMappingValue(); + String mappingKey = portalMapping.getMappingKey(); uniCon.setUcId(Long.parseLong(mappingValue)); if (portalMapping.getMappingType() == 0) { - uniCon.setQuery(request.getParameter(mappingValue)); + uniCon.setQuery(request.getParameter(mappingKey)); } else if (portalMapping.getMappingType() == 1) { - uniCon.setQuery(request.getHeaders(mappingValue)); + uniCon.setQuery(request.getHeaders(mappingKey)); } else { - uniCon.setQuery(request.getParameter(mappingValue)); + uniCon.setQuery(request.getParameter(mappingKey)); } uniConList.add(uniCon); } searchInfo.setList(uniConList); - DataAdapter dataAdapter = dataAdapterMapper.selectById(portal.getAdapterId()); - - if (portal.getState() == 1 || portal.getState() == 2) { - Object execute = dataAdapterHandler.execute(dataAdapter, portal, searchInfo); + DataAdapter dataAdapter = dataAdapterMapper.selectById(portalBo.getAdapterId()); + Object result = null; + if (portalBo.getState() == 1 || portalBo.getState() == 2) { + result = dataAdapterHandler.execute(dataAdapter, portalBo, searchInfo); } else { - Object mockExecute = dataAdapterHandler.mockExecute(dataAdapter, portal, searchInfo); + result = dataAdapterHandler.mockExecute(dataAdapter, portalBo, searchInfo); } + ResponseRender.renderString(response, Result.ok(result)); } diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/config/DubboServiceBean.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/config/DubboServiceBean.java new file mode 100644 index 0000000..7b82f48 --- /dev/null +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/config/DubboServiceBean.java @@ -0,0 +1,31 @@ +package cn.fateverse.query.portal.config; + +import org.apache.dubbo.config.spring.ServiceBean; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * @author Clay + * @date 2024/4/19 17:24 + */ +@Component +public class DubboServiceBean { + + @Resource + private ServiceBean serviceBean; + + private volatile int port; + + + public int getPort() { + if (port == 0) { + synchronized (this) { + if (port == 0) { + port = serviceBean.getExportedUrls().get(0).getPort(); + } + } + } + return port; + } +} diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/event/DispatchSyncEvent.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/event/DispatchSyncEvent.java new file mode 100644 index 0000000..e5821e1 --- /dev/null +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/event/DispatchSyncEvent.java @@ -0,0 +1,23 @@ +package cn.fateverse.query.portal.event; + +import lombok.Getter; +import org.springframework.context.ApplicationEvent; + +/** + * @author Clay + * @date 2024/4/19 17:42 + */ +@Getter +public class DispatchSyncEvent extends ApplicationEvent { + private final String path; + private final Boolean publish; + private final String requestMethod; + + public DispatchSyncEvent(String path, String requestMethod, Boolean publish) { + super(path); + this.path = path; + this.publish = publish; + this.requestMethod = requestMethod; + } + +} diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/service/DispatchSyncService.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/service/DispatchSyncService.java new file mode 100644 index 0000000..badb8c2 --- /dev/null +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/service/DispatchSyncService.java @@ -0,0 +1,153 @@ +package cn.fateverse.query.portal.service; + +import cn.fateverse.query.dubbo.DubboDispatchServletPublish; +import cn.fateverse.query.portal.config.DubboServiceBean; +import cn.fateverse.query.portal.event.DispatchSyncEvent; +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.cloud.nacos.NacosServiceManager; +import com.alibaba.cloud.nacos.registry.NacosRegistration; +import com.alibaba.nacos.api.PropertyKeyConst; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.pojo.Instance; +import lombok.extern.slf4j.Slf4j; +import org.apache.dubbo.config.annotation.DubboReference; +import org.apache.dubbo.rpc.cluster.specifyaddress.Address; +import org.apache.dubbo.rpc.cluster.specifyaddress.UserSpecifiedAddressUtil; +import org.springframework.context.event.EventListener; +import org.springframework.core.env.Environment; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import java.util.List; +import java.util.Optional; +import java.util.Properties; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +/** + * @author Clay + * @date 2024/4/19 17:43 + */ +@Slf4j +@Component +public class DispatchSyncService { + + + private final String serviceName; + + private final String host; + + private final NamingService namingService; + + private final DubboServiceBean serviceBean; + + @DubboReference + private DubboDispatchServletPublish dubboDispatchServletPublish; + + + private final BlockingQueue queue = new LinkedBlockingQueue<>(64); + + + public DispatchSyncService(NacosDiscoveryProperties nacosDiscoveryProperties, Environment environment, NacosRegistration nacosRegistration, ThreadPoolTaskExecutor taskExecuteExecutor, DubboServiceBean serviceBean) { + this.host = nacosRegistration.getHost(); + this.serviceBean = serviceBean; + NacosServiceManager nacosServiceManager = new NacosServiceManager(); + Properties nacosProperties = nacosDiscoveryProperties.getNacosProperties(); + String namespace = environment.getProperty("dubbo.registry.parameters.namespace"); + this.serviceName = environment.getProperty("dubbo.application.name"); + nacosProperties.setProperty(PropertyKeyConst.NAMESPACE, namespace); + this.namingService = nacosServiceManager.getNamingService(nacosProperties); + + new Thread(() -> { + while (true) { + try { + Runnable take = queue.take(); + taskExecuteExecutor.execute(take); + } catch (InterruptedException e) { + log.info("", e); + } + } + }).start(); + } + + @EventListener(DispatchSyncEvent.class) + public void syncDispatch(DispatchSyncEvent event) { + syncDispatch(event.getPath(), event.getRequestMethod(), event.getPublish()); + } + + + private void syncDispatch(String path, String requestMethod, Boolean publish) { + try { + List allInstances = namingService.getAllInstances(serviceName); + for (Instance instance : allInstances) { + if (!(instance.getIp().equals(host) && instance.getPort() == serviceBean.getPort())) { + Task task = new Task(instance.getIp(), instance.getPort(), path, publish, requestMethod) { + @Override + public void run() { + try { + List allInstances = namingService.getAllInstances(serviceName); + if (ObjectUtils.isEmpty(allInstances)) { + return; + } + Optional optional = allInstances.stream().filter(instance -> this.ip.equals(instance.getIp()) && this.port == instance.getPort()).findAny(); + if (optional.isEmpty()) { + return; + } + Instance instance = optional.get(); + UserSpecifiedAddressUtil.setAddress(new Address(instance.getIp(), instance.getPort(), true)); + Boolean state = null; + if (publish) { + log.info("dubboDispatchServletPublish.publish({}, {})", path, requestMethod); + state = dubboDispatchServletPublish.publish(path, requestMethod); + } else { + log.info("dubboDispatchServletPublish.unpublish({}, {})", path, requestMethod); + state = dubboDispatchServletPublish.unpublish(path, requestMethod); + } + if (state == null || !state) { + queue.add(this); + } + } catch (NacosException e) { + log.error("NacosException: {}", e.getMessage()); + queue.add(this); + } finally { + UserSpecifiedAddressUtil.setAddress(null); + } + } + }; + queue.add(task); + } + } + } catch (NacosException e) { + throw new RuntimeException(e); + } + } + + + private static abstract class Task implements Runnable { + + protected final String ip; + + protected final int port; + + protected final String path; + + protected final Boolean publish; + + protected final String requestMethod; + + + private Task(String ip, int port, String path, Boolean publish, String requestMethod) { + this.ip = ip; + this.port = port; + this.path = path; + this.publish = publish; + this.requestMethod = requestMethod; + } + + + } + + +} diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/config/HandlerMethodService.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/service/HandlerMethodService.java similarity index 68% rename from custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/config/HandlerMethodService.java rename to custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/service/HandlerMethodService.java index 064264c..7c8fe2b 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/config/HandlerMethodService.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/service/HandlerMethodService.java @@ -1,11 +1,29 @@ -package cn.fateverse.query.portal.config; +package cn.fateverse.query.portal.service; import cn.fateverse.common.core.exception.CustomException; +import cn.fateverse.common.core.utils.SpringContextHolder; +import cn.fateverse.query.dubbo.DubboDispatchServletPublish; import cn.fateverse.query.portal.PortalDispatchServlet; +import cn.fateverse.query.portal.config.DubboServiceBean; +import cn.fateverse.query.portal.event.DispatchSyncEvent; +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.cloud.nacos.NacosServiceManager; +import com.alibaba.cloud.nacos.registry.NacosRegistration; +import com.alibaba.nacos.api.PropertyKeyConst; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.pojo.Instance; import lombok.extern.slf4j.Slf4j; +import org.apache.dubbo.config.annotation.DubboReference; +import org.apache.dubbo.rpc.RpcContext; +import org.apache.dubbo.rpc.RpcContextAttachment; +import org.apache.dubbo.rpc.cluster.specifyaddress.Address; +import org.apache.dubbo.rpc.cluster.specifyaddress.UserSpecifiedAddressUtil; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import org.springframework.core.env.Environment; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.RequestMethod; @@ -14,7 +32,12 @@ import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import java.lang.reflect.Method; +import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.Properties; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; /** * @author Clay @@ -24,9 +47,10 @@ import java.util.Map; @Component public class HandlerMethodService implements ApplicationContextAware { - public static final String CUSTOM_INTERFACE = "customInterface:"; + private RequestMappingHandlerMapping mapping; + private Method mappingMethod; @@ -47,7 +71,6 @@ public class HandlerMethodService implements ApplicationContextAware { @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { mapping = (RequestMappingHandlerMapping) applicationContext.getBean("requestMappingHandlerMapping"); - registerMapping("/anonymity/mapping",RequestMethod.GET); } /** @@ -56,7 +79,7 @@ public class HandlerMethodService implements ApplicationContextAware { * @param path 接口路径 * @param requestMethod 请求方法 */ - public void registerMapping(String path, RequestMethod requestMethod) { + public void registerMapping(String path, RequestMethod requestMethod, Boolean sync) { String[] empty = {}; RequestMappingInfo.Builder builder = RequestMappingInfo .paths(path) @@ -72,6 +95,9 @@ public class HandlerMethodService implements ApplicationContextAware { throw new CustomException("path is exist"); } mapping.registerMapping(requestMappingInfo, "portalDispatchServlet", mappingMethod); + if (sync) { + SpringContextHolder.publishEvent(new DispatchSyncEvent(path, requestMethod.name(), Boolean.TRUE)); + } } /** @@ -80,13 +106,16 @@ public class HandlerMethodService implements ApplicationContextAware { * @param path 接口路径 * @param requestMethod 请求类型 */ - public void unregisterMapping(String path, RequestMethod requestMethod) { + public void unregisterMapping(String path, RequestMethod requestMethod, Boolean sync) { Map handlerMethods = mapping.getHandlerMethods(); for (RequestMappingInfo mappingInfo : handlerMethods.keySet()) { if (!ObjectUtils.isEmpty(mappingInfo.getName()) && (CUSTOM_INTERFACE + path).equals(mappingInfo.getName()) && mappingInfo.getMethodsCondition().getMethods().contains(requestMethod)) { mapping.unregisterMapping(mappingInfo); + if (sync) { + SpringContextHolder.publishEvent(new DispatchSyncEvent(path, requestMethod.name(), Boolean.FALSE)); + } } } } diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/service/PortalService.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/service/PortalService.java index 1d92116..1b94603 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/service/PortalService.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/service/PortalService.java @@ -3,6 +3,7 @@ package cn.fateverse.query.service; import cn.fateverse.common.core.result.page.TableDataInfo; import cn.fateverse.query.entity.dto.PortalDto; import cn.fateverse.query.entity.query.PortalQuery; +import cn.fateverse.query.entity.vo.PortalIdWrapper; import cn.fateverse.query.entity.vo.PortalVo; import cn.fateverse.query.entity.vo.SimplePortalVo; @@ -23,7 +24,7 @@ public interface PortalService { TableDataInfo searchList(PortalQuery query); - void save(PortalDto portalDto); + PortalIdWrapper save(PortalDto portalDto); - void edit(PortalDto portalDto); + PortalIdWrapper edit(PortalDto portalDto); } diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/service/impl/PortalServiceImpl.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/service/impl/PortalServiceImpl.java index d389892..7793a50 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/service/impl/PortalServiceImpl.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/service/impl/PortalServiceImpl.java @@ -3,7 +3,9 @@ package cn.fateverse.query.service.impl; import cn.fateverse.common.core.exception.CustomException; import cn.fateverse.common.core.result.page.TableDataInfo; import cn.fateverse.common.mybatis.utils.PageUtils; -import cn.fateverse.query.portal.config.HandlerMethodService; +import cn.fateverse.query.entity.bo.PortalBo; +import cn.fateverse.query.entity.vo.PortalIdWrapper; +import cn.fateverse.query.portal.service.HandlerMethodService; import cn.fateverse.query.constant.QueryConstant; import cn.fateverse.query.entity.DataAdapter; import cn.fateverse.query.entity.Portal; @@ -22,16 +24,16 @@ import cn.fateverse.query.mapper.UniQueryMapper; import cn.fateverse.query.service.PortalService; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.RequestMethod; import javax.annotation.Resource; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; /** * @author Clay @@ -43,10 +45,9 @@ public class PortalServiceImpl implements PortalService { @Resource - private RedisTemplate redisTemplate; + private RedisTemplate redisTemplate; - @Resource - private HandlerMethodService methodService; + private final HandlerMethodService methodService; private final PortalMapper portalMapper; @@ -59,11 +60,44 @@ public class PortalServiceImpl implements PortalService { public PortalServiceImpl(PortalMapper portalMapper, UniQueryMapper queryMapper, DataAdapterMapper adapterMapper, - PortalMappingMapper portalMappingMapper) { + PortalMappingMapper portalMappingMapper, + ThreadPoolTaskExecutor taskExecuteExecutor, + HandlerMethodService methodService) { this.portalMapper = portalMapper; this.queryMapper = queryMapper; this.adapterMapper = adapterMapper; this.portalMappingMapper = portalMappingMapper; + this.methodService = methodService; + taskExecuteExecutor.execute(() -> { + List portalList = portalMapper.selectList(new PortalQuery()); + if (ObjectUtils.isEmpty(portalList)) { + log.info("portal is empty!"); + return; + } + List portalIds = portalList.stream().map(Portal::getPortalId).collect(Collectors.toList()); + if (ObjectUtils.isEmpty(portalIds)) { + log.info("portalIds is empty!"); + return; + } + List portalMappingList = portalMappingMapper.selectByPortalIds(portalIds); + Map> portalMappingMap = new HashMap<>(); + if (!ObjectUtils.isEmpty(portalMappingList)) { + portalMappingMap = portalMappingList.stream().collect(Collectors.groupingBy(PortalMapping::getPortalId)); + } + List emptyPortalMapping = new ArrayList<>(); + for (Portal portal : portalList) { + try { + publishPortal(portal, portalMappingMap.getOrDefault(portal.getPortalId(), emptyPortalMapping), Boolean.FALSE); + } catch (Exception e) { + if (e instanceof CustomException) { + log.error(e.getMessage()); + } else { + log.error("portalId:{} , portalName:{} publish error", portal.getPortalId(), portal.getPortalName(), e); + } + redisTemplate.delete(QueryConstant.PORTAL_KEY + portal.getPath() + ":" + portal.getRequestMethod()); + } + } + }); } @Override @@ -128,7 +162,7 @@ public class PortalServiceImpl implements PortalService { @Override @Transactional(rollbackFor = Exception.class) - public void save(PortalDto portalDto) { + public PortalIdWrapper save(PortalDto portalDto) { Portal portal = portalDto.toPortal(); checkPortalType(portal); Portal old = portalMapper.selectByPath(portal.getPath(), portal.getRequestMethod()); @@ -157,21 +191,22 @@ public class PortalServiceImpl implements PortalService { } portalMappingMapper.insertBatch(mappings); } - publishPortal(portal); + publishPortal(portal, mappings, Boolean.TRUE); + return PortalIdWrapper.builder() + .portalId(String.valueOf(portal.getPortalId())) + .adapterId(String.valueOf(portal.getAdapterId())) + .build(); } @Override - public void edit(PortalDto portalDto) { + public PortalIdWrapper edit(PortalDto portalDto) { Portal portal = portalDto.toPortal(); checkPortalType(portal); Portal old = portalMapper.selectByPath(portal.getPath(), portal.getRequestMethod()); if (!ObjectUtils.isEmpty(old) && !old.getPortalId().equals(portal.getPortalId())) { throw new CustomException("系统中存在当前请求路径"); } - if (!old.getPath().equals(portal.getPath())) { - unpublishPortal(old); - publishPortal(portal); - } + if (portal.getCreateDataAdapter() != old.getCreateDataAdapter()) { if (portal.getCreateDataAdapter()) { createDataAdapter(portalDto, portal); @@ -179,18 +214,57 @@ public class PortalServiceImpl implements PortalService { adapterMapper.deleteById(old.getAdapterId()); } } + PortalBo portalBo = PortalBo.toPortalBo(portal, portalDto.getMappings()); + if (!old.getPath().equals(portal.getPath()) + || !old.getRequestMethod().equals(portal.getRequestMethod())) { + unpublishPortal(old, true); + methodService.registerMapping(portalBo.getPath(), RequestMethod.valueOf(portalBo.getRequestMethod()), true); + } + redisTemplate.opsForValue().set(QueryConstant.PORTAL_KEY + portalBo.getPath() + ":" + portalBo.getRequestMethod(), portalBo); + return PortalIdWrapper.builder() + .portalId(String.valueOf(portal.getPortalId())) + .adapterId(String.valueOf(portal.getAdapterId())) + .build(); + + } + + private boolean mappingCheckEquals(List oldPortalMappingList, List mappings) { + if (ObjectUtils.isEmpty(oldPortalMappingList) && ObjectUtils.isEmpty(mappings)) { + return true; + } + if (ObjectUtils.isEmpty(oldPortalMappingList) || ObjectUtils.isEmpty(mappings)) { + return false; + } + Optional optional = mappings.stream().filter(ObjectUtils::isEmpty).findAny(); + if (optional.isEmpty()) { + return false; + } + if (oldPortalMappingList.size() == mappings.size()) { + Map oldPortalMappingMap = oldPortalMappingList.stream() + .collect(Collectors.toMap(PortalMapping::getMappingId, Function.identity())); + Map portalMappingMap = mappings.stream() + .collect(Collectors.toMap(PortalMapping::getMappingId, Function.identity())); + for (Long mappingId : oldPortalMappingMap.keySet()) { + PortalMapping portalMapping = portalMappingMap.get(mappingId); + if (ObjectUtils.isEmpty(portalMapping)) { + return false; + } + } + } + return false; } - private void publishPortal(Portal portal) { - methodService.registerMapping(portal.getPath(), RequestMethod.valueOf(portal.getRequestMethod())); - redisTemplate.opsForValue().set(QueryConstant.PORTAL_KEY + portal.getPortalId(), portal); + private void publishPortal(Portal portal, List mappings, Boolean sync) { + PortalBo portalBo = PortalBo.toPortalBo(portal, mappings); + redisTemplate.opsForValue().set(QueryConstant.PORTAL_KEY + portalBo.getPath() + ":" + portalBo.getRequestMethod(), portalBo); + methodService.registerMapping(portalBo.getPath(), RequestMethod.valueOf(portalBo.getRequestMethod()), sync); } - private void unpublishPortal(Portal portal) { - methodService.unregisterMapping(portal.getPath(), RequestMethod.valueOf(portal.getRequestMethod())); - redisTemplate.delete(QueryConstant.PORTAL_KEY + portal.getPortalId()); + private void unpublishPortal(Portal portal, Boolean sync) { + methodService.unregisterMapping(portal.getPath(), RequestMethod.valueOf(portal.getRequestMethod()), sync); + redisTemplate.delete(QueryConstant.PORTAL_KEY + portal.getPath() + ":" + portal.getRequestMethod()); } private void createDataAdapter(PortalDto portalDto, Portal portal) { diff --git a/custom-query/custom-query-biz/src/main/resources/mapper/PortalMappingMapper.xml b/custom-query/custom-query-biz/src/main/resources/mapper/PortalMappingMapper.xml index 32805ed..9f6d8ab 100644 --- a/custom-query/custom-query-biz/src/main/resources/mapper/PortalMappingMapper.xml +++ b/custom-query/custom-query-biz/src/main/resources/mapper/PortalMappingMapper.xml @@ -3,13 +3,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> - - + where portal_id = #{portalId} + + insert into portal_mapping (portal_id, mapping_key, mapping_type, mapping_value)