集成设计器数据流程、流程设计器可以暴露接口触发

集成设计器数据流程暴露接口触发

需求:在集成设计器配置的连接器、数据流程链接到外部接口,需要可以有一个管理页面,统一管理这些集成配置。比如对接多个医院的挂号系统,希望可以配置好数据流程之后,能够在已经实现的开放接口上,动态的调用集成平台配置的数据流程。

连接器暴露接口触发

  1. 设计连接器资源配置模型。使用业务ID+接口唯一标识+连接器实现连接器资源和业务唯一。

    @Model.model(EipConnectorResourceSetting.MODEL_MODEL)
    @Model(displayName = "连接器资源配置", summary = "连接器资源配置")
    @Model.Advanced(unique = {"hospitalId, interfaceUnique"})
    public class EipConnectorResourceSetting extends IdModel {
    
    public static final String MODEL_MODEL = "hr.simple.EipConnectorResourceSetting";
    
    @UxTableSearch.FieldWidget(@UxWidget())
    @Field(displayName = "医院", required = true)
    @Field.Relation(relationFields = {"hospitalId"}, referenceFields = {"id"})
    private Hospital hospital;
    
    @Field.Integer
    @Field(displayName = "医院ID", invisible = true)
    private Long hospitalId;
    
    @UxTableSearch.FieldWidget(@UxWidget())
    @Field.String
    @Field(displayName = "接口唯一标识", required = true)
    private String interfaceUnique;
    
    @UxTableSearch.FieldWidget(@UxWidget())
    @Field.many2one
    @Field(displayName = "连接器", required = true)
    @Field.Relation(relationFields = {"connectorId"}, referenceFields = {"id"})
    private EipConnector connector;
    
    @Field.Integer
    @Field(displayName = "连接器ID", invisible = true)
    private Long connectorId;
    
    @UxTableSearch.FieldWidget(@UxWidget())
    @Field.many2one
    @Field(displayName = "连接器接口", required = true)
    @Field.Relation(relationFields = {"integrationInterfaceId"}, referenceFields = {"id"}, domain = "connectorId==${activeRecord.connector.id}")
    private EipConnectorResource connectorResource;
    
    @Field.Integer
    @Field(displayName = "连接器接口ID", invisible = true)
    private Long connectorResourceId;
    }
  2. 开放接口定义,文档参考:https://doc.oinone.top/oio4/9326.html
    外部平台调用开放接口,实现动态调用连接器资源

    @Model.model(HospitalSection.MODEL_MODEL)
    @Model(displayName = "医院科室", summary = "医院科室")
    public class HospitalSection extends IdModel {
    
    public static final String MODEL_MODEL = "net.example.HospitalSection";
    
    @Field(displayName = "科室Code", required = true)
    private String deptCode;
    
    @Field(displayName = "科室名称", required = true)
    private String deptName;
    
    @Field(displayName = "科室描述")
    private String deptDesc;
    }
    @Slf4j
    @Fun
    @Component
    public class TestOpenService {
    
    @Resource
    private TestCommonService testCommonService;
    
    @Function
    @Open(name = "获取科室", path = "test/gainSections")
    @Open.Advanced(httpMethod = "POST")
    public OpenEipResult<List<HospitalSection>> gainSections(IEipContext<SuperMap> context, ExtendedExchange exchange) {
        return new OpenEipResult<>(testCommonService.gainSections(context));
    }
    }
    @Slf4j
    @Component
    public class TestCommonService {
    
    @Resource
    private CustomWorkflowService customWorkflowService;
    
    /**
     * 获取科室
     *
     * @param context 请求上下文
     * @return 科室集合
     */
    public List<HospitalSection> gainSections(IEipContext<SuperMap> context) {
        EipResult<SuperMap> gainSections = executeEipConnector(context, "gainSections");
        if (gainSections.getSuccess()) {
            if (gainSections.getContext().getInterfaceContext().get("DepartmentList")!=null) {
                return JSON.parseArray(JSON.toJSONString(gainSections.getContext().getInterfaceContext().get("DepartmentList")),
                        HospitalSection.class);
    
            }
        }
        String errorCode = gainSections.getErrorCode();
        String errorMessage = gainSections.getErrorMessage();
        // Object result = gainSections.getResult();
        return new ArrayList<>();
    }
        /**
     * 根据请求参数中医院id和参数key-value、接口标识获取连接器
     *
     * @param context         请求上下文
     * @param interfaceUnique 接口标识
     * @return 连接器
     */
    private EipResult<SuperMap> executeEipConnector(IEipContext<SuperMap> context, String interfaceUnique) {
        String hospitalId = Optional.ofNullable(String.valueOf(context.getInterfaceContext().getIteration("hospitalId"))).orElse("");
        String parameterValue = Optional.ofNullable(String.valueOf(context.getInterfaceContext().getIteration("parameterValue"))).orElse("");
        //step1. 获取连接器模型
        EipConnectorResourceSetting interfaceSetting = new EipConnectorResourceSetting()
                .setHospitalId(Long.valueOf(hospitalId)).setInterfaceUnique(interfaceUnique).queryOne();
        if (interfaceSetting == null) {
            throw PamirsException.construct(ExpEnumerate.SYSTEM_ERROR).appendMsg("连接器模型未找到").errThrow();
        }
        //step2. 获取连接器接口
        interfaceSetting.fieldQuery(EipConnectorResourceSetting::getConnectorResource);
        EipConnectorResource connectorResource = interfaceSetting.getConnectorResource();
        if (connectorResource == null) {
            throw PamirsException.construct(ExpEnumerate.SYSTEM_ERROR).appendMsg("连接器接口未找到").errThrow();
        }
        //3. 执行连接器获取结果
        Map<String, Object> params = new HashMap<>();
        params.put("wxValue1", parameterValue);
        return EipExecutor.newInstance().call(connectorResource.getInterfaceName(), params);
    }

数据流程暴露接口触发

  1. 设计数据流程配置模型。使用业务ID+接口唯一标识+连接器实现数据流程配置和业务唯一。

    @Model.model(EipOpenDataflowSetting.MODEL_MODEL)
    @Model(displayName = "数据流程配置", summary = "数据流程配置")
    @Model.Advanced(unique = {"hospitalId,interfaceUnique"})
    public class EipOpenDataflowSetting extends IdModel {
    
    public static final String MODEL_MODEL = "hr.simple.EipOpenDataflowSetting";
    
    @UxTableSearch.FieldWidget(@UxWidget())
    @Field(displayName = "医院", required = true)
    @Field.Relation(relationFields = {"hospitalId"}, referenceFields = {"id"})
    private Hospital hospital;
    
    @Field.Integer
    @Field(displayName = "医院ID", invisible = true)
    private Long hospitalId;
    
    @UxTableSearch.FieldWidget(@UxWidget())
    @Field.String
    @Field(displayName = "接口唯一标识", required = true)
    private String interfaceUnique;
    
    @UxTableSearch.FieldWidget(@UxWidget())
    @Field.many2one
    @Field(displayName = "数据流程", required = true)
    @Field.Relation(relationFields = {"dataflowId"}, referenceFields = {"id"})
    private DataflowDesigner dataflow;
    
    @Field.Integer
    @Field(displayName = "数据流程编码", invisible = true)
    private Long dataflowId;
    
    @Field.String
    @Field.Related(related = {"dataflow", "code"})
    @UxForm.FieldWidget(@UxWidget(readonly = "true"))
    @Field(displayName = "数据流程编码", store = NullableBoolEnum.TRUE)
    private String dataflowCode;
    }
  2. 开放接口定义。

    @Fun(RegisterOpenService.FUN_NAMESPACE)
    public interface RegisterOpenService {
    
    String FUN_NAMESPACE = "net.example.RegisterService";
    
    @Function
    RegisterOrder queryById(Long hospitalId, Long memberId);
    }
    @Slf4j
    @Fun(RegisterOpenService.FUN_NAMESPACE)
    @Component
    public class RegisterOpenServiceImpl implements RegisterOpenService {
    
    @Autowired
    private CustomWorkflowService customWorkflowService;
    
    /**
     * 开放接口:把自己的开放出去供外部调用(如:小程序调用)
     *
     * 文档参考:https://doc.oinone.top/oio4/9326.html
     *
     * 1、接口访问URL地址:http://127.0.0.1:8195/openapi/pamirs/register/queryById 端口根据yml配置进行修改
     * 2、参数默认是map格式,构造为IEipContext对象
     */
    @Function
    @Open(name = "根据ID查询挂号流水", path = "register/queryById")
    @Open.Advanced(
            httpMethod = "GET"
    )
    public OpenEipResult<RegisterOrder> queryById4Open(IEipContext<SuperMap> context, ExtendedExchange exchange) {
        String memberId = Optional.ofNullable(String.valueOf(context.getInterfaceContext().getIteration("id"))).orElse("0");
        String hospitalId = Optional.ofNullable(String.valueOf(context.getInterfaceContext().getIteration("hospitalId"))).orElse("0");
        // 开发接口的内部具体实现
        RegisterOrder register = queryById(Long.valueOf(hospitalId), Long.valueOf(memberId));
        if (register!=null) {
            return new OpenEipResult<>(register);
        } else {
            throw PamirsException.construct(EipExpEnumerate.SYSTEM_ERROR).appendMsg("根据ID "+ memberId +"查询挂号流水").errThrow();
        }
    }
    
    @Override
    @Function
    public RegisterOrder queryById(Long hospitalId, Long memberId) {
        // step1、业务前置逻辑、参数校验等
        // step2、构造请求数据、或者通过BD获取数据(这里先进行Mock)
        RegisterOrder register = new RegisterOrder();
        register.setHospitalId("719331903102009889");
        register.setSectionId("1");
        register.setDoctorId("1");
        register.setSourceId("1");
        register.setPatienterName("zhangsan");
        register.setPatienterIdCard("123444");
        // 配置表,医院 + 接口功能 == >> 流程编码。   ****** 实际项目这些基础数据需要缓存,如果Caffeine/Redis*********
        EipOpenDataflowSetting dataflowSetting = new EipOpenDataflowSetting().setHospitalId(hospitalId).setInterfaceUnique("theWeather").queryOne();
        if (dataflowSetting !=  null) {
            // step3、调用数据流程
            WorkflowDefinition workflowDefinition = customWorkflowService.queryWorkflowDefinition(dataflowSetting.getDataflowCode());
            if (workflowDefinition != null) {
                // 分支1:调用数据流程
                Map<String, Object> result = customWorkflowService.startWorkflow(workflowDefinition, register);
                log.info("调用数据流程返回:" + result);
    
                // 根据返回处理业务逻辑
            }
        } else {
            // 分支2:异常处理,或者没有数据流程实现执行默认逻辑
        }
    
        // Step4 业务后置逻辑,数据操作、其他service的调用之类
        return register;
    }
    }
    @Slf4j
    @Component
    public class CustomWorkflowService {
    
    public WorkflowDefinition queryWorkflowDefinition(String workflowCode) {
        return new WorkflowDefinition().queryOneByWrapper(
                Pops.<WorkflowDefinition>lambdaQuery()
                        .from(WorkflowDefinition.MODEL_MODEL)
                        .eq(WorkflowDefinition::getWorkflowCode, workflowCode)
                        .eq(WorkflowDefinition::getActive, 1)
        );
    }
    
    /**
     * 触发⼯作流实例
     */
    public Map<String, Object> startWorkflow(WorkflowDefinition workflowDefinition, IdModel modelData) {
        Map<String, Object> result = new HashMap<>();
        if (null == workflowDefinition) {
            // 流程没有运⾏实例
            return result;
        }
        String model = Models.api().getModel(modelData);
        //⼯作流上下⽂
        WorkflowDataContext wdc = new WorkflowDataContext();
        wdc.setDataType(WorkflowVariationTypeEnum.ADD);
        wdc.setModel(model);
        wdc.setWorkflowDefinitionDefinition(workflowDefinition.parseContent());
        wdc.setWorkflowDefinition(workflowDefinition);
        wdc.setWorkflowDefinitionId(workflowDefinition.getId());
        IdModel copyData = KryoUtils.get().copy(modelData);
        // ⼿动触发创建的动作流,将操作⼈设置为当前用户,作为流程的发起⼈
        copyData.setCreateUid(PamirsSession.getUserId());
        copyData.setWriteUid(PamirsSession.getUserId());
        String jsonData = JsonUtils.toJSONString(copyData.get_d());
        //触发工作流 新增时触发-onCreateManual 更新时触发-onUpdateManual
        Fun.run(WorkflowModelTriggerFunction.FUN_NAMESPACE, "onCreateManualSync", wdc, "1", jsonData);
    
        // 处理返回结果
        Long workflowInstanceId = (Long) wdc.getDataMap().get(WorkflowConstant.WORKFLOW_INSTANCE_ID);
        if (workflowInstanceId !=null) {
            WorkflowInstance workflowInstance = new WorkflowInstance().setId(workflowInstanceId).queryById();
            WorkflowContext workflowContext = workflowInstance.fetchWorkflowContext();
            result = (HashMap)workflowContext.get(ParamNode.PARAM_PREFIX);
        }
    
        return result;
    }
    
    public Map<String,Object> startCreatOrderWorkflow(WorkflowDefinition workflowDefinition, IdModel modelData){
        Map<String, Object> result = new HashMap<>();
        if (null == workflowDefinition) {
            // 流程没有运⾏实例
            return result;
        }
        String model = Models.api().getModel(modelData);
        //⼯作流上下⽂
        WorkflowDataContext wdc = new WorkflowDataContext();
        wdc.setDataType(WorkflowVariationTypeEnum.ADD);
        wdc.setModel(model);
        wdc.setWorkflowDefinitionDefinition(workflowDefinition.parseContent());
        wdc.setWorkflowDefinition(workflowDefinition);
        wdc.setWorkflowDefinitionId(workflowDefinition.getId());
        IdModel copyData = KryoUtils.get().copy(modelData);
        // ⼿动触发创建的动作流,将操作⼈设置为当前用户,作为流程的发起⼈
        copyData.setCreateUid(PamirsSession.getUserId());
        copyData.setWriteUid(PamirsSession.getUserId());
        String jsonData = JsonUtils.toJSONString(copyData.get_d());
        //触发⼯作流 新增时触发-onCreateManual 更新时触发-onUpdateManual
        Fun.run(WorkflowModelTriggerFunction.FUN_NAMESPACE, "onCreateManualSync", wdc, "1", jsonData);
    
        // 处理返回结果
        Long workflowInstanceId = (Long) wdc.getDataMap().get(WorkflowConstant.WORKFLOW_INSTANCE_ID);
        if (workflowInstanceId !=null) {
            WorkflowInstance workflowInstance = new WorkflowInstance().setId(workflowInstanceId).queryById();
            WorkflowContext workflowContext = workflowInstance.fetchWorkflowContext();
            result = (HashMap)workflowContext.get(ParamNode.PARAM_PREFIX);
        }
    
        return result;
    }
    }

Oinone社区 作者:yexiu原创文章,如若转载,请注明出处:https://doc.oinone.top/dai-ma-shi-jian/20972.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

Like (0)
yexiu's avataryexiu数式员工
Previous 2025年4月17日 pm8:52
Next 2025年4月21日 pm3:53

相关推荐

  • IWrapper、QueryWrapper和LambdaQueryWrapper使用

    条件更新updateByWrapper 通常我们在更新的时候new一个对象出来在去更新,减少更新的字段 Integer update = new DemoUser().updateByWrapper(new DemoUser().setFirstLogin(Boolean.FALSE), Pops.<DemoUser>lambdaUpdate().from(DemoUser.MODEL_MODEL).eq(IdModel::getId, userId) 使用基础模型的updateById方法更新指定字段的方法: new 一下update对象出来,更新这个对象。 WorkflowUserTask userTaskUp = new WorkflowUserTask(); userTaskUp.setId(userTask.getId()); userTaskUp.setNodeContext(json); userTaskUp.updateById(); 条件删除updateByWrapper public List<T> delete(List<T> data) { List<Long> petTypeIdList = new ArrayList(); for(T item:data){ petTypeIdList.add(item.getId()); } Models.data().deleteByWrapper(Pops.<PetType>lambdaQuery().from(PetType.MODEL_MODEL).in(PetType::getId,petTypeIdList)); return data; } 构造条件查询数据 示例1: LambdaQueryWrapper拼接查询条件 private void queryPetShops() { LambdaQueryWrapper<PetShop> query = Pops.<PetShop>lambdaQuery(); query.from(PetShop.MODEL_MODEL); query.setSortable(Boolean.FALSE); query.orderBy(true, true, PetShop::getId); List<PetShop> petShops2 = new PetShop().queryList(query); System.out.printf(petShops2.size() + ""); } 示例2: IWrapper拼接查询条件 private void queryPetShops() { IWrapper<PetShop> wrapper = Pops.<PetShop>lambdaQuery() .from(PetShop.MODEL_MODEL).eq(PetShop::getId,1L); List<PetShop> petShops4 = new PetShop().queryList(wrapper); System.out.printf(petShops4.size() + ""); } 示例3: QueryWrapper拼接查询条件 private void queryPetShops() { //使用Lambda获取字段名,防止后面改字段名漏改 String nameField = LambdaUtil.fetchFieldName(PetTalent::getName); //使用Lambda获取Clumon名,防止后面改字段名漏改 String nameColumn = PStringUtils.fieldName2Column(nameField); QueryWrapper<PetShop> wrapper2 = new QueryWrapper<PetShop>().from(PetShop.MODEL_MODEL) .eq(nameColumn, "test"); List<PetShop> petShops5 = new PetShop().queryList(wrapper2); System.out.printf(petShops5.size() + ""); } IWrapper转为LambdaQueryWrapper @Function.Advanced(type= FunctionTypeEnum.QUERY) @Function.fun(FunctionConstants.queryPage) @Function(openLevel = {FunctionOpenEnum.API}) public Pagination<PetShopProxy> queryPage(Pagination<PetShopProxy> page, IWrapper<PetShopProxy> queryWrapper) { LambdaQueryWrapper<PetShopProxy> wrapper = ((QueryWrapper<PetShopProxy>) queryWrapper).lambda(); // 非存储字段从QueryData中获取 Map<String, Object> queryData = queryWrapper.getQueryData(); if (null != queryData && !queryData.isEmpty()) { String codes = (String) queryData.get("codes"); if (org.apache.commons.lang3.StringUtils.isNotBlank(codes)) { wrapper.in(PetShopProxy::getCode, codes.split(",")); } } return new PetShopProxy().queryPage(page, wrapper); }

    2024年5月25日
    2.4K00
  • 如何选择适合的模型类型?

    介绍 通过Oinone 7天从入门到精通的模型的类型章节我们可以知道模型有抽象模型、存储模型、代理模型、传输模型这四种。但是在在定义模型的时候我们可能不知道该如何选择类型,下面结合业务场景为大家讲解几种模型的典型使用场景。 抽象模型 抽象模型往往是提供公共能力和字段的模型,它本身不会直接用于构建协议和基础设施(如表结构等)。 场景:猫、鸟都继承自动物这个抽象模型 package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; import pro.shushi.pamirs.meta.annotation.sys.Base; import pro.shushi.pamirs.meta.base.IdModel; import pro.shushi.pamirs.meta.enmu.ModelTypeEnum; @Base @Model.model(AbstractAnimal.MODEL_MODEL) @Model.Advanced(type = ModelTypeEnum.ABSTRACT) @Model(displayName = "动物") public abstract class AbstractAnimal extends IdModel { public static final String MODEL_MODEL = "demo.AbstractAnimal"; @Field.String @Field(displayName = "名称") private String name; @Field.String @Field(displayName = "颜色") private String color; } package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; @Model.model(Cat.MODEL_MODEL) @Model(displayName = "猫") public class Cat extends AbstractAnimal { private static final long serialVersionUID = -5104390780952634397L; public static final String MODEL_MODEL = "demo.Cat"; @Field.Integer @Field(displayName = "尾巴长度") private Integer tailLength; } package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; @Model.model(Bird.MODEL_MODEL) @Model(displayName = "鸟") public class Bird extends AbstractAnimal { private static final long serialVersionUID = -5144390780952634397L; public static final String MODEL_MODEL = "demo.Bird"; @Field.Integer @Field(displayName = "翼展宽度") private Integer wingSpanWidth; } 存储模型 存储模型用于定义数据表结构和数据的增删改查(数据管理器)功能,是直接与连接器进行交互的数据容器。 场景:存储模型对应传统开发模式中的数据表,上面例子中的Cat和Birdd都属于传输模型,由于模型定义的注解@Model.Advanced(type = ModelTypeEnum.STORE)默认值就是存储模型,所以一般不用手动指定 代理模型 代理模型是用于代理存储模型的数据管理器能力,同时又可以扩展出非存储数据信息的交互功能的模型。 场景一:隔离数据权限 场景二:增强列表的搜索项 场景三:导入导出的时候增加其他特殊信息 场景四:重写下拉组件的查询逻辑做数据过滤 传输模型 传输模型不会在数据库生成的表,只是作为数据的传输使用,跟传统开发模式中的DTO有一点相似。 场景一:批量处理数据 场景二:处理一些跟数据表无关的操作,如:清理指定业务的缓存、查看一些系统监控信息,可以根据业务信息建立对应的传输模型,在传输模型上创建action动作 场景三:通过传输模型完成复杂页面数据传输

    2024年4月7日
    1.7K00
  • 动态视图支持权限配置

    动态页面在前端自定义之后,目前是没办法通过权限去控制路由页面的权限的,本篇文章将介绍如何解决这个问题。阅读本篇文章之前,应已经实现自定义页面渲染动态视图。参考文章:自定义视图内部渲染动态视图 实现思路: 通过自定义页面里设计的组件api名字,获取配置的路由页面名字。 解析路由页面包含的所有动作,拼接权限节点。 将路由页面的权限节点,拼到自定义视图所在菜单的权限管理上。 效果,路由页面的动作出现在自定义视图所在的菜单权限节点上 代码示例 package pro.shushi.pamirs.top.core.auth; import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import pro.shushi.pamirs.auth.api.entity.node.ActionPermissionNode; import pro.shushi.pamirs.auth.api.entity.node.PermissionNode; import pro.shushi.pamirs.auth.api.extend.load.PermissionNodeLoadExtendApi; import pro.shushi.pamirs.auth.api.loader.visitor.AuthCompileContext; import pro.shushi.pamirs.auth.api.loader.visitor.AuthCompileVisitor; import pro.shushi.pamirs.auth.api.loader.visitor.DslParser; import pro.shushi.pamirs.auth.api.pmodel.AuthResourceAuthorization; import pro.shushi.pamirs.auth.api.utils.AuthAuthorizationHelper; import pro.shushi.pamirs.boot.base.model.Menu; import pro.shushi.pamirs.boot.base.model.View; import pro.shushi.pamirs.boot.base.model.ViewAction; import pro.shushi.pamirs.boot.base.ux.model.UIView; import pro.shushi.pamirs.boot.base.ux.model.UIWidget; import pro.shushi.pamirs.boot.base.ux.model.view.UIField; import pro.shushi.pamirs.boot.web.loader.path.AccessResourceInfo; import pro.shushi.pamirs.boot.web.loader.path.ResourcePath; import pro.shushi.pamirs.boot.web.manager.MetaCacheManager; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.meta.common.spi.SPI; import pro.shushi.pamirs.top.api.TopModule; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; @Component @Order(88) @SPI.Service @Slf4j public class MyTestNodeLoadExtend implements PermissionNodeLoadExtendApi { @Autowired protected MetaCacheManager metaCacheManager; @Override public List<PermissionNode> buildNextPermissions(PermissionNode selected, List<PermissionNode> nodes) { // 动态页面所属的菜单name List<String> menuNames = new ArrayList<>(); menuNames.add("uiMenu3c094a75bd88461ba0ad780825069b32"); // 自定义组件api名称 List<String> apiNames = new ArrayList<>(); apiNames.add("dynamicView"); List<ActionPermissionNode> newNodes = new ArrayList<>(); for (String menuName : menuNames) { List<ActionPermissionNode> actionPermissionNodes = buildActionPermissionNodes(selected, menuName, apiNames); if (CollectionUtils.isNotEmpty(actionPermissionNodes)) { newNodes.addAll(actionPermissionNodes); } } nodes.addAll(newNodes); return nodes; } private List<ActionPermissionNode> buildActionPermissionNodes(PermissionNode selected, String menuName, List<String> apiNames) { String path = ResourcePath.generatorPath(TopModule.MODULE_MODULE, menuName); if (!path.equals(selected.getPath())) { return null; } Menu menu = metaCacheManager.fetchCloneMenus(TopModule.MODULE_MODULE).stream() .filter(v -> v.getName().equals(menuName)) .findFirst() .orElse(null); if (menu == null) { return null; } menu.fieldQuery(Menu::getViewAction); View mainView = fetchMainView(menu.getViewAction()); if (mainView == null) { return null; }…

    2025年9月10日
    1.1K00
  • 系统图标使用自定义CDN地址(内网部署)

    在实际项目中,客户网络环境不能访问外网即纯内网部署。此时需要将所有的静态资源都放在客户内部的CDN上,该篇详细说明实现步骤。 实现步骤 1、把图片等静态资源上传到本地CDN上(如MINIO、Nginx等),图片等静态资源找 数式支持人员 提供; 【注意】:MINIO情况,放置图片等静态资源的桶权限需设置为公共读; 2、项目中YAML的OSS配置,使用本地CDN、并指定使用的本地CDN图标的标识appLogoUseCdn: true, OSS配置参考如下: 本地CDN使用MINIO(仅示例需根据实际情况修改) cdn: oss: name: MINIO type: MINIO # MINIO的配置根据实际情况修改 bucket: pamirs(您的bucket) # 上传和下载地址根据实际情况修改 uploadUrl: http://39.103.145.77:9000 downloadUrl: http://39.103.145.77:9000 accessKeyId: 您的accessKeyId accessKeySecret: 您的accessKeySecret # mainDir对用CDN的图片目录,根据项目情况自行修改 mainDir: upload/demo/ validTime: 3600000 timeout: 600000 active: true referer: # 使用客户自己的CDN的图片,否则系统默认的从数式的CDN中获取 appLogoUseCdn: true 或本地CDN使用Nginx(仅示例需根据实际情况修改) cdn: oss: name: 本地文件NG系统 type: LOCAL bucket: # uploadUrl 这个是Oinone后端服务地址和端口 uploadUrl: http://192.168.0.129:8099 # downloadUrl前端地址,即直接映射在nginx的静态资源的路径和端口 downloadUrl: http://192.168.0.129:9999 validTime: 3600000 timeout: 600000 active: true referer: # 本地Nginx静态资源目录 localFolderUrl: /opt/pamirs/static # 使用客户自己的CDN的图片,否则系统默认的从数式的CDN中获取 appLogoUseCdn: true 3、前端工程3.1 前端源码工程,在.evn中把 STATIC_IMG地址进行修改;http(https)、IP和端口改成与CDN对应的配置,URL中/oinone/static/images是固定的;例如: 本地CDN使用MINIO(仅示例需根据实际情况修改) STATIC_IMG: 'http://39.103.145.77:9000/pamirs(这里替换为OSS中的bucket)/oinone/static/images' 或本地CDN使用Nginx(仅示例需根据实际情况修改) STATIC_IMG: 'http://192.168.0.129:9999/static/oinone/static/images' 3.2 对于已经打包好的前端资源对于已打包好的前端资源即无法修改.evn的情况;需在前端资源的根目录,新建config/manifest.js. 如果已存在则不需要新建,同时原来的内容也不需要删除(追加即可),需增加的配置: 本地CDN使用MINIO(仅示例需根据实际情况修改) runtimeConfigResolve({ STATIC_IMG: 'http://39.103.145.77:9000/pamirs(这里替换为OSS中的bucket)/oinone/static/images', plugins: { usingRemote: true } }) 或本地CDN使用Nginx(仅示例需根据实际情况修改) runtimeConfigResolve({ STATIC_IMG: 'http://192.168.0.129:9999/static/oinone/static/images', plugins: { usingRemote: true } })

    2025年2月8日
    1.6K00
  • mybatis拦截器的使用

    场景:自定义拦截器做数据的加解密。 注册自定义拦截器 @Configuration public class MyBatisConfig { // TODO: 注册自定义拦截器 @Bean @Order(999) public EncryptionInterceptor encryptionInterceptor() { return new EncryptionInterceptor(); } } 使用mybatis拦截器拦截查询。 @Intercepts({ @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}) }) public class EncryptionInterceptor implements Interceptor { @Autowired private EncryptionConfig encryptionConfig; @Override public Object intercept(Invocation invocation) throws Throwable { Object[] args = invocation.getArgs(); MappedStatement ms = (MappedStatement) args[0]; Object parameter = args[1]; // 判断操作类型是insert, update 或 delete if (ms.getSqlCommandType().equals(SqlCommandType.INSERT) || ms.getSqlCommandType().equals(SqlCommandType.UPDATE) || ms.getSqlCommandType().equals(SqlCommandType.DELETE)) { // TODO: 加密字段 encryptFields(parameter); } else if (ms.getSqlCommandType().equals(SqlCommandType.SELECT)) { // TODO: 查询操作,在执行后需要对结果进行解密 Object result = invocation.proceed(); List<EncryptionConfig.Models> models = encryptionConfig.getModels(); for (EncryptionConfig.Models model : models) { if (judgmentModel(parameter, model)) { decryptFields(result); } } return result; } return invocation.proceed(); } private Boolean judgmentModel(Object parameter, EncryptionConfig.Models model) { MetaObject metaObject = SystemMetaObject.forObject(parameter); if (metaObject.getOriginalObject() instanceof MapperMethod.ParamMap) { if (metaObject.hasGetter("ew")) { Object param1 = metaObject.getValue("ew"); if (param1 != null) { Object originalObject = SystemMetaObject.forObject(param1).getOriginalObject(); if (originalObject instanceof QueryWrapper) { DataMap entity = (DataMap) ((QueryWrapper<?>) originalObject).getEntity(); if (entity != null) { Object modelFieldName = entity.get(FieldConstants._d_modelFieldName);…

    2024年12月2日
    1.5K00

Leave a Reply

Please Login to Comment