4.1.20 框架之Session

在日常开发中,我们经常需要把一些通用的信息放入程序执行的上下文中,以便业务开发人员快速获取。那么oinone的PamirsSession就是来解决此类问题的。

一、PamirsSession介绍

在oinone的体系中PamirsSession是执行上下文的承载,您能从中获取业务基础信息、指令信息、元数据信息、环境信息、请求参数,以及前后端MessageHub等。在前面的学习过程中我们已经多次接触到了如何使用PamirsSession:

  1. 在4.1.19【框架之网关协议-后端占位符】一文中,使用PamirsSession.getUserId()来获取当前登入用户Id,诸如此类的业务基础信息;

  2. 在4.1.18【框架之网关协议-variables变量】一文中,使用PamirsSession.getRequestVariables()得到PamirsRequestVariables对象,进而获取前端请求的相关信息;

  3. 在4.1.5【模型之持久层配置】一文中,使用PamirsSession.directive(),来操作元位指令系统,进而影响执行策略;

  4. 在4.1.13【Action之校验】、3.4.1【构建第一个Function】等文章中,都用到PamirsSession.getMessageHub()来设置返回消息。

二、构建模块自身Session(举例)

不同的应用场景对PamirsSession的诉求是不一样的,这个时候我们就可以去扩展PamirsSession来达到我们的目的

构建模块自身Session的步骤

  1. 构建自身特有的数据结构XSessionData

  2. 对XSessionData进行线程级缓存封装

  3. 利用Hook机制初始化XSessionData并放到ThreadLocal中

  4. 定义自身XSessionApi

  5. 实现XSessionApi接口、SessionClearApi。在请求结束时会调用SessionClearApi的clear方法

  6. 定义XSession继承PamirsSession

扩展PamirsSession的经典案例设计图

4.1.20 框架之Session

图4-1-20-1 扩展PamirsSession的经典案例设计图

构建Demo应用自身Session

下面的例子为给Session放入当前登陆用户

Step1 新建DemoSessionData类

构建自身特有的数据结构DemoSessionData,增加一个模型为PamirsUser的字段user,DemoSessionData用Data注解,注意要用Oinone平台提供的@Data

package pro.shushi.pamirs.demo.core.session;

import pro.shushi.pamirs.meta.annotation.fun.Data;
import pro.shushi.pamirs.user.api.model.PamirsUser;

@Data
public class DemoSessionData {

    private PamirsUser user;

}

图4-1-20-2 新建DemoSessionData类

Step2 新建DemoSessionCache

对DemoSessionData进行线程级缓存封装

package pro.shushi.pamirs.demo.core.session;

import pro.shushi.pamirs.meta.api.CommonApiFactory;
import pro.shushi.pamirs.meta.api.session.PamirsSession;
import pro.shushi.pamirs.user.api.model.PamirsUser;
import pro.shushi.pamirs.user.api.service.UserService;

public class DemoSessionCache {
    private static final ThreadLocal<DemoSessionData> BIZ_DATA_THREAD_LOCAL = new ThreadLocal<>();
    public static PamirsUser getUser(){
        return BIZ_DATA_THREAD_LOCAL.get()==null?null:BIZ_DATA_THREAD_LOCAL.get().getUser();
    }
    public static void init(){
        if(getUser()!=null){
            return ;
        }
        Long uid = PamirsSession.getUserId();
        if(uid == null){
            return;
        }
        PamirsUser user = CommonApiFactory.getApi(UserService.class).queryById(uid);
        if(user!=null){
            DemoSessionData demoSessionData = new DemoSessionData();
            demoSessionData.setUser(user);
            BIZ_DATA_THREAD_LOCAL.set(demoSessionData);
        }
    }
    public static void clear(){
        BIZ_DATA_THREAD_LOCAL.remove();
    }
}

图4-1-20-3 对DemoSessionData进行线程级缓存封装

Step3 新建DemoSessionHook

利用Hook机制,调用DemoSessionCache的init方法初始化DemoSessionData并放到ThreadLocal中。

@Hook(module= DemoModule.MODULE_MODULE), 规定只有增对DemoModule模块访问的请求该拦截器才会生效,不然其他模块的请求都会被DemoSessionHook拦截。

package pro.shushi.pamirs.demo.core.hook;

import org.springframework.stereotype.Component;
import pro.shushi.pamirs.demo.api.DemoModule;
import pro.shushi.pamirs.demo.core.session.DemoSessionCache;
import pro.shushi.pamirs.meta.annotation.Hook;
import pro.shushi.pamirs.meta.api.core.faas.HookBefore;
import pro.shushi.pamirs.meta.api.dto.fun.Function;

@Component
public class DemoSessionHook implements HookBefore {
    @Override
    @Hook(priority = 1,module = DemoModule.MODULE_MODULE)
    public Object run(Function function, Object... args) {
        DemoSessionCache.init();
        return function;
    }
}

图4-1-20-4 新建DemoSessionHook

Step4 新建DemoSessionApi

package pro.shushi.pamirs.demo.core.session;

import pro.shushi.pamirs.meta.api.CommonApi;
import pro.shushi.pamirs.user.api.model.PamirsUser;

public interface DemoSessionApi extends  CommonApi {
    PamirsUser getUser();
}

图4-1-20-5 新建DemoSessionApi

Step5 新建DemoSessionHolder

  1. 实现DemoSessionApi接口

  2. 实现SessionClearApi接口,在请求结束时会调用SessionClearApi的clear方法

package pro.shushi.pamirs.demo.core.session;

import org.springframework.stereotype.Component;
import pro.shushi.pamirs.meta.api.core.session.SessionClearApi;
import pro.shushi.pamirs.user.api.model.PamirsUser;

@Component
public class DemoSessionHolder implements DemoSessionApi, SessionClearApi {
    @Override
    public PamirsUser getUser() {
        return DemoSessionCache.getUser();
    }

    @Override
    public void clear() {
        DemoSessionCache.clear();
    }
}

图4-1-20-6 新建DemoSessionHolder

Step6 新建DemoSession

package pro.shushi.pamirs.demo.core.session;

import pro.shushi.pamirs.meta.api.CommonApiFactory;
import pro.shushi.pamirs.meta.api.session.PamirsSession;
import pro.shushi.pamirs.user.api.model.PamirsUser;

public class DemoSession extends PamirsSession {
    public static PamirsUser getUser(){
        return CommonApiFactory.getApi(DemoSessionApi.class).getUser();
    }
}

图4-1-20-7 新建DemoSession

Step7 修改UserPlaceHolder

使用DemoSession.getUser().getId()替代PamirsSession.getUserId()

package pro.shushi.pamirs.demo.core.placeholder;

import org.springframework.stereotype.Component;
import pro.shushi.pamirs.demo.core.session.DemoSession;
import pro.shushi.pamirs.user.api.AbstractPlaceHolderParser;

@Component
public class UserPlaceHolder extends AbstractPlaceHolderParser {
    @Override
    protected String value() {
        return DemoSession.getUser().getId().toString();
    }

    @Override
    public Integer priority() {
        return 10;
    }

    @Override
    public Boolean active() {
        return Boolean.TRUE;
    }

    @Override
    public String namespace() {
        return "currentUserId";
    }
}

图4-1-20-8 修改UserPlaceHolder

Step8 重启看效果

效果跟4.1.19【框架之网关协议-后端占位符】一文中的例子一样

Oinone社区 作者:史, 昂原创文章,如若转载,请注明出处:https://doc.oinone.top/oio4/9295.html

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

Like (0)
史, 昂's avatar史, 昂数式管理员
Previous 2024年5月23日 am8:43
Next 2024年5月23日 am8:45

相关推荐

  • 3.3.10 字段类型之关系描述的特殊场景

    在3.3.9【字段类型之关系与引用】一文中已经描述了各种关系字段的常规写法,还有一些特殊场景如:关系映射中存在常量,或者M2M中间表是大于两个字段构成。 举例说明关系字段-高级用法 场景描述 PetTalent模型增加talentType字段,PetItem与PetTalent的多对多关系增加talentType(达人类型),PetItemRelPetTalent 中间表维护petItemId、petTalentId以及talentType,PetDogItem和PetCatItem分别重写petTalents 字段,关系中增加常量描述。示意图如下 实际操作步骤: Step1 新增 TalentTypeEnum package pro.shushi.pamirs.demo.api.enumeration; import pro.shushi.pamirs.meta.annotation.Dict; import pro.shushi.pamirs.meta.common.enmu.BaseEnum; @Dict(dictionary = TalentTypeEnum.DICTIONARY,displayName = "达人类型") public class TalentTypeEnum extends BaseEnum<TalentTypeEnum,Integer> { public static final String DICTIONARY ="demo.TalentTypeEnum"; public final static TalentTypeEnum DOG =create("DOG",1,"狗达人","狗达人"); public final static TalentTypeEnum CAT =create("CAT",2,"猫达人","猫达人"); } Step2 PetTalent模型增加talentType字段 package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.demo.api.enumeration.TalentTypeEnum; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; @Model.model(PetTalent.MODEL_MODEL) @Model(displayName = "宠物达人",summary="宠物达人",labelFields ={"name"}) public class PetTalent extends AbstractDemoIdModel{ public static final String MODEL_MODEL="demo.PetTalent"; @Field(displayName = "达人") private String name; @Field(displayName = "达人类型") private TalentTypeEnum talentType; } Step3 修改PetItem的petTalents字段,在关系描述中增加talentType(达人类型) @Field.many2many(relationFields = {"petItemId"},referenceFields = {"petTalentId","talentType"},through = "PetItemRelPetTalent") @Field.Relation(relationFields = {"id"}, referenceFields = {"id","talentType"}) @Field(displayName = "推荐达人",summary = "推荐该商品的达人们") private List<PetTalent> petTalents; Step4 PetDogItem增加petTalents字段,重写父类PetItem的关系描述 talentType配置为常量,填入枚举的值 增加domain描述用户页面选择的时候自动过滤出特定类型的达人,RSQL用枚举的name @Field.many2many(relationFields = {"petItemId"},referenceFields = {"petTalentId","talentType"},through = "PetItemRelPetTalent") @Field.Relation(relationFields = {"id"}, referenceFields = {"id","talentType"}) @Field(displayName = "推荐达人",summary = "推荐该商品的达人们") private List<PetTalent> petTalents; Step5 PetCatItem增加petTalents字段,重写父类PetItem的关系描述 talentType配置为常量,填入枚举的值 增加domain描述用户页面选择的时候自动过滤出特定类型的达人,RSQL用枚举的name @Field(displayName = "推荐达人") @Field.many2many( through = "PetItemRelPetTalent", relationFields = {"petItemId"}, referenceFields = {"petTalentId","talentType"} ) @Field.Relation(relationFields = {"id"}, referenceFields = {"id", "#2#"}, domain = " talentType == CAT") private List<PetTalent> petTalents; Step6 清除中间表demo_core_pet_item_rel_pet_talent的数据记录 清除PetItem与PetTalent的多对多中间表demo_core_pet_item_rel_pet_talent的数据记录 Step7 重启看效果 修改达人记录,选择不同达人类型 PetItem、PetCatItem、PetDogItem不同的交互页面

    2024年5月23日
    1.9K00
  • 4.2.2 框架之MessageHub

    一、MessageHub 请求出现异常时,提供”点对点“的通讯能力 二、何时使用 错误提示是用户体验中特别重要的组成部分,大部分的错误体现在整页级别,字段级别,按钮级别。友好的错误提示应该是怎么样的呢?我们假设他是这样的 与用户操作精密契合 当字段输入异常时,错误展示在错误框底部 按钮触发服务时异常,错误展示在按钮底部 区分不同的类型 错误 成功 警告 提示 调试 简洁易懂的错误信息 在oinone平台中,我们怎么做到友好的错误提示呢?接下来介绍我们的MessageHub,它为自定义错误提示提供无限的可能。 三、如何使用 订阅 import { useMessageHub, ILevel } from "@kunlun/dependencies" const messageHub = useMessageHub('当前视图的唯一标识'); /* 订阅错误信息 */ messageHub.subscribe((errorResult) => { console.log(errorResult) }) /* 订阅成功信息 */ messageHub.subscribe((errorResult) => { console.log(errorResult) }, ILevel.SUCCESS) 图4-2-2-1 订阅的示例代码 销毁 /** * 在适当的时机销毁它 * 如果页面逻辑运行时都不需要销毁,在页面destroyed是一定要销毁,重要!!! */ messageHub.unsubscribe() 图4-2-2-2 销毁的示例代码 四、实战 让我们把3.5.7.5【自定义视图-表单】一文中的自定义表单进行改造,加入我们的messageHub,模拟在表单提交时,后端报错信息在字段下方给予提示。 Step1 (后端)重写PetType的创建函数 重写PetType的创建函数,在创建逻辑中通过MessageHub返回错误信息,返回错误信息的同时要设置paths信息方便前端处理 @Action.Advanced(name = FunctionConstants.create, managed = true) @Action(displayName = "确定", summary = "创建", bindingType = ViewTypeEnum.FORM) @Function(name = FunctionConstants.create) @Function.fun(FunctionConstants.create) public PetType create(PetType data){ List<Object> paths = new ArrayList<>(); paths.add("demo.PetType"); paths.add("kind"); PamirsSession.getMessageHub().msg(new Message().msg("kind error").setPath(paths).setLevel(InformationLevelEnum.ERROR).setErrorType(ErrorTypeEnum.BIZ_ERROR)); List<Object> paths2 = new ArrayList<>(); paths2.add("demo.PetType"); paths2.add("name"); PamirsSession.getMessageHub().msg(new Message().msg("name error").setPath(paths2).setLevel(InformationLevelEnum.ERROR).setErrorType(ErrorTypeEnum.BIZ_ERROR)); // data.create(); return data; } 图4-2-2-3 (后端)重写PetType的创建函数 Step 2 修改PetForm.vue <template> <div class="petFormWrapper"> <form :model="formState" @finish="onFinish"> <a-form-item label="品种种类" id="name" name="kind" :rules="[{ required: true, message: '请输入品种种类!', trigger: 'focus' }]"> <a-input v-model:value="formState.kind" @input="(e) => onNameChange(e, 'kind')" /> <span style="color: red">{{ getServiceError('kind') }}</span> </a-form-item> <a-form-item label="品种名" id="name" name="name" :rules="[{ required: true, message: '请输入品种名!', trigger: 'focus' }]"> <a-input v-model:value="formState.name" @input="(e) => onNameChange(e, 'name')" /> <span style="color: red">{{ getServiceError('name') }}</span> </a-form-item> </form> </div> </template>- <script lang="ts"> import { defineComponent, reactive…

    2024年5月23日
    1.8K00
  • 2.4 Oinone的三大独特性

    Oinone在技术方面通过整合互联网架构和低代码技术,实现了三个独特的关键创新点(如图2-5所示): 独立模块化的个性化定制:每个需求都可以被视为一个独立的模块,从而实现个性化定制,提高软件生产效率。此外,这些独立模块也不会影响产品的迭代和升级,为客户带来无忧的体验。 灵活的部署方式:单体部署和分布式部署的灵活切换,为企业业务的发展提供了便利,同时适用于不同规模的公司,有助于有效地节约企业成本,提升创新效率,并让互联网技术更加亲民。 低代码和无代码的结合:低无一体为不同的IT组织和业务用户提供了有效的协同工作方式,能够快速部署安全、可扩展的应用程序和解决方案,帮助企业/组织更好地管理业务流程并不断优化。 图2-5 Oinone的三大独特性

    2024年5月23日
    2.5K00

Leave a Reply

Please Login to Comment