自定义RSQL占位符(placeholder)及在权限中使用

1 自定义RSQL占位符常用场景

  • 统一的数据权限配置
  • 查询表达式的上下文变量扩展

2 自定义RSQL的模板

/**
 * 演示Placeholder占位符基本定义
 *
 * @author Adamancy Zhang at 13:53 on 2024-03-24
 */
@Component
public class DemoPlaceHolder extends AbstractPlaceHolderParser {

    private static final String PLACEHOLDER_KEY = "${thisPlaceholder}";

    /**
     * 占位符
     *
     * @return placeholder
     */
    @Override
    public String namespace() {
        return PLACEHOLDER_KEY;
    }

    /**
     * 占位符替换值
     *
     * @return the placeholder replace to the value
     */
    @Override
    protected String value() {
        return PamirsSession.getUserId().toString();
    }

    /**
     * 优先级
     *
     * @return execution order of placeholders, ascending order.
     */
    @Override
    public Integer priority() {
        return 0;
    }

    /**
     * 是否激活
     *
     * @return the placeholder is activated
     */
    @Override
    public Boolean active() {
        return true;
    }
}

注意事项

  • 在一些旧版本中,priorityactive可能不起作用,为保证升级时不受影响,请保证该属性配置正确。

  • PLACEHOLDER_KEY变量表示自定义占位符使用的关键字,需按照所需业务场景的具体功能并根据上下文语义正确定义。

  • 为保证占位符可以被正确替换并执行,所有占位符都不应该出现重复,尤其是不能与系统内置的重复。

3 占位符使用时的优先级问题

多个占位符在进行替换时,会根据优先级按升序顺序执行,如需要指定替换顺序,可使用SpringOrder注解对其进行排序。

import org.springframework.core.annotation.Order;

@Order(0)

4 Oinone平台内置的占位符

占位符 数据类型 含义 备注
${currentUser} String 当前用户ID 未登录时无法使用
${currentRoles} Set<String> 当前用户的角色ID集合 未登录时无法使用

5 如何覆盖平台内置的占位符?

通过指定占位符的优先级,并定义相同的namespace可优先替换。

6 如何定义会话级别的上下文变量?

在上述模板中,我们使用的是Oinone平台内置的上下文变量进行演示,通常情况下,我们需要根据实际业务场景增加上下文变量,以此来实现所需功能。

下面,我们将根据当前用户获取当前员工ID定义该上下文变量进行演示。

/**
 * 员工Session
 *
 * @author Adamancy Zhang at 14:33 on 2024-03-24
 */
@Component
public class EmployeeSession implements HookBefore {

    private static final String SESSION_KEY = "CUSTOM_EMPLOYEE_ID";

    @Autowired
    private DemoEmployeeService demoEmployeeService;

    public static String getEmployeeId() {
        return PamirsSession.getTransmittableExtend().get(SESSION_KEY);
    }

    @Override
    @Hook(priority = 1)
    public Object run(Function function, Object... args) {
        Long userId = PamirsSession.getUserId();
        if (userId == null) {
            return function;
        }
        if (StringUtils.isBlank(EmployeeSession.getEmployeeId())) {
            PamirsSession.getTransmittableExtend().put(SESSION_KEY, getCurrentEmployeeId());
        }
        return function;
    }

    private String getCurrentEmployeeId() {
        String employeeId = getEmployeeIdByCache();
        if (employeeId == null) {
            employeeId = demoEmployeeService.getCurrentEmployeeId();
        }
        return employeeId;
    }

    private String getEmployeeIdByCache() {
        // do something.
        return null;
    }
}

注意事项

  • 使用HookBefore在请求发起时向上下文中设置employeeId,使用EmployeeSession.getEmployeeId()获取即可。

  • HookBefore的优先级设置为priority = 1,需要该Hook在平台内置的UserHook之后执行,以确保PamirsSession.getUserId()中的值已经被正确设置。

  • DemoEmployeeService服务应使用平台@Fun@Function注解进行实现,以确保该Session可在分布式环境中正确运行。

  • getEmployeeIdByCache方法需自行实现,在运行时使用缓存可有效提高性能。

7 将员工Sessionplaceholder中使用

DemoPlaceHolder改写,使用${currentEmployeeId}获取员工Session中保存的employeeId

/**
 * 演示Placeholder占位符使用员工Session
 *
 * @author Adamancy Zhang at 15:02 on 2024-03-24
 */
@Component
public class DemoPlaceHolder extends AbstractPlaceHolderParser {

    private static final String PLACEHOLDER_KEY = "${currentEmployeeId}";

    /**
     * 占位符
     *
     * @return placeholder
     */
    @Override
    public String namespace() {
        return PLACEHOLDER_KEY;
    }

    /**
     * 占位符替换值
     *
     * @return the placeholder replace to the value
     */
    @Override
    protected String value() {
        return EmployeeSession.getEmployeeId();
    }

    /**
     * 优先级
     *
     * @return execution order of placeholders, ascending order.
     */
    @Override
    public Integer priority() {
        return 0;
    }

    /**
     * 是否激活
     *
     * @return the placeholder is activated
     */
    @Override
    public Boolean active() {
        return true;
    }
}

至此,我们已完成了一个员工ID占位符。

8 在权限配置时使用该占位符作为过滤条件

下面,我们将模拟一个简单业务场景,详细介绍该占位符如何在业务中使用。

8.1 场景描述

当前系统中包含部门员工两个模型,模型的基本定义如下所示:

部门

/**
 * 演示部门
 *
 * @author Adamancy Zhang at 15:18 on 2024-03-24
 */
@Model.model(DemoDepartment.MODEL_MODEL)
@Model(displayName = "演示部门", labelFields = "name")
public class DemoDepartment extends IdModel {

    private static final long serialVersionUID = -300189841334506668L;

    public static final String MODEL_MODEL = "demo.DemoDepartment";

    @Field(displayName = "部门名称")
    private String name;

    @Field(displayName = "管理员")
    private DemoEmployee manager;

}

员工

/**
 * 演示员工
 *
 * @author Adamancy Zhang at 15:17 on 2024-03-24
 */
@Model.model(DemoEmployee.MODEL_MODEL)
@Model.Advanced(unique = {"bindingUserId"})
@Model(displayName = "演示员工", labelFields = "name")
public class DemoEmployee extends IdModel {

    private static final long serialVersionUID = -6237083162460091500L;

    public static final String MODEL_MODEL = "demo.DemoEmployee";

    @Field(displayName = "员工名称")
    private String name;

    @Field.Relation(relationFields = {"bindingUserId"}, referenceFields = {"id"})
    @Field(displayName = "绑定用户")
    private PamirsUser bindingUser;

    @Field(displayName = "绑定用户ID")
    private Long bindingUserId;

}

我们要求当前登录用户仅能查看其作为管理员的所在部门,因此,我们需要使用managerId == ${currentEmployeeId}这一过滤条件,在权限中进行配置并使其生效。

此处忽略数据准备过程,仅展示关键页面的配置及最终效果。

8.2 权限项配置

从应用中切换至权限模块,并选择权限项列表,创建一个数据权限项,并按照下图内容进行配置。

权限项配置

8.3 角色权限配置

选择角色列表,并选择角色表格中指定角色的权限配置按钮,进入权限配置页面,并按照下图内容进行配置。

PS:这里省略了动作权限相关配置,配置的权限应保证该角色可以正确进入演示部门页面查看效果。

角色权限配置

8.4 为用户绑定指定角色(如已绑定可忽略该步骤)

从应用中切换至用户中心模块,选择指定用户,绑定指定角色。

8.5 在演示部门页面查看权限配置效果

未配置权限页面效果

未配置权限页面效果

权限配置后页面效果

权限配置后页面效果

本文来自投稿,不代表Oinone社区立场,如若转载,请注明出处:https://doc.oinone.top/backend/6393.html

(1)
张博昊的头像张博昊数式管理员
上一篇 2024年3月20日 pm3:03
下一篇 2024年3月24日 pm1:45

相关推荐

  • Oinone开发实践-业务实现多租户方案

    总体方案 业务项目中,需要隔离的模型自定义增加租户字段进行数据隔离; 参考了Mybatis-Plus插件的TenantSqlParser进行的JPA实现,使用jsqlparser解析并修改SQL; 实现获取当前用户租户ID,SQL增删改查时处理租户字段,实现租户数据的隔离 参考项目: https://github.com/baomidou/mybatis-plus https://github.com/JSQLParser/JSqlParser 具体实现方式 1、业务上定义两个基础抽象模型包含租户字段 定义包含ID的基础抽象模型,且包含租户字段(如:公司编码, 用其他字段作为租户字段也可以,根据实际业务情况灵活修改)。 @Model.model(XXIdModel.MODEL_MODEL) @Model.Advanced(type = ModelTypeEnum.ABSTRACT) @Model(displayName = “带公司CODE的基础ID抽象模型”, summary = “带公司Code的Id模型”) public abstract class XXIdModel extends IdModel { public static final String MODEL_MODEL = “demo.biz.XXIdModel”; @Field.String @Field(displayName = “所属公司编码”, invisible = true, index = true) private String companyCode; } 定义包含Code的基础抽象模型,且包含租户字段(如:公司编码, 用其他字段作为租户字段也可以,根据实际业务情况灵活修改)。 @Model.model(XXCodeModel.MODEL_MODEL) @Model.Advanced(type = ModelTypeEnum.ABSTRACT) @Model(displayName = “带公司CODE的基础Code抽象模型”, summary = “带公司CODE的Code模型”) public abstract class XXCodeModel extends CodeModel { public static final String MODEL_MODEL = “demo.biz.XXCodeModel”; @Field.String @Field(displayName = “所属公司编码”, invisible = true, index = true) private String companyCode; } 2、业务模块的模型需租户隔离的都是继承上面这两个模型; @Model.model(PetPetCompany.MODEL_MODEL) @Model(displayName = “宠物公司”, labelFields = “name”) public class PetPetCompany extends AbstractCompanyCodeModel { public static final String MODEL_MODEL = “demo.PetPetCompany”; @Field.String @Field(displayName = “名称”) private String name; @Field.Text @Field(displayName = “简介”) private String introduction; } 3、自定义扩展Session,Session中设置租户信息 每次请求多把登录用户所属公司编码(companyCode)放到Session中;Session扩展参考:https://doc.oinone.top/oio4/9295.html 4、定义拦截器Interceptor进行数据隔离 数据创建和查询通过拦截器把Session中的中的公司编码(companyCode)设置到隔离字段中;拦截器的java示例代码参考: package pro.shushi.pamirs.demo.core.interceptor; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.LongValue; import net.sf.jsqlparser.expression.StringValue; import net.sf.jsqlparser.expression.operators.relational.ExpressionList; import net.sf.jsqlparser.expression.operators.relational.ItemsListVisitor; import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList; import net.sf.jsqlparser.expression.operators.relational.NamedExpressionList; import net.sf.jsqlparser.parser.CCJSqlParserUtil; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.insert.Insert; import net.sf.jsqlparser.statement.select.*; import net.sf.jsqlparser.statement.update.Update; import net.sf.jsqlparser.statement.values.ValuesStatement; import org.apache.commons.lang3.StringUtils; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.mapping.SqlSource; import org.apache.ibatis.plugin.*; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.SystemMetaObject; import…

    2024年4月6日
    1.3K00
  • 【MSSQL】后端部署使用MSSQL数据库(SQLServer)

    MSSQL数据库配置 驱动配置 Maven配置(2017版本可用) <mssql.version>9.4.0.jre8</mssql.version> <dependency> <groupId>com.microsoft.sqlserver</groupId> <artifactId>mssql-jdbc</artifactId> <version>${mssql.version}</version> </dependency> 离线驱动下载 mssql-jdbc-7.4.1.jre8.jarmssql-jdbc-9.4.0.jre8.jarmssql-jdbc-12.2.0.jre8.jar JDBC连接配置 pamirs: datasource: base: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=base username: xxxxxx password: xxxxxx initialSize: 5 maxActive: 200 minIdle: 5 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true asyncInit: true 连接url配置 暂无官方资料 url格式 jdbc:sqlserver://${host}:${port};DatabaseName=${database} 在jdbc连接配置时,${database}必须配置,不可缺省。 其他连接参数如需配置,可自行查阅相关资料进行调优。 方言配置 pamirs方言配置 pamirs: dialect: ds: base: type: MSSQL version: 2017 major-version: 2017 pamirs: type: MSSQL version: 2017 major-version: 2017 数据库版本 type version majorVersion 2017 MSSQL 2017 2017 PS:由于方言开发环境为2017版本,其他类似版本原则上不会出现太大差异,如出现其他版本无法正常支持的,可在文档下方留言。 schedule方言配置 pamirs: event: enabled: true schedule: enabled: true dialect: type: MSSQL version: 2017 major-version: 2017 type version majorVersion MSSQL 2017 2017 PS:由于schedule的方言在多个版本中并无明显差异,目前仅提供一种方言配置。 其他配置 逻辑删除的值配置 pamirs: mapper: global: table-info: logic-delete-value: CAST(DATEDIFF(S, CAST('1970-01-01 00:00:00' AS DATETIME), GETUTCDATE()) AS BIGINT) * 1000000 + DATEPART(NS, SYSUTCDATETIME()) / 100 MSSQL数据库用户初始化及授权 — init root user (user name can be modified by oneself) CREATE LOGIN [root] WITH PASSWORD = 'password'; — if using mssql database, this authorization is required. ALTER SERVER ROLE [sysadmin] ADD MEMBER [root];

    2024年10月18日
    1.2K00
  • 【OceanBase】后端部署使用 OceanBase 数据库(海扬/OB)

    OceanBase 数据库配置 驱动配置 Maven配置(4.2.5.3版本可用) <oceanbase.version>2.4.14</oceanbase.version> <dependency> <groupId>com.oceanbase</groupId> <artifactId>oceanbase-client</artifactId> <version>${oceanbase.version}</version> </dependency> PS: oceanbase 驱动必须使用 2.4.5 版本或以上,低于此版本的驱动无法使用自增ID功能,无法正常启动。点击查看官方JDBC版本发布记录 JDBC连接配置 OceanBase – Oracle 版 pamirs: datasource: base: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.alipay.oceanbase.jdbc.Driver url: jdbc:oceanbase://10.xxx.xxx.xxx:1001/BASE?useServerPrepStmts=true&useOraclePrepareExecute=true&defaultFetchSize=4096 username: xxxxxx password: xxxxxx validConnectionCheckerClassName: com.alibaba.druid.pool.vendor.OracleValidConnectionChecker validationQuery: SELECT 1 FROM DUAL OceanBase – MySQL 版 pamirs: datasource: base: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.alipay.oceanbase.jdbc.Driver url: jdbc:oceanbase://10.xxx.xxx.xxx:1001/base username: xxxxxx password: xxxxxx 连接 URL 配置 点击查看官方JDBC连接配置说明 URL 格式(OceanBase – Oracle 版) jdbc:oceanbase://${host}:${port}/${database}?useServerPrepStmts=true&useOraclePrepareExecute=true&defaultFetchSize=4096 在jdbc连接配置时,useServerPrepStmts=true&useOraclePrepareExecute=true 必须配置,否则自增主键无法正常使用。 defaultFetchSize=4096 意味着在使用服务端预处理时,游标每次获取的结果集行数,驱动默认值为 10,在进行大量数据获取时会出现卡顿的现象,因此推荐使用 4096 作为其结果集大小。过大可能会导致 OOM,过小可能还是会出现卡顿,该值需要按实际情况进行配置。 其他连接参数如需配置,可自行查阅相关资料进行调优。 方言配置(OceanBase – Oracle 版) PS:OceanBase – MySQL 版无需配置方言,只需修改数据库连接即可正常使用。 pamirs方言配置 pamirs: dialect: ds: base: type: OceanBase version: 4.2.5.3 major-version: oracle-4.2 pamirs: type: OceanBase version: 4.2.5.3 major-version: oracle-4.2 plus: configuration: jdbc-type-for-null: "NULL" using-model-as-property: true using-statement-handler-dialect: true mapper: batch: collectionCommit default-batch-config: read: 500 write: 100 数据库版本 type version majorVersion 4.2.5.3 OceanBase 4.2.5.3 oracle-4.2 PS:由于方言开发环境为4.2.5.3版本,其他类似版本(4.x)原则上不会出现太大差异,如出现其他版本无法正常支持的,可在文档下方留言。 schedule方言配置 pamirs: event: enabled: true schedule: enabled: true dialect: type: Oracle version: 12.2 major-version: 12c type version majorVersion Oracle 12.2 12c PS:由于 schedule 的方言与 Oracle 数据库并无明显差异,OceanBase 数据库可以直接使用 Oracle 数据库方言。 其他配置(OceanBase – Oracle 版) 逻辑删除的值配置 pamirs: mapper: global: table-info: logic-delete-value: (CAST(SYSTIMESTAMP AS DATE) – TO_DATE('1970-01-01 08:00:00', 'YYYY-MM-DD HH24:MI:SS')) * 8640000000000

    2025年7月21日
    90500
  • Oinone协同开发源码分析

    前提 源码分析版本是 5.1.x版本 什么是协同开发模式 协同开发模式解决的是不同开发,在开发同一个模型时,不会相互影响,也不会影响到测试环境详见:Oinone协同开发使用手册 协同开发原理 在协同模式下,本地开发的元数据,配置pamirs.data.distribution.session.ownSign参数后,元数据前缀加ownSign值,然后只存在redis缓存,不落库。其它环境无法直接访问到该数据。测试环境,或其它环境访问,需要在url上加ownSign等于设置的,则读redis数据时,除了加载通用数据,也会合并ownSign前缀的redis数据,显示出来 注意事项 协同开发仅支持界面设计器,其他设计器均不支持 不支持权限配置 不支持工作流触发 版本支持 完整支持5.1.0及以上 功能详解 启动时操作 做元数据保护检查 配置ownSign,则key拼接为 ownSign + ‘:’ + key 清除掉ownSign的redis缓存数据;非ownSign不用清理 计算差量数据 有差量数据,放入ownSign标识数据,并清理本地标识 dubbo注册服务,group拼接group + ownSign 后进行注册 读取时操作 读本地 组装key: ownSign + ‘:’ + key 本地缓存有数据,更新缓存本地数据,返回 本地没有数据,读redis,并插入本地缓存 读远程 dubbo注册消费者,group拼接group + ownSign 后进行泛化调用 元数据保护检查 开启数据保护模式,在启动参数里加-PmetaProtected=pamirs 会在启动时,往redis里写入数据 private static final String META_PROTECTED_KEY = “pamirs:check:meta-protected”; private void writeMetaProtected(String metaProtected) { stringRedisTemplate.opsForValue().set(META_PROTECTED_KEY, metaProtected); } 如果同时又设置 pamirs.data.distribution.session.ownSign则会报错 在使用元数据保护模式下,不允许设置 [pamirs.distribution.session.ownSign] 处理逻辑如下 看redis是否启用保护标识的值 获取pamirs.distribution.session.ownSign配置 没有启动参数 且redis没有值,则retrun 如果有启动参数且配置了ownSign,报错 在使用元数据保护模式下,不允许设置 [pamirs.distribution.session.ownSign] 如果有启动参数且 redis没有值或启动参数设置 -P metaForceProtected,则写入redis 如果有启动参数, 且启动参数跟redis值不同,则报错[公共环境开启了元数据保护模式,本地开发环境需配置[pamirs.distribution.session.ownSign]] 如果没有启动参数且redis有值,但没有配置ownSign 报错[公共环境开启了元数据保护模式,本地开发环境需配置[pamirs.distribution.session.ownSign]] 核心代码如下MetadataProtectedChecker public void process(AppLifecycleCommand command, Set<String> runModules, List<ModuleDefinition> installModules, List<ModuleDefinition> upgradeModules, List<ModuleDefinition> reloadModules) { String currentMetaProtected = stringRedisTemplate.opsForValue().get(META_PROTECTED_KEY); String metaProtected = getMetaProtected(); boolean hasCurrentMetaProtected = StringUtils.isNotBlank(currentMetaProtected); boolean hasMetaProtected = StringUtils.isNotBlank(metaProtected); if (!hasCurrentMetaProtected && !hasMetaProtected) { return; } if (hasMetaProtected) { if (Spider.getDefaultExtension(SessionFillOwnSignApi.class).handleOwnSign()) { // 如果有启动参数且配置了ownSign throw new UnsupportedOperationException(“在使用元数据保护模式下,不允许设置 [pamirs.distribution.session.ownSign]”); } if (!hasCurrentMetaProtected || isForceProtected()) { writeMetaProtected(metaProtected); } else if (!metaProtected.equals(currentMetaProtected)) { // 如果有启动参数, 且启动参数跟redis值不同 throw unsupportedLocalOperation(); } } else { if (Spider.getDefaultExtension(SessionFillOwnSignApi.class).handleOwnSign()) { return; } // 没有启动参数且redis有值,但没有配置ownSign 报错 throw unsupportedLocalOperation(); } } 取ownSign方式 看header是否有ownSign这个标识 header没有,则从配置里取,并放到header里 ownSign的获取核心代码 CdDistributionSessionFillOwnSignApi @Override public String getCdOwnSign() { String cdOwnSign = null; // 看header是否有ownSign这个标识…

    2024年9月12日
    1.8K00
  • 如何通过自定义支持excel导出的动态表头

    介绍 本文需要阅读过前置文档如何自定义Excel导出功能,动态表头的功能在前置文档的基础上做的进一步扩展,本文未提到的部分都参考这个前置文档。 在日常的业务开发中,我们在导出的场景会遇到需要设置动态表头的场景,比如统计商品在最近1个月的销量,固定表头列为商品的名称等基础信息,动态表头列为最近一个月的日期,在导出的时候设置每个日期的销量,本文将通过此业务场景提供示例代码。 1.自定义导出任务模型 package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.file.api.model.ExcelExportTask; import pro.shushi.pamirs.meta.annotation.Model; @Model.model(DemoItemDynamicExcelExportTask.MODEL_MODEL) @Model(displayName = "商品-Excel动态表头导出任务") public class DemoItemDynamicExcelExportTask extends ExcelExportTask { public static final String MODEL_MODEL = "demo.DemoItemDynamicExcelExportTask"; } 2.自定义导出任务处理数据的扩展点 package pro.shushi.pamirs.demo.core.excel.exportdemo.extPoint; import org.springframework.stereotype.Component; import pro.shushi.pamirs.core.common.FetchUtil; import pro.shushi.pamirs.core.common.cache.MemoryIterableSearchCache; import pro.shushi.pamirs.demo.api.model.DemoItem; import pro.shushi.pamirs.file.api.config.FileConstant; import pro.shushi.pamirs.file.api.context.ExcelDefinitionContext; import pro.shushi.pamirs.file.api.enmu.ExcelTemplateTypeEnum; import pro.shushi.pamirs.file.api.entity.EasyExcelCellDefinition; import pro.shushi.pamirs.file.api.extpoint.impl.ExcelExportSameQueryPageTemplate; import pro.shushi.pamirs.file.api.model.ExcelExportTask; import pro.shushi.pamirs.file.api.model.ExcelWorkbookDefinition; import pro.shushi.pamirs.file.api.util.ExcelFixedHeadHelper; import pro.shushi.pamirs.file.api.util.ExcelHelper; import pro.shushi.pamirs.file.api.util.ExcelTemplateInit; import pro.shushi.pamirs.framework.common.entry.TreeNode; import pro.shushi.pamirs.meta.annotation.ExtPoint; import pro.shushi.pamirs.meta.api.CommonApiFactory; import pro.shushi.pamirs.meta.api.core.orm.ReadApi; import pro.shushi.pamirs.meta.api.core.orm.systems.relation.RelationReadApi; import pro.shushi.pamirs.meta.api.dto.config.ModelConfig; import pro.shushi.pamirs.meta.api.dto.config.ModelFieldConfig; import pro.shushi.pamirs.meta.api.session.PamirsSession; import pro.shushi.pamirs.meta.enmu.TtypeEnum; import pro.shushi.pamirs.meta.util.FieldUtils; import java.util.*; @Component public class DemoItemDynamicExportExtPoint extends ExcelExportSameQueryPageTemplate<DemoItem> implements ExcelTemplateInit { public static final String TEMPLATE_NAME ="商品动态导出"; @Override public List<ExcelWorkbookDefinition> generator() { ExcelFixedHeadHelper excelFixedHeadHelper = ExcelHelper.fixedHeader(DemoItem.MODEL_MODEL,TEMPLATE_NAME) .createBlock(TEMPLATE_NAME, DemoItem.MODEL_MODEL) .setType(ExcelTemplateTypeEnum.EXPORT); return Collections.singletonList(excelFixedHeadHelper.build()); } public static void buildHeader(ExcelFixedHeadHelper excelFixedHeadHelper) { excelFixedHeadHelper.addColumn("name","名称") .addColumn("cateName","类目") .addColumn("searchFrom","搜索来源") .addColumn("description","描述") .addColumn("itemPrice","单价") .addColumn("inventoryQuantity","库存"); } @Override @ExtPoint.Implement(expression = "context.model == \"" + DemoItem.MODEL_MODEL+"\" && context.name == \"" +TEMPLATE_NAME+"\"" ) public List<Object> fetchExportData(ExcelExportTask exportTask, ExcelDefinitionContext context) { List<Object> result = super.fetchExportData(exportTask,context); Object block = result.get(0); if (block instanceof ArrayList) { ((List<Object>) block).forEach(o -> { if (o instanceof DemoItem) { DemoItem item = (DemoItem) o; // TODO 设置动态表头部分字段的值 item.get_d().put("2024-09-10", "1111"); item.get_d().put("2024-09-11", "2222"); }…

    2024年9月11日
    3.8K00

Leave a Reply

登录后才能评论