代码示例:快速找到示例代码

1、图表设计器

数据可视化能加载到的接口,方法上需要加 category = FunctionCategoryEnum.QUERY_PAGE

@Model.model(ExpensesIncome.MODEL_MODEL)
@Component
@Slf4j
public class ExpensesIncomeAction {

    @Function.Advanced(type = FunctionTypeEnum.QUERY,category = FunctionCategoryEnum.QUERY_PAGE)
    @Function.fun(FunctionConstants.queryPage)
    @Function(openLevel = {FunctionOpenEnum.API})
    public Pagination<ExpensesIncome> queryPage(Pagination<ExpensesIncome> page, IWrapper<ExpensesIncome> queryWrapper) {
        page = new ExpensesIncome().queryPage(page, queryWrapper);

        if (page!=null && CollectionUtils.isNotEmpty(page.getContent())) {
            page.getContent().forEach(a->{
                if (a.getBudgetInCome()!=null && a.getBudgetInCome().compareTo(new BigDecimal("0.0"))!=0) {
                    if (a.getRetailAmount()!=null) {
                        a.setInComeRate(a.getRetailAmount().divide(a.getBudgetInCome(),2));
                    }
                }
            });
        }

        return page;
    }
}

2、事务支持

1)对于单个系统(InJvm)/模型内部采用强事务的方式。比如:在库存转移时,库存日志和库存数量的变化在一个事务中,保证两个表的数据同时成功或者失败。
2)强事务管理采用编码式,Oinone事务管理兼容Spring的事务管理方式;有注解的方式和代码块的方式。
a) 使用注解的方式:

    @Override
    @Transactional
    public void awardOut(AwardBookOutEmpRecordDetail detail) {
        List<OutEmpRecordSubjectDetail> subjectDetails = detail.getSubjectDetails();
        ……
    }

重要说明
基于注解的事务,Bean和加注解的方法必须能被Spring切面到。

b) 使用编码方式:

  //组装数据/获取数据/校验
  Tx.build(new TxConfig()).executeWithoutResult(status -> {
      // 1、保存部门数据
      deliDepartmentService.createOrUpdateBatch(departments);

      //2、如果父进行了下级关联设置,处理下级关联设置的逻辑
      for (DeliDepartment department : departments) {
          doDepartSubLink(department);
      }
  });
  …………

3)对于分布式事务采用最终数据一致性,借助【可靠消息】或者【Job】的方式来实现。

3、平台工具类使用

3.1 FetchUtil

pro.shushi.pamirs.core.common.FetchUtil,重点关注

# 根据Id查询列表
pro.shushi.pamirs.core.common.FetchUtil#fetchMapByIds

# 根据Id查询,返回Map
pro.shushi.pamirs.core.common.FetchUtil#fetchListByIds

# 根据Codes查询列表
pro.shushi.pamirs.core.common.FetchUtil#fetchMapByCodes

# 根据Code查询,返回Map
pro.shushi.pamirs.core.common.FetchUtil#fetchListByCodes

# 根据Entity查询
pro.shushi.pamirs.core.common.FetchUtil#fetchOne

其他更多方法参考这个类的源代码

3.2 ListUtils

pro.shushi.pamirs.meta.common.util.ListUtils

# 把输入的List,按给定的长度进行分组
pro.shushi.pamirs.meta.common.util.ListUtils#fixedGrouping

# 从给定的List中返回特定字段的数组,忽略Null并去重
pro.shushi.pamirs.meta.common.util.ListUtils#transform

4、枚举获取

根据枚举的name获取value
Integer value = ArchivesConfigTypeEnum.getValueByName(ArchivesConfigTypeEnum.class,“status1”);
根据枚举的name获取枚举项
ArchivesConfigTypeEnum typeEnum = TtypeEnum.getEnum(ArchivesConfigTypeEnum.class, “status1”);

5、Map类型的数据怎么定义,即ttype 为map的字段写法

@Field(displayName = "上下文", store = NullableBoolEnum.TRUE, serialize = JSON)
@Field.Advanced(columnDefinition = "TEXT")
private Map<String, Object> context;

6、后端获取前端请求中的variables

PamirsRequestVariables requestVariables = PamirsSession.getRequestVariables();

7、为什么有时候调用服务走远程了

1、假设:
A模型(modelA)所在的模块(ModuleA)
B模型(modelB)所在的模块(ModuleB)

部署方式决定调用方式:
1)部署方式1:
ModuleA 和 ModuleB 部署在一个jvm中,此时:
modelA的服务/页面去调用 modelB,因为部署在一起,直接走inJvm

2、部署方式2:
ModuleA 和 ModuleB 部署在不同的jvm中;
modelA的服务/页面去调用 modelB,则会走远程(RPC);此时需要:
a)ModuleB 需要开启dubbo服务,且ModuleA 和 ModuleB注册中心相同
b)ModuleB 对应的方法需要暴露dubbo接口,自定义service需要增加@Function注解

8、流程拒绝和回退调用自定义函数

1)、工作流撤销的自定义函数

@Function
public XXX recall(String data) {    
    return new XXX();
} 

2)、撤销回退调用的自定义函数
调用入口:
pro/shushi/pamirs/workflow/app/core/service/operator/ApprovalFallbackOperatorService.java:181

@Function
public XXX fallBack(String data) {
    return new XXX();
}

3)、工作流拒绝之后回调钩子

@Function
public XXX reject((String data) {    
    return new XXX();
} 

9、启动后访问页面报枚举项不存在

现象:枚举项不存在, model:base.ViewAction, name:target,value:ROUTER

pro.shushi.pamirs.meta.common.exception.PamirsException: 枚举项不存在, model:base.ViewAction, name:target,value:ROUTER
    at pro.shushi.pamirs.meta.common.exception.PamirsException$Builder.errThrow(PamirsException.java:173) ~[pamirs-meta-common-4.6.6.jar:na]
    at pro.shushi.pamirs.framework.orm.converter.entity.handler.EnumNamedHandler.lambda$dealEnum$4(EnumNamedHandler.java:149) ~[pamirs-framework-orm-4.6.8.jar:na]
    at pro.shushi.pamirs.framework.orm.converter.multi.MultiValueStrategy.submit(MultiValueStrategy.java:59) ~[pamirs-framework-orm-4.6.8.jar:na]
    at pro.shushi.pamirs.framework.orm.converter.multi.MultiValueStrategy.submit(MultiValueStrategy.java:23) ~[pamirs-framework-orm-4.6.8.jar:na]
    at pro.shushi.pamirs.framework.orm.converter.entity.handler.EnumNamedHandler.dealEnum(EnumNamedHandler.java:86) ~[pamirs-framework-orm-4.6.8.jar:na]
    at pro.shushi.pamirs.framework.orm.converter.entity.handler.EnumNamedHandler.stringify(EnumNamedHandler.java:66) ~[pamirs-framework-orm-4.6.8.jar:na]
    at pro.shushi.pamirs.framework.orm.converter.entity.handler.EnumHandler.stringify(EnumHandler.java:57) ~[pamirs-framework-orm-4.6.8.jar:na]
    at pro.shushi.pamirs.framework.orm.client.converter.entity.ClientEnumConverter.out(ClientEnumConverter.java:30) ~[pamirs-framework-orm-client-4.6.8.jar:na]
    at pro.shushi.pamirs.framework.orm.client.converter.processor.ClientTypeProcessor.lambda$out$6(ClientTypeProcessor.java:71) ~[pamirs-framework-orm-client-4.6.8.jar:na]
    at pro.shushi.pamirs.meta.common.enmu.BaseEnum.switchConsume(BaseEnum.java:717) ~[pamirs-meta-common-4.6.6.jar:na]
    at pro.shushi.pamirs.meta.common.enmu.BaseEnum.switch0(BaseEnum.java:697) ~[pamirs-meta-common-4.6.6.jar:na]
    at pro.shushi.pamirs.meta.common.enmu.BaseEnum.switches(BaseEnum.java:638) ~[pamirs-meta-common-4.6.6.jar:na]

问题原因:
Oinone重写了GraphQL的枚举转换类,报错是没有加载到这个自定义类

解决方法
在boot工程的pom的最前面引用如下的jar,重新发布即可

<dependency>
  <groupId>pro.shushi.pamirs</groupId>
  <artifactId>a</artifactId>
</dependency>

10、配置日志文件

现在启动输出到out.log应该是启动脚本中定向输出了(>>)

不定向输出,采用自己配置的方式,与标准的SpringBoot工程配置日志一样
两种方式(都是Spring提供的方式):
1、bootstrap.yml 里面可以按profiles指定logback的配置文件,例如:
logging:
config: classpath:logback-pre.xml
具体文件名和文件输入在logback里面进行配置,跟通用的logback配置一致

2、resources的根目录,直接配置 logback-spring.xml, 启动会自动加载

11、自定义controller如何获取用户信息

    private PamirsUser fetchUserByRequest() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String sessionId = CookieUtil.getValue(request, pro.shushi.pamirs.framework.common.utils.CookieUtil.USER_SESSION_ID);
        PamirsUserDTO pamirsUserDTO = UserCache.get(sessionId);
        if (pamirsUserDTO!=null) {
            // 根据用户ID获取完整的用户信息
           return UserInfoCache.queryUserById(pamirsUserDTO.getUserId()); 
        } else {
            throw PamirsException.construct(BaseExpEnumerate.BASE_USER_NOT_LOGIN_ERROR).errThrow();
        }
    }

12、查询设置不排序

查询的page和Wrapper可设置setSortable(Boolean.FALSE),来实现查询不需要排序的场景

示例1:page中设置不排序

page.setSortable(Boolean.FALSE);
page = new PetShop().queryPage(page, queryWrapper);

示例2:Wrapper中设置不排序

IWrapper wrapper = Pops.lambdaQuery()
    .from(PetShop.MODEL_MODEL).setBatchSize(-1).setSortable(Boolean.FALSE);
List petShops4 = new PetShop().queryList(wrapper);

持续补充中。。。。。

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

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

(1)
shao的头像shao数式管理员
上一篇 2024年2月20日 pm6:52
下一篇 2024年2月20日 pm7:15

相关推荐

  • 动态视图支持权限配置

    动态页面在前端自定义之后,目前是没办法通过权限去控制路由页面的权限的,本篇文章将介绍如何解决这个问题。阅读本篇文章之前,应已经实现自定义页面渲染动态视图。参考文章:自定义视图内部渲染动态视图 实现思路: 通过自定义页面里设计的组件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日
    75900
  • 非存储字段搜索,适应灵活的搜索场景

    1、非存储字段搜索 1.1 描述 通常根据本模型之外的信息作为搜索条件时,通常会把 这些字段放在代理模型上。这类场景我们称之为 非存储字段搜索 1.2 场景一 非存储字段为基本的String。1.代码定义:非存储字段为基本的包装数据类型 @Field(displayName = "确认密码", store = NullableBoolEnum.FALSE) private String confirmPassword; 2.设计器拖拽:列表中需要有退拽这个字段,字段是否隐藏的逻辑自身业务是否需要决定。3.页面通过非存储字段为基本的包装数据类型进行搜索时。会拼在 queryWrapper 的属性queryData中,queryData为Map,key为字段名,value为搜索值。4.后台逻辑处理代码示例: Map<String, Object> queryData = queryWrapper.getQueryData(); if (null != queryData) { Object productIdObj = queryData.get(PRODUCT_ID); if (Objects.nonNull(productIdObj)) { String productId = productIdObj.toString(); queryWrapper.lambda().eq(MesProduceOrderProxy::getProductId, productId); } } 1.3 场景二 非存储字段为非存储对象。 定义 为非存储的 @Field(displayName = "款", store = NullableBoolEnum.FALSE) @Field.many2one @Field.Relation(store = false) private MesProduct product; 2.页面在搜索栏拖拽非存储字段作为搜索条件 3.后台逻辑处理代码示例: try { if (null != queryData && !queryData.isEmpty()) { List<Long> detailId = null; BasicSupplier supplier = JsonUtils.parseMap2Object((Map<String, Object>) queryData.get(supplierField), BasicSupplier.class); MesProduct product = JsonUtils.parseMap2Object((Map<String, Object>) queryData.get(productField), MesProduct.class); MesMaterial material = JsonUtils.parseMap2Object((Map<String, Object>) queryData.get(materialField), MesMaterial.class); if (supplier != null) { detailId = bomService.queryBomDetailIdBySupplierId(supplier.getId()); if (CollectionUtils.isEmpty(detailId)) { detailId.add(-1L); } } if (product != null) { List<Long> produceOrderId = produceOrderService.queryProductOrderIdByProductIds(product.getId()); if (CollectionUtils.isNotEmpty(produceOrderId)) { queryWrapper.lambda().in(MesProduceBomSizes::getProduceOrderId, produceOrderId); } } if (material != null) { //找出两个bom列表的并集 List<Long> materBomDetailId = bomService.queryBomDetailIdByMaterialId(material.getId()); if (CollectionUtils.isNotEmpty(detailId)) { detailId = detailId.stream().filter(materBomDetailId::contains).collect(Collectors.toList()); } else { detailId = new ArrayList<>(); if (CollectionUtils.isEmpty(materBomDetailId)) { detailId.add(-1L); } else { detailId.addAll(materBomDetailId); } } } if (CollectionUtils.isNotEmpty(detailId)) { queryWrapper.lambda().in(MesProduceBomSizes::getProductBomId, detailId); } } } catch (Exception e) { log.error("queryData处理异常", e); } 注意:…

    2024年2月20日
    1.3K00
  • 自定义表格支持合并或列、表头分组

    本文将讲解如何通过自定义实现表格支持单元格合并和表头分组。 点击下载对应的代码 在学习该文章之前,你需要先了解: 1: 自定义视图2: 自定义视图、字段只修改 UI,不修改数据和逻辑3: 自定义视图动态渲染界面设计器配置的视图、动作 1. 自定义 widget 创建自定义的 MergeTableWidget,用于支持合并单元格和表头分组。 // MergeTableWidget.ts import { BaseElementWidget, SPI, ViewType, TableWidget, Widget, DslRender } from '@kunlun/dependencies'; import MergeTable from './MergeTable.vue'; @SPI.ClassFactory( BaseElementWidget.Token({ viewType: ViewType.Table, widget: 'MergeTableWidget' }) ) export class MergeTableWidget extends TableWidget { public initialize(props) { super.initialize(props); this.setComponent(MergeTable); return this; } /** * 表格展示字段 */ @Widget.Reactive() public get currentModelFields() { return this.metadataRuntimeContext.model.modelFields.filter((f) => !f.invisible); } /** * 渲染行内动作VNode */ @Widget.Method() protected renderRowActionVNodes() { const table = this.metadataRuntimeContext.viewDsl!; const rowAction = table?.widgets.find((w) => w.slot === 'rowActions'); if (rowAction) { return rowAction.widgets.map((w) => DslRender.render(w)); } return null; } } 2. 创建对应的 Vue 组件 定义一个支持合并单元格与表头分组的 Vue 组件。 <!– MergeTable.vue –> <template> <vxe-table border height="500" :column-config="{ resizable: true }" :merge-cells="mergeCells" :data="showDataSource" @checkbox-change="checkboxChange" @checkbox-all="checkedAllChange" > <vxe-column type="checkbox" width="50"></vxe-column> <!– 渲染界面设计器配置的字段 –> <vxe-column v-for="field in currentModelFields" :key="field.name" :field="field.name" :title="field.label" ></vxe-column> <!– 表头分组 https://vxetable.cn/v4.6/#/table/base/group –> <vxe-colgroup title="更多信息"> <vxe-column field="role" title="Role"></vxe-column> <vxe-colgroup title="详细信息"> <vxe-column field="sex" title="Sex"></vxe-column> <vxe-column field="age" title="Age"></vxe-column> </vxe-colgroup> </vxe-colgroup> <vxe-column title="操作" width="120"> <template #default="{ row, $rowIndex }"> <!– 渲染界面设计器配置的行内动作 –> <row-action-render :renderRowActionVNodes="renderRowActionVNodes" :row="row" :rowIndex="$rowIndex" :parentHandle="currentHandle" ></row-action-render> </template> </vxe-column> </vxe-table> <!– 分页 –> <oio-pagination :pageSizeOptions="pageSizeOptions" :currentPage="pagination.current"…

    2025年1月9日
    1.4K00
  • 如何使用位运算的数据字典

    场景举例 日常有很多项目,数据库中都有表示“多选状态标识”的字段。在这里用我们项目中的一个例子进行说明一下: 示例一: 表示某个商家是否支持多种会员卡打折(如有金卡、银卡、其他卡等),项目中的以往的做法是:在每条商家记录中为每种会员卡建立一个标志位字段。如图: 用多字段来表示“多选标识”存在一定的缺点:首先这种设置方式很明显不符合数据库设计第一范式,增加了数据冗余和存储空间。再者,当业务发生变化时,不利于灵活调整。比如,增加了一种新的会员卡类型时,需要在数据表中增加一个新的字段,以适应需求的变化。  – 改进设计:标签位flag设计二进制的“位”本来就有表示状态的作用。可以用各个位来分别表示不同种类的会员卡打折支持:这样,“MEMBERCARD”字段仍采用整型。当某个商家支持金卡打折时,则保存“1(0001)”,支持银卡时,则保存“2(0010)”,两种都支持,则保存“3(0011)”。其他类似。表结构如图: 我们在编写SQL语句时,只需要通过“位”的与运算,就能简单的查询出想要数据。通过这样的处理方式既节省存储空间,查询时又简单方便。 //查询支持金卡打折的商家信息:   select * from factory where MEMBERCARD & b'0001'; // 或者:   select * from factory where MEMBERCARD & 1;    // 查询支持银卡打折的商家信息:   select * from factory where MEMBERCARD & b'0010'; // 或者:   select * from factory where MEMBERCARD & 2; 二进制( 位运算)枚举 可以通过@Dict注解设置数据字典的bit属性或者实现BitEnum接口来标识该枚举值为2的次幂。二进制枚举最大的区别在于值的序列化和反序列化方式是不一样的。 位运算的枚举定义示例 import pro.shushi.pamirs.meta.annotation.Dict; import pro.shushi.pamirs.meta.common.enmu.BitEnum; @Dict(dictionary = ClientTypeEnum.DICTIONARY, displayName = "客户端类型枚举", summary = "客户端类型枚举") public enum ClientTypeEnum implements BitEnum { PC(1L, "PC端", "PC端"), MOBILE(1L << 1, "移动端", "移动端"), ; public static final String DICTIONARY = "base.ClientTypeEnum"; private final Long value; private final String displayName; private final String help; ClientTypeEnum(Long value, String displayName, String help) { this.value = value; this.displayName = displayName; this.help = help; } @Override public Long value() { return value; } @Override public String displayName() { return displayName; } @Override public String help() { return help; } } 使用方法示例 API: addTo 和 removeFrom List<ClientTypeEnum> clientTypes = module.getClientTypes(); // addTo ClientTypeEnum.PC.addTo(clientTypes); // removeFrom ClientTypeEnum.PC.removeFrom(clientTypes); 在查询条件中的使用 List<Menu> moduleMenus = new Menu().queryListByWrapper(menuPage, LoaderUtils.authQuery(wrapper).eq(Menu::getClientTypes, ClientTypeEnum.PC));

    2023年11月24日
    1.6K00
  • 蓝绿发布

    背景 应用程序升级面临最大挑战是新旧业务切换,将软件从测试的最后阶段带到生产环境,同时要保证系统不间断提供服务。长期以来,业务升级渐渐形成了几个发布策略:蓝绿发布、灰度发布和滚动发布,目的是尽可能避免因发布导致的流量丢失或服务不可用问题。 本文主要介绍Oinone平台如何实现蓝绿发布。蓝绿发布:项目逻辑上分为AB组,在项目系统时,首先把A组从负载均衡中摘除,进行新版本的部署。B组仍然继续提供服务。 需求 统一权限统一登录信息不同业务数据 实现方案 首先需要两个环境并统一流量入口,这里使用Nginx配置负载均衡:nginx如何配置后端服务的负载均衡 统一权限配置 在蓝绿环境添加不同的redis前缀 spring: redis: prefix: xxx 在蓝绿环境的修改AuthRedisTemplate Bean,利用setKeySerializer去掉redis前缀。 可以使用@Profile注解指定仅线上环境生效。 @Configuration // @Profile("prod") public class AuthRedisTemplateConfig { @Bean(AuthConstants.REDIS_TEMPLATE_BEAN_NAME) public AuthRedisTemplate<?> authRedisTemplate( RedisConnectionFactory redisConnectionFactory, PamirsStringRedisSerializer pamirsStringRedisSerializer ) { AuthRedisTemplate<Object> template = new AuthRedisTemplate<Object>(redisConnectionFactory, pamirsStringRedisSerializer) { @Override public void afterPropertiesSet() { // 重写 key serializer,去掉前缀隔离 this.setKeySerializer(new PamirsStringRedisSerializer(null)); super.afterPropertiesSet(); } }; return template; } } 统一登录 在蓝绿环境自定义实现pro.shushi.pamirs.user.api.spi.UserCacheApi SPI,去除redis前缀 package pro.shushi.pamirs.top.core.spi; import com.alibaba.fastjson.JSON; import com.google.common.collect.Sets; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.annotation.Order; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.core.SessionCallback; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import pro.shushi.pamirs.auth.api.cache.redis.AuthRedisTemplate; import pro.shushi.pamirs.meta.api.dto.model.PamirsUserDTO; import pro.shushi.pamirs.meta.api.dto.protocol.PamirsRequestVariables; import pro.shushi.pamirs.meta.api.session.PamirsSession; import pro.shushi.pamirs.meta.common.spi.SPI; import pro.shushi.pamirs.user.api.cache.UserCache; import pro.shushi.pamirs.user.api.configure.UserConfigure; import pro.shushi.pamirs.user.api.spi.UserCacheApi; import java.net.URI; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; @Order(1) @Component @SPI.Service("MyUserCache") public class MyUserCache implements UserCacheApi { private static final Set<String> DEFAULT_FILTER_URIS = Collections.singleton("/pamirs/message"); @Autowired private AuthRedisTemplate redisTemplate; @Override public PamirsUserDTO getSessionUser(String key) { String objectValue = getUserCacheAndRenewed(key); if (StringUtils.isNotBlank(objectValue)) { return JSON.parseObject(objectValue, PamirsUserDTO.class); } return null; } @Override public void setSessionUser(String key, PamirsUserDTO user, Integer expire) { user.setPassword(null); expire = getExpire(expire); redisTemplate.opsForValue().set(key.replace("'", " "), JSON.toJSONString(user), expire, TimeUnit.SECONDS); // 当前的实现是一个user可以在多个客户端登录,需要在管理端修改user权限后强制清除掉该用户已登录的session,所以需要记录uid对应所有已登录的sessionId String userRelSessionKey = UserCache.createUserRelSessionKey(user.getUserId()); redisTemplate.opsForSet().add(userRelSessionKey, key); redisTemplate.expire(userRelSessionKey,…

    2025年9月18日
    56400

Leave a Reply

登录后才能评论