扩展操作日志字段,实现操作日志界面显示自定义字段

注:该功能在pamirs-core 4.3.27 / 4.7.8.12以上版本可用

  1. 在模块依赖里新增DataAuditModule.MODULE_MODULE模块依赖。

    @Module(
           name = DemoModule.MODULE_NAME,
           dependencies = {
                   CommonModule.MODULE_MODULE,
                   DataAuditModule.MODULE_MODULE
           },
           displayName = "****",
           version = "1.0.0"
    )
  2. 继承OperationBody模型,设置需要在操作日志中显示的字段,并重写clone方法,设置自定义字段值。用于在计入日志处传递参数。

    public class MyOperationBody extends OperationBody {
       public MyOperationBody(String operationModel, String operationName) {
           super(operationModel, operationName);
       }
    
       private String itemNames;
    
       public String getItemNames() {
           return itemNames;
       }
    
       public void setItemNames(String itemNames) {
           this.itemNames = itemNames;
       }
    
       @Override
       public OperationBody clone() {
            //设置自定义字段值
           MyOperationBody body = OperationBody.transfer(this, new MyOperationBody(this.getOperationModel(), this.getOperationName()));
           body.setItemNames(this.getItemNames());
           return body;
       }
    }
  3. 继承OperationLog模型,新增需要在操作日志中显示的字段。用于界面展示该自定义字段。

    @Model.model(MyOperationLog.MODEL_MODEL)
    @Model(displayName = "自定义操作日志", labelFields = {"itemNames"})
    public class MyOperationLog extends OperationLog {
    
       public static final String MODEL_MODEL = "operation.MyOperationLog";
    
       @Field(displayName = "新增日志字段")
       @Field.String
       private String itemNames;
    }
  4. 定义一个常量

    public interface OperationLogConstants {
    
    String MY_SCOPE = "MY_SCOPE";
    }
  5. 在计入日志处,构造出MyOperationBody对象,向该对象中设置自定义日志字段。构造OperationLogBuilder对象并设置scope的值,用于跳转自定义服务实现。

    MyOperationBody body = new MyOperationBody(CustomerCompanyUserProxy.MODEL_MODEL, CustomerCompanyUserProxyDataAudit.UPDATE);
    body.setItemNames("新增日志字段");
    OperationLogBuilder builder = OperationLogBuilder.newInstance(body);
    //设置一个scope,用于跳转自定义服务实现.OperationLogConstants.MY_SCOPE是常量,请自行定义
    builder.setScope(OperationLogConstants.MY_SCOPE);
    //记录日志
    builder.record(data.queryByPk(), data);
  6. 实现OperationLogService接口,加上@SPI.Service()注解,并设置常量,一般为类名。定义scope(注意:保持和计入日志处传入的scope值一致),用于计入日志处找到该自定义服务实现。根据逻辑重写父类中方法,便可以扩展操作日志,实现自定义记录了。

    @Slf4j
    @Service
    @SPI.Service("myOperationLogServiceImpl")
    public class MyOperationLogServiceImpl< T extends D > extends OperationLogServiceImpl< T > implements OperationLogService< T >{
    
        //定义scope,用于计入日志处找到该自定义服务实现
        private static final String[] MY_SCOPE = new String[]{OperationLogConstants.MY_SCOPE};
    
        @Override
        public String[] scopes() {
            return MY_SCOPE;
        }
    
        //此方法用于创建操作日志
        @Override
        protected OperationLog createOperationLog(OperationBody body, OperationLogConfig config) {
           MyOperationBody body1 = (MyOperationBody) body;
           return new MyOperationLog().setItemNames(body1.getItemNames())
                   .setLogConfig(config)
                   .setModel(body.getOperationModel())
                   .setOperationName(body.getOperationName())
                   .setRemark(body.getRemark())
                   .setBusinessId(body.getBusinessId())
                   .setBusinessCode(body.getBusinessCode())
                   .setBusinessName(body.getBusinessName())
                   .setPartnerId(body.getPartnerId())
                   .setPartnerCode(body.getPartnerCode())
                   .setExtId(body.getExtId())
                   .setExtName(body.getExtName())
                   .setOperatorId(ObjectHelper.getOrDefault(body.getOperatorId(), PamirsSession.getUserId()))
                   .setOperatorName(ObjectHelper.getOrDefault(body.getOperatorName(), PamirsSession.getUserName()));
       }
    
    }
    

Oinone社区 作者:yexiu原创文章,如若转载,请注明出处:https://doc.oinone.top/backend/14174.html

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

(0)
yexiu的头像yexiu数式员工
上一篇 2024年6月27日 pm12:09
下一篇 2024年6月28日 am10:28

相关推荐

  • 如何在代码中使用自增ID和获取序列

    在使用继承IDModel或CodeModel时,id和code是系统默认自动生成, 默认值规则:ID–>分布式ID; CODE–>根据定义的SequenceConfig规则自动生成。 在特定情况下需要落库前先生成ID或者Code,这些场景下可参照如下代码示例 一、使用自增ID 单个字段设置方式 // 主键字段,可以使用mysql的自增能力 @Field.Integer @Field.PrimaryKey(keyGenerator = KeyGeneratorEnum.AUTO_INCREMENT) @Field.Advanced(batchStrategy = FieldStrategyEnum.NEVER) @Field(displayName = "id", summary = "Id字段,⾃增") private Long id; @Field.Integer @Field(displayName = "自增版本") @Field.Sequence(sequence = "SEQ", initial = 1) private Long version; 全局设置方式 该方式会作用到每一个存储模型的id字段,在application.yml配置文件中修改id的生成规则,查找配置项关键字key-generator,默认为DISTRIBUTION(分布式id),可修改为 AUTO_INCREMENT(自增id) 二、手动方式获取序列 获取方式示例1 /** * 在特定场景下需要手动生成Id或者code时,可参照这个示例 */ public void manualSetIdCode(){ DemoItem demoItem = new DemoItem(); //手动生成ID和code Object idObj = Spider.getDefaultExtension(IdGenerator.class).generate(PamirsTableInfo.fetchKeyGenerator(DemoItem.MODEL_MODEL)); demoItem.setId(TypeUtils.createLong(idObj)); Object codeObj = CommonApiFactory.getSequenceGenerator().generate("SEQ",DemoItem.MODEL_MODEL); String code = TypeUtils.stringValueOf(codeObj); demoItem.setCode(code); //…… } 获取方式示例2 1、在系统启动的时候初始化SequenceConfig package pro.shushi.pamirs.demo.core.init; import org.springframework.stereotype.Component; import pro.shushi.pamirs.boot.common.api.command.AppLifecycleCommand; import pro.shushi.pamirs.boot.common.extend.MetaDataEditor; import pro.shushi.pamirs.core.common.InitializationUtil; import pro.shushi.pamirs.demo.api.DemoModule; import pro.shushi.pamirs.demo.core.constant.SeqConstants; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.meta.api.dto.meta.Meta; import pro.shushi.pamirs.meta.enmu.SequenceEnum; import java.util.Map; /** * DemoMetadataEditor */ @Slf4j @Component public class DemoMetadataEditor implements MetaDataEditor { @Override public void edit(AppLifecycleCommand command, Map<String, Meta> metaMap) { InitializationUtil util = InitializationUtil.get(metaMap, DemoModule.MODULE_MODULE, DemoModule.MODULE_NAME); if (util == null) { log.error("获取初始化序列失败"); return; } bizSequence(util); } private void bizSequence(InitializationUtil util) { util.createSequenceConfig("申请单编码生成", SeqConstants.NABEL_SAMPLE_APPLY_SEQ, SequenceEnum.ORDERLY_SEQ, 8) .setStep(1) .setInitial(80000000L) .setIsRandomStep(false); util.createSequenceConfig("订单编码生成", SeqConstants.NABEL_SAMPLE_ORDER_SEQ_YP, SequenceEnum.ORDERLY_SEQ, 8) .setPrefix("YP") .setStep(1) .setInitial(80000000L) .setIsRandomStep(false); } } 2、在代码中使用序列 public static String getSaleOrderCode() { Object sequence = CommonApiFactory.getSequenceGenerator().generate(SequenceEnum.ORDERLY_SEQ.value(), SeqConstants.NABEL_SAMPLE_STRUCTURE_SEQ); return TypeUtils.stringValueOf(sequence); } public static String getApplyOrderCode(String prefix) { Object sequence = CommonApiFactory.getSequenceGenerator().generate(SequenceEnum.ORDERLY_SEQ.value(), SeqConstants.NABEL_SAMPLE_APPLY_SEQ); return…

    2024年5月25日
    1.9K00
  • 工作流审核撤回/回退/拒绝/同意/反悔钩子使用

    目录 1. 流程撤回、拒绝和回退调用自定义函数1.1 工作流【撤销】回调钩子1.2 撤销【回退】回调钩子1.3 工作流【拒绝】回调钩子1.4 工作流【同意】回调钩子1.4 工作流【反悔】回调钩子1.4 回调钩子在业务系统中的调用示例2. 自定义审批方式、自定义审批节点名称 1.流程撤回、拒绝和回退调用自定义函数 1.1工作流【撤销】回调钩子 使用方式:把该方法放置到XXX模型的Action下面,或@Fun(XXX.MODEL_MODEL)触发方式:当流程实例被撤销时调用入口:pro.shushi.pamirs.workflow.app.core.service.impl.WorkflowInstanceServiceImpl#undoInstance /** * XXX为当前流程触发方式为模型触发时对应的触发模型、 * 对应返回不影响流程上下文 * @param data 入参为触发时的业务数据,数据的JsonString * @return */ @Function public XXX recall(String data) { // TODO: 根据实际的业务逻辑把data转换为对象 WorkRecord workRecord = JsonUtils.parseObject(data, new TypeReference<WorkRecord>(){}); // TODO: 增加自定义业务逻辑 return new XXX(); } 1.2撤销【回退】回调钩子 使用方式:把该方法放置到XXX模型的Action下面,或@Fun(XXX.MODEL_MODEL)触发方式:流程待办进行回退操作时调用入口:pro.shushi.pamirs.workflow.app.core.service.operator.ApprovalFallbackOperatorService /** * XXX为当前流程触发方式为模型触发时对应的触发模型 * 对应返回不影响流程上下文 * @param data 入参为触发时的业务数据,数据的JsonString * @return */ @Function public XXX fallBack(String data) { // TODO: 根据实际的业务逻辑把data转换为对象 WorkRecord workRecord = JsonUtils.parseObject(data, new TypeReference<WorkRecord>(){}); // TODO: 增加自定义业务逻辑 return new XXX(); } 1.3工作流【拒绝】回调钩子 使用方式:把该方法放置到XXX模型的Action下面,或@Fun(XXX.MODEL_MODEL)触发方式:流程待办进行拒绝操作时调用入口:pro.shushi.pamirs.workflow.app.core.service.operator.ApprovalFallbackOperatorService /** * XXX为当前流程触发方式为模型触发时对应的触发模型 * 对应返回不影响流程上下文 * @param data 入参为触发时的业务数据,数据的JsonString * @return */ @Function public XXX reject(String data) { // TODO: 根据实际的业务逻辑把data转换为对象 WorkRecord workRecord = JsonUtils.parseObject(data, new TypeReference<WorkRecord>(){}); // TODO: 增加自定义业务逻辑 return new XXX(); } 1.4 工作流【同意】回调钩子 使用方式:把该方法放置到XXX模型的Action下面,或@Fun(XXX.MODEL_MODEL)触发方式:流程待办进行同意操作时调用入口:pro.shushi.pamirs.workflow.app.core.util.ArtificialTaskUtils @Function(summary = "发起的审批同意时会自动调用此方法") @Function.Advanced(displayName = "审批同意") public Teacher agree(String data) { // TODO: 根据实际的业务逻辑把data转换为对象 // WorkRecord workRecord = JsonUtils.parseObject(data, new TypeReference<WorkRecord>(){}); // TODO: 增加自定义业务逻辑 return new Teacher(); } 1.4 工作流【反悔】回调钩子 使用方式:把该方法放置到XXX模型的Action下面,或@Fun(XXX.MODEL_MODEL)触发方式:流程待办进行反悔操作时使用场景:流程待办进行反悔操作时,需要额外更改其他的业务数据逻辑时可用该回调钩子。 注意:该函数的namespace需要设置为流程触发模型。 调用入口:pro.shushi.pamirs.workflow.app.core.service.operator.ArtificialRetractOperatorService @Function @Function.fun(WorkflowBizCallConstants.retract) public void retract(WorkflowUserTask workflowUserTask) { // 获取流程实例 workflowUserTask.fieldQuery(WorkflowUserTask::getInstance); WorkflowInstance instance = workflowUserTask.getInstance(); // 获取用户任务实例 WorkflowUserInstance userInstance = new WorkflowUserInstance() .setId(workflowUserTask.getWorkflowUserInstanceId()) .queryById(); // 反悔的用户id…

    2023年11月15日
    1.4K00
  • 全局首页及应用首页配置方法(homepage)

    1 Oinone平台首页介绍 1.1 首页包括全局首页和应用首页两类 全局首页:指用户在登录时未指定重定向地址的情况下使用的应用首页 应用首页:指用户在切换应用时使用的首页 PS:全局首页本质上也是应用首页,是在用户没有指定应用时使用的首页。如登录后。 1.2 全局首页查找规则 找到当前用户有权限访问的全部应用。 若使用AppConfig配置首页,则优先使用该配置作为全局首页。若未指定或无权限访问,则继续第3步。 依次按照应用优先级,获取有权限的首页或菜单作为全局首页。 若未查找到任何可访问页面,则提示无权限访问相关异常,用户无法进入平台。 1.3 应用首页查找规则 在指定应用下,获取有权限的首页或菜单作为应用首页。 若未查找到任何可访问页面,则提示无权限访问相关异常,用户进入应用后无法正常查看或操作。 2 配置全局首页 2.1 使用应用优先级设置全局首页 /** * 演示模块 * * @author Adamancy Zhang at 16:55 on 2024-03-24 */ @UxHomepage(@UxRoute(DemoDepartment.MODEL_MODEL)) @Component @Boot @Module( name = DemoModule.MODULE_NAME, displayName = "演示应用", version = "1.0.0", dependencies = {ModuleConstants.MODULE_BASE}, priority = 0 ) @Module.module(DemoModule.MODULE_MODULE) @Module.Advanced(selfBuilt = true) public class DemoModule implements PamirsModule { public final static String MODULE_MODULE = "demo"; public final static String MODULE_NAME = "demo"; @Override public String[] packagePrefix() { return new String[]{"pro.shushi.pamirs.demo"}; } } 注意事项 @UxHomepage用于指定应用首页 @Module#priority用于指定模块优先级,按升序排列 PS:下面描述的内容不再提供完整的模块定义内容,仅针对@UxHomepage进行介绍。 3 配置应用首页 3.1 使用@UxHomepage配置应用首页 指定模型的默认表格视图作为应用首页 @UxHomepage(@UxRoute(DemoDepartment.MODEL_MODEL)) 该指定方式将产生以下结果: 生成一个跳转动作(ViewAction),其模型编码为DemoDepartment.MODEL_MODEL,动作名称为homepage。 设置ModuleDefinition#homePageModel和ModuleDefinition#homePageName为该跳转动作。 指定模型对应的菜单作为应用首页 在当前应用下有如下菜单定义: /** * 演示模块菜单 * * @author Adamancy Zhang at 17:16 on 2024-03-24 */ @UxMenus public class DemoMenus { @UxMenu("演示部门") @UxRoute(DemoDepartment.MODEL_MODEL) class DepartmentManagement { } @UxMenu("演示员工") @UxRoute(DemoEmployee.MODEL_MODEL) class EmployeeManagement { } } 根据菜单定义我们可以知道: 演示部门这个菜单会生成一个跳转动作(ViewAction),其模型编码为DemoDepartment.MODEL_MODEL,动作名称为DemoMenus_DepartmentManagement。 因此,我们可以使用如下方式指定应用首页为演示部门这个菜单: @UxHomepage(actionName = "DemoMenus_DepartmentManagement", value = @UxRoute(DemoDepartment.MODEL_MODEL)) 4 在应用中心修改应用首页 在平台启动之后,将无法通过代码的方式修改首页,因此需要在应用中心修改应用首页。 按照如下图所示操作对应用首页进行设置。 在绑定菜单选项中,选择指定菜单即可。

    2024年3月24日
    1.6K00
  • 元数据表介绍

    模型 模型元数据的讲解 https://doc.oinone.top/oio4/9281.html base_model 模型表 字段名 备注 示例 system_source BASE是系统创建, MANUAL是人工创建 MANUAL pk 主键 id module 模块编码 demo_core model 模型编码 demo.PetType name api名称 petType lname 模型代码名称 pro.shushi.pamirs.demo.api.model.PetType table 逻辑数据表名称 demo_core_pet_type ds_key 逻辑数据源名 pamirs type 模型类型 store display_name 显示名称 品种 data_manager 是否允许系统根据模型变化自动创建表和更新表 1 ordering 排序 createDate DESC, id DESC super_models 父模型 demo.AbstractDemoIdModel uniques 唯一索引 indexes 索引 name,createDate 模块 模块元数据的讲解 https://doc.oinone.top/oio4/9279.html base_module 模块表 字段名 备注 示例 display_name 显示名称 OinoneDemo name api名称 DemoCore module 模块编码 demo_core module_dependencies 依赖模块编码列表 base,common,file,trigger module_exclusions 互斥模块编码列表 module_upstreams 上游模块编码列表 system_source BASE是系统创建, MANUAL是人工创建 MANUAL web web应用 1 default_home_page_model 默认主页模型编码 函数 函数元数据的讲解 https://doc.oinone.top/oio4/9282.html base_function 函数表 字段名 备注 示例 display_name 显示名称 根据条件分页查询记录列表和总数 clazz 函数位置 pro.shushi.pamirs.framework.orm.DefaultReadApi module 模块 demo_core method 函数方法 queryPage namespace 函数命名空间 demo.PetType argument_list 函数参数 [{"ltype":"pro.shushi.pamirs.meta.api.dto.condition.Pagination","model":"base.Pagination","modelGeneric":false,"multi":false,"name":"page","ttype":"m2o"},{"ltype":"pro.shushi.pamirs.meta.api.dto.wrapper.IWrapper","ltypeT":"java.lang.Object","model":"base.Condition","modelGeneric":true,"multi":false,"name":"queryWrapper","ttype":"m2o"}] fun 函数编码 queryPage return_type 返回值类型 {"ltype":"pro.shushi.pamirs.meta.api.dto.condition.Pagination","model":"base.Pagination","modelGeneric":false,"multi":false,"ttype":"m2o"} sys 由系统产生的元数据 1 type 函数类型 1: CREATE, 2: DELETE, 4: UPDATE, 8: QUERY 8 data_manager 数据管理器函数 1 codes 代码内容 open_level 开放级别 2: LOCAL, 4: REMOTE, 8: API, 6: LOCAL+REMOTE, 10: LOCAL+API, 12: REMOTE+API, 14:LOCAL+REMOTE+API 14 模型字段 字段讲解 https://doc.oinone.top/oio4/9239.html base_field 字段表 字段名 备注 示例 system_source BASE是系统创建, MANUAL是人工创建 MANUAL name api名称 name field 字段编码 name ttype 关系类型, 类型:m2o/o2m/m2m/enum/string/integer/map/datetime/related/money/html string model 模型编码…

    2024年8月23日
    1.1K00
  • 【Excel导入/导出】多Sheet导入导出示例(4.7.x及以上版本)

    代码示例 示例仅供参考 点击下载代码示例 业务场景 准备工作:两个模型,物料(Material)和物料类别(MaterialCategory)。目标:在一个Excel模板中同时导入和导出两个模型的数据。 Material模型 @Model.model(Material.MODEL_MODEL) @Model.Advanced(unique = {"code"}) @Model(displayName = "物料", labelFields = {"name"}) public class Material extends IdModel { private static final long serialVersionUID = -2594216864389636135L; public static final String MODEL_MODEL = "maas.Material"; @Field.String @Field(displayName = "物料编码", required = true) private String code; @Field.String @Field(displayName = "物料名称", required = true) private String name; } MaterialCategory模型 @Model.model(MaterialCategory.MODEL_MODEL) @Model.Advanced(unique = {"code"}) @Model(displayName = "物料类别", labelFields = {"name"}) public class MaterialCategory extends IdModel { private static final long serialVersionUID = 6300896634558908349L; public static final String MODEL_MODEL = "maas.MaterialCategory"; @Field.String @Field(displayName = "类别编码", required = true) private String code; @Field.String @Field(displayName = "类别名称", required = true) private String name; } 模板定义 MaterialTemplate @Component public class MaterialTemplate implements ExcelTemplateInit { public static final String TEMPLATE_NAME = "materialTemplate"; @Override public List<ExcelWorkbookDefinition> generator() { WorkbookDefinitionBuilder builder = WorkbookDefinitionBuilder.newInstance(Material.MODEL_MODEL, TEMPLATE_NAME) .setDisplayName("物料和物料类别") .setEachImport(Boolean.FALSE);//设置importData的入参为 (ExcelImportContext importContext, List<MaterialCategory> data),如入参是单个对象,请删除setEachImport(Boolean.FALSE) createMaterialSheet(builder); createMaterialCategorySheet(builder); return Collections.singletonList(builder.build()); } private static void createMaterialSheet(WorkbookDefinitionBuilder builder) { builder.createSheet().setName("物料") .createBlock(Material.MODEL_MODEL, ExcelAnalysisTypeEnum.FIXED_HEADER, ExcelDirectionEnum.HORIZONTAL, "A1:B2") .createHeader().setStyleBuilder(ExcelHelper.createDefaultStyle()).setIsConfig(Boolean.TRUE) .createCell().setField("code").setAutoSizeColumn(Boolean.TRUE).and() .createCell().setField("name").setAutoSizeColumn(Boolean.TRUE).and() .and() .createHeader().setStyleBuilder(ExcelHelper.createDefaultStyle(v -> v.setBold(Boolean.TRUE)).setHorizontalAlignment(ExcelHorizontalAlignmentEnum.CENTER)) .createCell().setValue("物料编码").and() .createCell().setValue("物料名称"); } private static void createMaterialCategorySheet(WorkbookDefinitionBuilder builder) { builder.createSheet().setName("物料类别") .createBlock(MaterialCategory.MODEL_MODEL, ExcelAnalysisTypeEnum.FIXED_HEADER, ExcelDirectionEnum.HORIZONTAL, "A1:B2") .createHeader().setStyleBuilder(ExcelHelper.createDefaultStyle()).setIsConfig(Boolean.TRUE) .createCell().setField("code").setAutoSizeColumn(Boolean.TRUE).and()…

    2024年4月23日
    1.2K00

Leave a Reply

登录后才能评论