技术精要:数据导出与固化实用指南

数据被认为是企业发展和决策的重要资产。随着业务的不断发展和数据量的不断增加,企业通常需要将数据从不同的源头导出,并将其固化到产品中,以便进行进一步的分析、处理和利用。数据导出与固化的过程涉及到数据的提取、清洗、整合和存储,是确保数据长期有效性和可用性的关键步骤。

了解数据导出与固化的流程和方法对于企业具有重要意义。通过有效的数据导出和固化,企业可以更好地管理和利用数据资源,提升决策的准确性和效率,实现业务的持续发展和创新。本次讨论将重点探讨数据导出与固化的流程和关键步骤,帮助参与者深入了解如何将数据从导出到产品中,为企业数据管理和应用提供有力支持。

1. 数据导出与固化:将数据从导出到产品中的流程

1.1. pom依赖:

<dependency>
 <groupId>pro.shushi.pamirs.metadata.manager</groupId>
 <artifactId>pamirs-metadata-manager</artifactId>
</dependency>

1.2 将第⼆步下载后的⽂件放⼊项⽬中(注意⽂件放置的位置)。放置⼯程的resources

下⾯。例如:
技术精要:数据导出与固化实用指南

1.3 项⽬启动过程中,将⽂件中的数据导⼊(通常放在core模型的init包下

⾯)。⽰例代码:

package pro.shushi.pamirs.sys.setting.enmu;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import 
org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import 
pro.shushi.pamirs.boot.common.api.command.AppLifecycleCom
mand;
import 
pro.shushi.pamirs.boot.common.api.init.LifecycleCompleted
AllInit;
import 
pro.shushi.pamirs.boot.common.extend.MetaDataEditor;
import pro.shushi.pamirs.core.common.InitializationUtil;
import 
pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j;
import pro.shushi.pamirs.meta.api.dto.meta.Meta;
import 
pro.shushi.pamirs.meta.domain.module.ModuleDefinition;
import 
pro.shushi.pamirs.metadata.manager.core.helper.DesignerIn
stallHelper;
import 
pro.shushi.pamirs.metadata.manager.core.helper.WidgetInst
allHelper;
import java.util.List;
import java.util.Map;
@Slf4j
@Component
public class DemoAppMetaInstall implements 
MetaDataEditor, LifecycleCompletedAllInit {
 @Autowired
 private ApplicationContext applicationContext;
 @Override
 public void edit(AppLifecycleCommand command, 
Map<String, Meta> metaMap) {
 if (!doImport()) {
 return;
 }
 log.info("[设计器业务元数据导⼊]");
 InitializationUtil bizInitializationUtil = 
InitializationUtil.get(metaMap, DemoModule.MODULE_MODULE/
***改成⾃⼰的Module*/, DemoModule.MODULE_NAME/***改成⾃⼰的
Module*/);

DesignerInstallHelper.mateInitialization(bizInitializatio
nUtil, "install/meta.json");
 log.info("[⾃定义组件元数据导⼊]");
// 写法1: 将组件元数据导⼊到⻚⾯设计器. 只有在安装设计器的
服务中执⾏才有效果
 WidgetInstallHelper.mateInitialization(metaMap, 
"install/widget.json");
// 写法2: 与写法1相同效果
 InitializationUtil uiInitializationUtil = 
InitializationUtil.get(metaMap, "ui_designer", 
"uiDesigner");
 if (uiInitializationUtil != null) {

DesignerInstallHelper.mateInitialization(uiInitialization
Util, "install/widget.json");
 }
// 写法3: 业务⼯程和设计器分布式部署,且希望通过业务⼯程导⼊
⾃定义组件元数据. 业务模块需要依赖⻚⾯设计器模块,然后指定业务模块导
⼊

DesignerInstallHelper.mateInitialization(bizInitializatio
nUtil, "install/widget.json");
 }
 @Override
 public void process(AppLifecycleCommand command, 
Map<String, ModuleDefinition> runModuleMap) {
 if (!doImport()) {
 return;
 }
 log.info("[设计器业务数据导⼊]");
// ⽀持远程调⽤,但是执⾏的⽣命周期必须是
LifecycleCompletedAllInit或之后. 本地如果安装了设计器,则没有要
求
 DesignerInstallHelper.bizInitialization("install/
meta.json");
 log.info("[⾃定义组件业务数据导⼊]");
// 当开发环境和导⼊环境的⽂件服务不互通时, 可通过指定js和
css的⽂件压缩包,⾃动上传到导⼊环境,并替换导⼊组件数据中的⽂件url
// WidgetInstallHelper.bizInitialization("install/
widget.json", "install/widget.zip");
 WidgetInstallHelper.bizInitialization("install/
widget.json");
 return;
 }
 private boolean doImport() {
 // ⾃定义导⼊判断. 避免⽤于设计的开发环境执⾏导⼊逻辑
 String[] envs = 
applicationContext.getEnvironment().getActiveProfiles();
 List<String> envList = Lists.newArrayList(envs);
 return CollectionUtils.isNotEmpty(envList) && 
(envList.contains("prod"));
 }
}

2. 设计器数据导出

简介
通过调⽤导出接口,将设计器的设计数据与运动数据打包导出到⽂件中。
提供了download/export两类接⼜。
export
导出到OSS。导出的⽂件会上传到⽂件服务,通过返回的url下载导出⽂件。
请求⽰例:

mutation {
    uiDesignerExportReqMutation {
        export(
            data: { module: "gemini_core", fileName: "meta", moduleBasics: true }
        ) {
            jsonUrl
        }
    }
}

响应⽰例:

{
          "data":  {
                    "uiDesignerExportReqMutation":  {
                              "export":  {
                                        "jsonUrl":  "https://xxx/meta.json"
                              }
                    }
          },
          "errors":  [

          ],
          "extensions":  {

          }
}

download
直接返回导出数据。适⽤于通过浏览器直接下载⽂件。
请求⽰例:

mutation {
    uiDesignerExportReqMutation {
        download(
            data: { module: "gemini_core", fileName: "meta", moduleBasics: true }
        ) {
            jsonUrl
        }
    }
}

如何构造url
protocol :// hostname[:port] / path ?
query=URLEncode(GraphQL)
例:

http://127.0.0.1:8080/pamirs/base?
query=mutation%20%7B%0A%09uiDesignerExportReqMutation%20%
7B%0A%09%09download(%0A%09%09%09data%3A%20%7B%20module%3A
%20%22gemini_core%22%2C%20fileName%3A%20%22meta%22%2C%20m
oduleBasics%3A%20true%20%7D%0A%09%09)%20%7B%0A%09%09%09js
onUrl%0A%09%09%7D%0A%09%7D%0A%7D

在浏览器中访问构造后的url,可直接下载⽂件
接口列表

2.1 模型设计器

指定模块导出

#生成json
query {
    modelMetaDataExporterQuery {
        export(query: { module: "模块编码" }) {
            module
            url
        }
    }
}
#生成json文件下载
query {
    modelMetaDataExporterQuery {
        download(query: { module: "模块编码" }) {
            module
            url
        }
    }
}

module参数:指定导出的模块编码
url返回结果:export⽅式导出的⽂件url

2.2 页⾯设计器-导出页⾯

2.2.1 指定模块导出

#生成json
mutation {
    uiDesignerExportReqMutation {
        export(
            data: { module: "gemini_core", fileName: "meta", moduleBasics: true }
        ) {
            jsonUrl
        }
    }
}
#生成json文件下载
mutation {
    uiDesignerExportReqMutation {
        download(
            data: { module: "gemini_core", fileName: "meta", moduleBasics: true }
        ) {
            jsonUrl
        }
    }
}

module参数:模块编码
fileName参数:指定⽣成的json⽂件名称
moduleBasics参数:指定是否只导出模块基础数据,如果为true,只导出内置布局、模
块菜单、菜单关联的动作。 如果为false,还会导出模块内的所有页⾯,以及页⾯关联
的动作元数据、页⾯设计数据 等等。 默认值为false。

2.2.2 指定菜单导出

#生成json
mutation {
    uiDesignerExportReqMutation {
        export(
            data: {
                menu: { name: "uiMenu0000000000048001" }
                fileName: "meta"
                relationViews: true
            }
        ) {
            jsonUrl
        }
    }
}
#生成json文件下载
mutation {
    uiDesignerExportReqMutation {
        download(
            data: {
                menu: { name: "uiMenu0000000000048001" }
                fileName: "meta"
                relationViews: true
            }
        ) {
            jsonUrl
        }
    }
}

menu参数:菜单对象,指定菜单的name。只会导出该菜单及其绑定页⾯,不会递归查
询⼦菜单
fileName参数:指定⽣成的json⽂件名称
relationViews参数:指定是否导出关联页⾯,默认为false,只导出菜单关联的页⾯。如
果为true,还会导出该页⾯通过跳转动作关联的⾃定义页⾯。

2.2.3 指定页⾯导出

#生成json
mutation {
    uiDesignerExportReqMutation {
        export(
            data: {
                view: {
                    name: "xx_TABLE_0000000000119001"
                    model: "ui.designer.TestUiDesigner"
                }
                fileName: "meta"
                relationViews: true
            }
        ) {
            jsonUrl
        }
    }
}
#生成json文件下载
mutation {
    uiDesignerExportReqMutation {
        download(
            data: {
                view: {
                    name: "xx_TABLE_0000000000119001"
                    model: "ui.designer.TestUiDesigner"
                }
                fileName: "meta"
                relationViews: true
            }
        ) {
            jsonUrl
        }
    }
}

view参数:视图对象,指定视图的name和model
fileName参数:指定⽣成的json⽂件名称
relationViews参数:指定是否导出关联页⾯,默认为false,只导出菜单关联的页⾯。如
果为true,还会导出该页⾯通过跳转动作关联的⾃定义页⾯。

2.3 导出组件

2.3.1 导出全部组件数据

#生成json
mutation {
    uiDesignerExportReqMutation {
        exportWidget(data: { fileName: "meta" }) {
            jsonUrl
        }
    }
}
#生成json文件下载
mutation {
    uiDesignerExportReqMutation {
        downloadWidget(data: { fileName: "meta" }) {
            jsonUrl
        }
    }
}

fileName参数:指定⽣成的json⽂件名称
注意:⾃定义组件的元数据归属于页⾯设计器(ui_designer) 因此导⼊元数据的模块
(module)并不是业务模块。组件导⼊建议使⽤
pro.shushi.pamirs.metadata.manager.core.helper.WidgetInstallHelper

2.3.2 导出全部组件⽂件

当开发环境,和导⼊环境的oss不互通时,可通过⼀下⽅法导出⾃定义组件的css和
js⽂件压缩包,在导⼊时⽀持指定zip⽂件上传到oss,并替换导⼊组件数据中的css和js
⽂件路径。

#生成json
mutation {
    uiDesignerExportReqMutation {
        exportWidgetFile(data: { fileName: "widget" }) {
            jsonUrl
        }
    }
}
#生成json文件下载
mutation {
    uiDesignerExportReqMutation {
        downloadWidgetFile(data: { fileName: "widget" }) {
            jsonUrl
        }
    }
}

2.4 流程设计器

2.4.1 指定模块导出

参数说明:

  • module参数:模块编码

接口示例:

#生成json
mutation {
    workflowDesignerExportReqMutation {
        export(data: { module: "resource", fileName: "meta" }) {
            jsonUrl
        }
    }
}
#生成json文件下载
mutation {
    workflowDesignerExportReqMutation {
        download(data: { module: "resource", fileName: "meta" }) {
            jsonUrl
        }
    }
}

2.4.2 指定流程编码导出

参数说明:

  • workflowCode参数:流程编码

接口示例:

#生成json
mutation {
    workflowDesignerExportReqMutation {
        export(data: { workflowCode: "WF0000000000132500", fileName: "meta" }) {
            jsonUrl
        }
    }
}
#生成json文件下载
mutation {
    workflowDesignerExportReqMutation {
        download(data: { workflowCode: "WF0000000000132500", fileName: "meta" }) {
            jsonUrl
        }
    }
}

2.4 数据可视化

2.4.1全部导出

接口示例:

#生成json
mutation {
    dataDesignerExportReqMutation {
        export(data: { fileName: "meta" }) {
            jsonUrl
        }
    }
}
#生成json文件下载
mutation {
    dataDesignerExportReqMutation {
        download(data: { fileName: "meta" }) {
            jsonUrl
        }
    }
}

2.4.2 指定图表导出

参数说明:

  • chartCode参数:图表编码

接口示例:

#生成json
mutation {
    dataDesignerExportReqMutation {
        export(data: { chartCode: "CT00000000002000", fileName: "meta" }) {
            jsonUrl
        }
    }
}
#生成json文件下载
mutation {
    dataDesignerExportReqMutation {
        download(data: { chartCode: "CT00000000002000", fileName: "meta" }) {
            jsonUrl
        }
    }
}

2.4.3指定报表导出

参数说明:

  • reportCode参数:报表编码

接口示例:

#生成json
mutation {
    dataDesignerExportReqMutation {
        export(data: { reportCode: "RP00001000", fileName: "meta" }) {
            jsonUrl
        }
    }
}
#生成json文件下载
mutation {
    dataDesignerExportReqMutation {
        download(data: { reportCode: "RP00001000", fileName: "meta" }) {
            jsonUrl
        }
    }
}

2.4.4 指定业务⼤屏导出

参数说明:

  • screenCode参数:业务⼤屏

接口示例:

#生成json
mutation {
    dataDesignerExportReqMutation {
        export(data: { screenCode: "DS00001000", fileName: "meta" }) {
            jsonUrl
        }
    }
}
#生成json文件下载
mutation {
    dataDesignerExportReqMutation {
        download(data: { screenCode: "DS00001000", fileName: "meta" }) {
            jsonUrl
        }
    }
}

以上方式通过有效的数据导出和固化,企业可以建立可靠的数据基础设施,支持数据驱动的决策和业务发展。

Oinone社区 作者:数式-海波原创文章,如若转载,请注明出处:https://doc.oinone.top/backend/5785.html

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

Like (2)
数式-海波's avatar数式-海波数式管理员
Previous 2024年2月26日 pm5:47
Next 2024年2月28日 pm3:11

相关推荐

  • 树形下拉组件数据查询覆写

    一、背景与问题 Oinone 的树形下拉组件(TreeSelect)和树视图(Tree)在查询数据时,最终调用的是树节点所配置模型的数据管理器函数: 树接口 调用的数据管理器函数 场景 fetchAll queryListByWrapper 一次性全量加载 fetchChildren queryPage 按层级懒加载(默认) queryKeywords4Tree queryListByWrapper 关键字搜索 reverselyQuery queryListByWrapper 已选数据回填 官方文档原文:「所有树查询接口都会调用模型对应的数据管理器函数,可以通过重写的方式修改其查询结果。」 直接覆写的风险 如果直接在原模型(如 business.PamirsDepartment)上覆写 queryPage: // ⚠️ 危险写法:会影响所有使用 PamirsDepartment.queryPage 的场景 @Component @Model.model(PamirsDepartment.MODEL_MODEL) public class DeptQueryAction { @Function.Advanced(type = FunctionTypeEnum.QUERY, category = FunctionCategoryEnum.QUERY_PAGE) @Function.fun(FunctionConstants.queryPage) @Function(openLevel = {FunctionOpenEnum.LOCAL, FunctionOpenEnum.REMOTE, FunctionOpenEnum.API}) public Pagination<PamirsDepartment> queryPage(Pagination<PamirsDepartment> page, IWrapper<PamirsDepartment> queryWrapper) { // 追加过滤条件… return new PamirsDepartment().queryPage(page, queryWrapper); } } 该覆写会全局生效——不仅是树形下拉,所有调用 PamirsDepartment 的 queryPage 的地方(组织架构页面、人员管理列表、权限分配、框架内部关联查询等)都会受到影响,容易引发难以排查的副作用。 二、推荐方案:代理模型(PROXY)隔离 核心思路 创建一个继承原模型的代理模型(ModelTypeEnum.PROXY),拥有独立的模型命名空间 在代理模型中重新声明自关联字段(如 parent),将关联目标指向代理模型自身 在代理模型的 Action 中覆写数据管理器函数,追加树形场景专属的过滤逻辑 树形下拉的视图 DSL 中,<node> 的 model 指向代理模型 代理模型与原模型共享同一张数据表,但函数注册在各自独立的命名空间下,互不干扰。 框架官方示例 Oinone 框架自身就采用了这一模式:ResourceRegionProxyModel 继承 ResourceRegion,用于地区页面的代理展示,其扩展点 ResourceRegionProxyModelExtPoint 中的 queryPageAfter 只影响代理模型自身的查询。 三、完整实现步骤(以 PamirsDepartment 为例) 场景假设 业务需求:某个表单页面的「部门」树形下拉只需要展示当前公司(companyCode == 当前用户公司)且启用状态(dataStatus == ENABLED)的部门,而系统中其他使用 PamirsDepartment 的地方(组织架构、权限管理等)不受影响。 步骤 1:定义代理模型 package com.example.demo.model; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; import pro.shushi.pamirs.meta.enmu.ModelTypeEnum; import pro.shushi.pamirs.business.api.model.PamirsDepartment; import java.util.List; /** * 部门树形下拉专用代理模型 * <p> * 继承 PamirsDepartment,共享同一张数据表, * 但拥有独立的函数命名空间,覆写 queryPage 不影响原模型。 */ @Model.model(DeptTreeProxy.MODEL_MODEL) @Model.Advanced(type = ModelTypeEnum.PROXY) @Model(displayName = "部门(树选择)", labelFields = "name") public class DeptTreeProxy extends PamirsDepartment { public static final String MODEL_MODEL = "demo.DeptTreeProxy"; /** * 【必须重新声明】上级部门(自关联字段) * <p> * 基类 PamirsDepartment 中 parent 的类型是 PamirsDepartment, * 代理模型必须将其覆盖为指向自身(DeptTreeProxy), * 否则设计器/树组件无法在代理模型命名空间内构成完整的自关联关系, * 导致树形下拉无法正确展开层级。 */ @Field.many2one @Field.Relation(relationFields = {"parentCode"}, referenceFields = {"code"}) @Field(displayName = "上级部门")…

    后端 2026年7月27日
    13300
  • 如何自定义Excel导入功能

    介绍 在平台提供的默认导入功能无法满足业务需求的时候,我们可以自定义导入功能,以满足业务中个性化的需求。 功能示例 下面以导入文件的时候加入发布人的字段作为示例讲解。 继承平台的导入任务模型,加上需要在导入的弹窗视图需要展示的字段 package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.file.api.model.ExcelImportTask; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; @Model.model(DemoItemImportTask.MODEL_MODEL) @Model(displayName = "商品-Excel导入任务") public class DemoItemImportTask extends ExcelImportTask { public static final String MODEL_MODEL = "demo.DemoItemImportTask"; // 自定义显示的字段 @Field.String @Field(displayName = "发布人") private String publishUserName; } 编写自定义导入弹窗视图的数据初始化方法和导入提交的action package pro.shushi.pamirs.demo.core.action; import org.springframework.stereotype.Component; import pro.shushi.pamirs.boot.base.resource.PamirsFile; import pro.shushi.pamirs.demo.api.model.DemoItemImportTask; import pro.shushi.pamirs.file.api.action.ExcelImportTaskAction; import pro.shushi.pamirs.file.api.config.FileProperties; import pro.shushi.pamirs.file.api.model.ExcelWorkbookDefinition; import pro.shushi.pamirs.file.api.service.ExcelFileService; import pro.shushi.pamirs.meta.annotation.Action; import pro.shushi.pamirs.meta.annotation.Function; import pro.shushi.pamirs.meta.annotation.Model; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.meta.enmu.ActionContextTypeEnum; import pro.shushi.pamirs.meta.enmu.FunctionOpenEnum; import pro.shushi.pamirs.meta.enmu.FunctionTypeEnum; import pro.shushi.pamirs.meta.enmu.ViewTypeEnum; @Slf4j @Component @Model.model(DemoItemImportTask.MODEL_MODEL) public class DemoItemExcelImportTaskAction extends ExcelImportTaskAction { public DemoItemExcelImportTaskAction(FileProperties fileProperties, ExcelFileService excelFileService) { super(fileProperties, excelFileService); } @Action(displayName = "导入", contextType = ActionContextTypeEnum.CONTEXT_FREE, bindingType = {ViewTypeEnum.TABLE}) public DemoItemImportTask createImportTask(DemoItemImportTask data) { if (data.getWorkbookDefinitionId() != null) { ExcelWorkbookDefinition workbookDefinition = new ExcelWorkbookDefinition(); workbookDefinition.setId(data.getWorkbookDefinitionId()); data.setWorkbookDefinition(workbookDefinition); } Object fileId = data.get_d().get("fileId"); if (fileId != null) { PamirsFile pamirsFile = new PamirsFile().queryById(Long.valueOf(fileId.toString())); data.setFile(pamirsFile); } super.createImportTask(data); return data; } /** * @param data * @return */ @Function(openLevel = FunctionOpenEnum.API) @Function.Advanced(type = FunctionTypeEnum.QUERY) public DemoItemImportTask construct(DemoItemImportTask data) { data.construct(); return data; } } 编写导入的单行数据处理逻辑,此处可以拿到导入弹窗内自定义的字段提交的值,然后根据这些值处理自定义逻辑,此处演示代码就是将导入后的商品的发布人都设置为自定义导入视图填的发布人信息 package pro.shushi.pamirs.demo.core.excel.extPoint; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import pro.shushi.pamirs.demo.api.model.DemoItem; import pro.shushi.pamirs.demo.api.model.DemoItemImportTask; import pro.shushi.pamirs.demo.api.service.DemoItemService; import pro.shushi.pamirs.file.api.context.ExcelImportContext; import pro.shushi.pamirs.file.api.extpoint.AbstractExcelImportDataExtPointImpl; import pro.shushi.pamirs.file.api.extpoint.ExcelImportDataExtPoint;…

    2023年11月22日
    1.6K00
  • 如何重写获取首页的方法

    介绍 用户登录成功后或者访问网页不带任何路由参数的时候前端会请求全局的首页的视图动作viewAction配置,然后跳转到该视图动作viewAction 方案 我们可以通过在该方法的后置hook自定义获取首页的逻辑,下面以按角色跳转不同首页的需求示例 package pro.shushi.pamirs.demo.core.hook; import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Component; import pro.shushi.pamirs.auth.api.model.AuthRole; import pro.shushi.pamirs.boot.base.enmu.BaseExpEnumerate; import pro.shushi.pamirs.boot.base.model.ViewAction; import pro.shushi.pamirs.boot.web.loader.PageLoadAction; import pro.shushi.pamirs.demo.api.model.DemoItemCategory; import pro.shushi.pamirs.demo.api.model.DemoItemLabel; import pro.shushi.pamirs.meta.annotation.Hook; import pro.shushi.pamirs.meta.api.CommonApiFactory; import pro.shushi.pamirs.meta.api.core.faas.HookAfter; import pro.shushi.pamirs.meta.api.dto.fun.Function; import pro.shushi.pamirs.meta.api.session.PamirsSession; import pro.shushi.pamirs.meta.common.exception.PamirsException; import pro.shushi.pamirs.user.api.model.PamirsUser; import java.util.List; import java.util.stream.Collectors; @Component public class DemoHomepageHook implements HookAfter { private static final String TEST_ROLE_CODE_01 = "ROLE_1211"; private static final String TEST_ROLE_CODE_02 = "ROLE_1211_1"; @Override @Hook(module = {"base"}, model = {ViewAction.MODEL_MODEL}, fun = {"homepage"}) public Object run(Function function, Object ret) { if (ret == null) { return null; } ViewAction viewAction = getViewActionByCurrentRole(); if (viewAction != null) { ViewAction retNew = CommonApiFactory.getApi(PageLoadAction.class).load(viewAction); ViewAction viewActionRet = (ViewAction) ((Object[]) ret)[0]; viewActionRet.set_d(retNew.get_d()); } return ret; } protected ViewAction getViewActionByCurrentRole() { try { PamirsUser user = new PamirsUser(); user.setId(PamirsSession.getUserId()); user.fieldQuery(PamirsUser::getRoles); List<AuthRole> roles = user.getRoles(); if (CollectionUtils.isNotEmpty(roles)) { List<String> roleCodes = roles.stream().map(AuthRole::getCode).collect(Collectors.toList()); if (roleCodes.contains(TEST_ROLE_CODE_01)) { return new ViewAction().setModel(DemoItemCategory.MODEL_MODEL).setName("DemoMenus_ItemPMenu_DemoItemAndCateMenu_DemoItemCategoryMenu").queryOne(); } else if (roleCodes.contains(TEST_ROLE_CODE_02)) { return new ViewAction().setModel(DemoItemLabel.MODEL_MODEL).setName("DemoMenus_ItemPMenu_DemoItemAndCateMenu_DemoItemLabelMenu").queryOne(); } } } catch (PamirsException exception) { if (PamirsSession.getUserId() == null) { throw PamirsException.construct(BaseExpEnumerate.BASE_USER_NOT_LOGIN_ERROR, exception.getCause()).errThrow(); } else { throw exception; } } return null; } }

    2024年7月6日
    2.9K00
  • EIP开放接口使用MD5验签发起请求(v5.x)

    验签工具类 PS:该验签方法仅在pamirs-core的5.0.16版本以上可正常使用 public class EipSignUtils { public static final String SIGN_METHOD_MD5 = "md5"; private static final String SIGN_METHOD_HMAC = "hmac"; private static final String SECRET_KEY_ALGORITHM = "HmacMD5"; private static final String MESSAGE_DIGEST_MD5 = "MD5"; public static String signTopRequest(Map<String, String> params, String secret, String signMethod) throws IOException { // 第一步:检查参数是否已经排序 String[] keys = params.keySet().toArray(new String[0]); Arrays.sort(keys); // 第二步:把所有参数名和参数值串在一起 StringBuilder query = new StringBuilder(); if (SIGN_METHOD_MD5.equals(signMethod)) { query.append(secret); } for (String key : keys) { String value = params.get(key); if (StringUtils.isNoneBlank(key, value)) { query.append(key).append(value); } } // 第三步:使用MD5/HMAC加密 byte[] bytes; if (SIGN_METHOD_HMAC.equals(signMethod)) { bytes = encryptHMAC(query.toString(), secret); } else { query.append(secret); bytes = encryptMD5(query.toString()); } // 第四步:把二进制转化为大写的十六进制(正确签名应该为32大写字符串,此方法需要时使用) return byte2hex(bytes); } private static byte[] encryptHMAC(String data, String secret) throws IOException { byte[] bytes; try { SecretKey secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), SECRET_KEY_ALGORITHM); Mac mac = Mac.getInstance(secretKey.getAlgorithm()); mac.init(secretKey); bytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8)); } catch (GeneralSecurityException e) { throw new IOException(e.toString(), e); } return bytes; } private static byte[] encryptMD5(String data) throws IOException { return encryptMD5(data.getBytes(StandardCharsets.UTF_8)); } private static byte[] encryptMD5(byte[] data) throws IOException { try { MessageDigest md = MessageDigest.getInstance(MESSAGE_DIGEST_MD5); return md.digest(data); } catch (NoSuchAlgorithmException e)…

    2024年6月29日
    1.9K00
  • 首次登录修改密码和自定义密码规则等

    场景描述 在某些场景下,可能需要实现 用户首次登录强制修改密码的功能,或者存在修改平台默认密码等校验规则等需求;本文将讲解不改变平台代码的情况下,如何实现这些功能需求。 首次登录修改密码 方案概述 自定义User增加是否是第一次登录的属性,登录后执行一个扩展点。 判断是否是一次登录,如果是则返回对应的状态码,前端根据状态码重定向到修改密码的页面。修改完成则充值第一次登录的标识。 PS:首次登录的标识平台前端已默认实现 扩展PamirsUser(例如:DemoUser) /** * @author wangxian */ @Model.model(DemoUser.MODEL_MODEL) @Model(displayName = "用户", labelFields = {"nickname"}) @Model.Advanced(index = {"companyId"}) public class DemoUser extends PamirsUser { public static final String MODEL_MODEL = "demo.DemoUser"; @Field.Integer @Field.Advanced(columnDefinition = "bigint DEFAULT '0'") @Field(displayName = "公司ID", invisible = true) private Long companyId; /** * 默认true->1 */ @Field.Boolean @Field.Advanced(columnDefinition = "tinyint(1) DEFAULT '1'") @Field(displayName = "是否首次登录") private Boolean firstLogin; } 定义扩展点接口(实际项目按需要增加和删减接口的定义) import pro.shushi.pamirs.meta.annotation.Ext; import pro.shushi.pamirs.meta.annotation.ExtPoint; import pro.shushi.pamirs.user.api.model.tmodel.PamirsUserTransient; @Ext(PamirsUserTransient.class) public interface PamirsUserTransientExtPoint { @ExtPoint PamirsUserTransient loginAfter(PamirsUserTransient user); @ExtPoint PamirsUserTransient loginCustomAfter(PamirsUserTransient user); @ExtPoint PamirsUserTransient firstResetPasswordAfter(PamirsUserTransient user); @ExtPoint PamirsUserTransient firstResetPasswordBefore(PamirsUserTransient user); @ExtPoint PamirsUserTransient modifyCurrentUserPasswordAfter(PamirsUserTransient user); @ExtPoint PamirsUserTransient modifyCurrentUserPasswordBefore(PamirsUserTransient user); } 编写扩展点实现(例如:DemoUserLoginExtPoint) @Order(0) @Component @Ext(PamirsUserTransient.class) @Slf4j public class DemoUserLoginExtPoint implements PamirsUserTransientExtPoint { @Override @ExtPoint.Implement public PamirsUserTransient loginAfter(PamirsUserTransient user) { return checkFirstLogin(user); } private PamirsUserTransient checkFirstLogin(PamirsUserTransient user) { //首次登录需要修改密码 Long userId = PamirsSession.getUserId(); if (userId == null) { return user; } DemoUser companyUser = new DemoUser().queryById(userId); // 判断用户是否是第一次登录,如果是第一次登录,需要返回错误码,页面重新向登录 Boolean isFirst = companyUser.getFirstLogin(); if (isFirst) { //如果是第一次登录,返回一个标识给前端。 // 首次登录的标识平台已默认实现 user.setBroken(Boolean.TRUE); user.setErrorCode(UserExpEnumerate.USER_FIRST_LOGIN_ERROR.code()); return user; } return user; } @Override public PamirsUserTransient loginCustomAfter(PamirsUserTransient user) { return checkFirstLogin(user); } @Override…

    2024年5月25日
    5.9K00

Leave a Reply

Please Login to Comment