4.2.6 框架之网络请求-拦截器

在整个http的链路中,异常错误对前端来说尤为重要,他作用在很多不同的场景,通用的比如500, 502等; 一个好的软件通常需要在不同的错误场景中做不同的事情。当用户cookie失效时,希望能自动跳转到登录页;当用户权限发生变更时,希望能跳转到一个友好的提示页;那么如何满足这些个性化的诉求呢?接下来让我们一起了解oinone前端网络请求-拦截器。

一、入口

在src目录下main.ts中可以看到VueOioProvider,这是系统功能提供者的注册入口

image.png

图4-2-6-1 VueOioProvider

import interceptor from './middleware/network-interceptor';
VueOioProvider(
  {
    http: {
      callback: interceptor
    }
  },
  []
);

图4-2-6-2 拦截器的申明入口

二、middleware

在项目初始化时使用CLI构建初始化前端工程,在src/middleware有拦截器的默认实现:

image.png

图4-2-6-3 在src/middleware有拦截器的默认实现

三、interceptor

interceptor在请求返回后触发,interceptor有两个回调函数,error和next

error参数

  • graphQLErrors 处理业务异常

  • networkError 处理网络异常

next

  • extensions 后端返回扩展参数
const interceptor: RequestHandler = (operation, forward) => {
  return forward(operation).subscribe({
    error: ({ graphQLErrors, networkError }) => {
            console.log(graphQLErrors, networkError);
      // 默认实现 => interceptor error 
    },
    next: ({ extensions }) => {
            console.log(extensions);
      // 默认实现 => interceptor next 
    },
  });
};

图4-2-6-4 后端返回扩展参数

四、interceptor error

// 定义错误提示等级
const DEFAULT_MESSAGE_LEVEL = ILevel.ERROR;
// 错误提示等级 对应提示的报错
const MESSAGE_LEVEL_MAP = {
  [ILevel.ERROR]: [ILevel.ERROR],
  [ILevel.WARN]: [ILevel.ERROR, ILevel.WARN],
  [ILevel.INFO]: [ILevel.ERROR, ILevel.WARN, ILevel.INFO],
  [ILevel.SUCCESS]: [ILevel.ERROR, ILevel.WARN, ILevel.INFO, ILevel.SUCCESS],
  [ILevel.DEBUG]: [ILevel.ERROR, ILevel.WARN, ILevel.INFO, ILevel.SUCCESS, ILevel.DEBUG]
};
// 错误提示通用函数
const notificationMsg = (type: string = 'error', tip: string = '错误', desc: string = '') => {
  notification[type]({
    message: tip,
    description: desc
  });
};
// 根据错误等级 返回错误提示和类型
const getMsgInfoByLevel = (level: ILevel) => {
  let notificationType = 'info';
  let notificationText = translate('kunlun.common.info');
  switch (level) {
    case ILevel.DEBUG:
      notificationType = 'info';
      notificationText = translate('kunlun.common.debug');
      break;
    case ILevel.INFO:
      notificationType = 'info';
      notificationText = translate('kunlun.common.info');
      break;
    case ILevel.SUCCESS:
      notificationType = 'success';
      notificationText = translate('kunlun.common.success');
      break;
    case ILevel.WARN:
      notificationType = 'warning';
      notificationText = translate('kunlun.common.warning');
      break;
  }
  return {
    notificationType,
    notificationText
  };
};

error: ({ graphQLErrors, networkError }) => {
      if (graphQLErrors) {
        graphQLErrors.forEach(async ({ message, locations, path, extensions }) => {
          let { errorCode, errorMessage, messages } = extensions || {};
          // FIXME: extensions.errorCode
          if (errorCode == null) {
            const codeArr = /code: (\d+),/.exec(message);
            if (codeArr) {
              errorCode = Number(codeArr[1]);
            }
          }
          if (errorMessage == null) {
            const messageArr = /msg: (.*),/.exec(message);
            if (messageArr) {
              errorMessage = messageArr[1];
            }
          }
          // 错误通用提示
          if (messages && messages.length) {
            messages.forEach((m) => {
              notificationMsg('error', translate('kunlun.common.error'), m.message || '');
            });
          } else {
            notificationMsg('error', translate('kunlun.common.error'), errorMessage || message);
          }
            // 提示扩展信息 根据错误等级来提示对应级别的报错
          const extMessage = getValue(response, 'extensions.messages');
          if (extMessage && extMessage.length) {
            const messageLevelArr = MESSAGE_LEVEL_MAP[DEFAULT_MESSAGE_LEVEL];
            extMessage.forEach((m) => {
              if (messageLevelArr.includes(m.level)) {
                const { notificationType, notificationText } = getMsgInfoByLevel(m.level);
                notificationMsg(notificationType, notificationText, m.message || '');
              }
            });
          }

          // 消息模块的用户未登录错误码
          const MAIL_USER_NOT_LOGIN = 20080002;
          // 基础模块的用户未登录错误码
          const BASE_USER_NOT_LOGIN_ERROR = 11500001;
          if (
            [MAIL_USER_NOT_LOGIN, BASE_USER_NOT_LOGIN_ERROR].includes(Number(errorCode)) &&
            location.pathname !== '/auth/login'
          ) {
            const redirect_url = location.pathname;
            location.href = `/login?redirect_url=${redirect_url}`;
          }
          /**
           * 应用配置异常跳转至通用的教程页面
           */
          // 模块参数配置未完成
          const BASE_SYSTEM_CONFIG_IS_NOT_COMPLETED_ERROR = 11500004;
          if ([BASE_SYSTEM_CONFIG_IS_NOT_COMPLETED_ERROR].includes(Number(errorCode))) {
            const action = getValue(response, 'extensions.extra.action');
            if (action) {
              Action.registerAction(action.model, action);
              const searchParams: string[] = [];
              searchParams.push(`module=${action.module}`);
              searchParams.push(`model=${action.model}`);
              searchParams.push(`viewType=${action.viewType}`);
              searchParams.push(`actionId=${action.id}`);
              const href = `${origin}/page;${searchParams.join(';')}`;
              location.href = href;
            }
          }
        });
      }
      if (networkError) {
        const { name, result } = networkError;
        const errMsg = (result && result.message) || `${networkError}`;
        if (name && result && result.message) {
          notification.error({
            message: translate('kunlun.common.error'),
            description: `[${name}]: ${errMsg}`,
          });
        }
      }
    }

图4-2-6-5 interceptor error

四、interceptor next

next: ({ extensions }) => {
  if (extensions) {
    const messages = extensions.messages as {
      level: 'SUCCESS';
      message: string;
    }[];
    if (messages)
      messages.forEach((msg) => {
        notification.success({
          message: '操作成功',
          description: msg.message,
        });
      });
  }
}

图4-2-6-6 interceptor next

六、完整代码

import { NextLink, Operation } from 'apollo-link';
import { notification } from 'ant-design-vue';
import getValue from 'lodash/get';
import { Action, ILevel, translate } from '@kunlun/dependencies';

interface RequestHandler {
  (operation: Operation, forward: NextLink): Promise<any> | any;
}

const DEFAULT_MESSAGE_LEVEL = ILevel.ERROR;
const MESSAGE_LEVEL_MAP = {
  [ILevel.ERROR]: [ILevel.ERROR],
  [ILevel.WARN]: [ILevel.ERROR, ILevel.WARN],
  [ILevel.INFO]: [ILevel.ERROR, ILevel.WARN, ILevel.INFO],
  [ILevel.SUCCESS]: [ILevel.ERROR, ILevel.WARN, ILevel.INFO, ILevel.SUCCESS],
  [ILevel.DEBUG]: [ILevel.ERROR, ILevel.WARN, ILevel.INFO, ILevel.SUCCESS, ILevel.DEBUG]
};

const notificationMsg = (type: string = 'error', tip: string = '错误', desc: string = '') => {
  notification[type]({
    message: tip,
    description: desc
  });
};

const getMsgInfoByLevel = (level: ILevel) => {
  let notificationType = 'info';
  let notificationText = translate('kunlun.common.info');
  switch (level) {
    case ILevel.DEBUG:
      notificationType = 'info';
      notificationText = translate('kunlun.common.debug');
      break;
    case ILevel.INFO:
      notificationType = 'info';
      notificationText = translate('kunlun.common.info');
      break;
    case ILevel.SUCCESS:
      notificationType = 'success';
      notificationText = translate('kunlun.common.success');
      break;
    case ILevel.WARN:
      notificationType = 'warning';
      notificationText = translate('kunlun.common.warning');
      break;
  }
  return {
    notificationType,
    notificationText
  };
};

const interceptor: RequestHandler = (operation, forward) => {
  return forward(operation).subscribe({
    error: ({ graphQLErrors, networkError, response }) => {
      if (graphQLErrors) {
        graphQLErrors.forEach(async ({ message, locations, path, extensions }) => {
          let { errorCode, errorMessage, messages } = extensions || {};
          // FIXME: extensions.errorCode
          if (errorCode == null) {
            const codeArr = /code: (\d+),/.exec(message);
            if (codeArr) {
              errorCode = Number(codeArr[1]);
            }
          }
          if (errorMessage == null) {
            const messageArr = /msg: (.*),/.exec(message);
            if (messageArr) {
              errorMessage = messageArr[1];
            }
          }
          if (messages && messages.length) {
            messages.forEach((m) => {
              notificationMsg('error', translate('kunlun.common.error') || '', m.message || '');
            });
          } else {
            notificationMsg('error', translate('kunlun.common.error') || '', errorMessage || message);
          }

          const extMessage = getValue(response, 'extensions.messages');
          if (extMessage && extMessage.length) {
            const messageLevelArr = MESSAGE_LEVEL_MAP[DEFAULT_MESSAGE_LEVEL];
            extMessage.forEach((m) => {
              if (messageLevelArr.includes(m.level)) {
                const { notificationType, notificationText } = getMsgInfoByLevel(m.level);
                notificationMsg(notificationType, notificationText, m.message || '');
              }
            });
          }
          console.log(extMessage);

          // 消息模块的用户未登录错误码
          const MAIL_USER_NOT_LOGIN = 20080002;
          // 基础模块的用户未登录错误码
          const BASE_USER_NOT_LOGIN_ERROR = 11500001;
          if (
            [MAIL_USER_NOT_LOGIN, BASE_USER_NOT_LOGIN_ERROR].includes(Number(errorCode)) &&
            location.pathname !== '/auth/login'
          ) {
            const redirect_url = location.pathname;
            location.href = `/login?redirect_url=${redirect_url}`;
          }
          /**
           * 应用配置异常跳转至通用的教程页面
           */
          // 模块参数配置未完成
          const BASE_SYSTEM_CONFIG_IS_NOT_COMPLETED_ERROR = 11500004;
          if ([BASE_SYSTEM_CONFIG_IS_NOT_COMPLETED_ERROR].includes(Number(errorCode))) {
            const action = getValue(response, 'extensions.extra.action');
            if (action) {
              Action.registerAction(action.model, action);
              const searchParams: string[] = [];
              searchParams.push(`module=${action.module}`);
              searchParams.push(`model=${action.model}`);
              searchParams.push(`viewType=${action.viewType}`);
              searchParams.push(`actionId=${action.id}`);
              const href = `${origin}/page;${searchParams.join(';')}`;
              location.href = href;
            }
          }
        });
      }
      if (networkError) {
        const { name, result } = networkError;
        const errMsg = (result && result.message) || `${networkError}`;
        if (name && result && result.message) {
          notification.error({
            message: translate('kunlun.common.error') || '',
            description: `[${name}]: ${errMsg}`
          });
        }
      }
    }
  });
};

export default interceptor;

图4-2-6-7 完整代码

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

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

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

相关推荐

  • 3.3.9 字段类型之关系与引用

    有关系与引用类型才让oinone具备完整的描述模型与模型间关系的能力 在PetShop以及其代理模型中已经上用到了O2M、M2O字段,分别如petItems(PetItem)和create(PamrisUser)字段,但是没有过多的讲解。本文重点举例RELATED、M2M、O2M,至于M2O留给大家自行尝试。 一、引用类型(举例) 业务类型 Java类型 数据库类型 规则说明 RELATED 基本类型或关系类型 不存储或varchar、text 引用字段【数据库规则】:点表达式最后一级对应的字段类型;数据库字段值默认为Java字段的序列化值,默认使用JSON序列化【前端交互规则】:点表达式最后一级对应的字段控件类型 表3-3-9-1 字段引用类型 Step1 修改PetShopProxy类 为PetShopProxy类新增一个引用字段relatedShopName,并加上@Field.Related("shopName")注解 为PetShopProxy类新增一个引用字段createrId,并加上@Field.Related({"creater","id"})注解 package pro.shushi.pamirs.demo.api.proxy; import pro.shushi.pamirs.demo.api.model.PetShop; 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.user.api.model.PamirsUser; @Model.model(PetShopProxy.MODEL_MODEL) @Model.Advanced(type = ModelTypeEnum.PROXY) @Model(displayName = "宠物店铺代理模型",summary="宠物店铺代理模型") public class PetShopProxy extends PetShop { public static final String MODEL_MODEL="demo.PetShopProxy"; @Field.many2one @Field(displayName = "创建者",required = true) @Field.Relation(relationFields = {"createUid"},referenceFields = {"id"}) private PamirsUser creater; @Field.Related("shopName") @Field(displayName = "引用字段shopName") private String relatedShopName; @Field.Related({"creater","id"}) @Field(displayName = "引用创建者Id") private String createrId; } 图3-3-9-1 修改PetShopProxy类 Step2 重启系统查看效果 我们发现商店管理-列表页面多出了两个有值字段:引用字段shopName和引用创建者Id 图3-3-9-2 商店管理-列表页面新增两个有值字段 二、关系类型 业务类型 Java类型 数据库类型 规则说明 O2O 模型/DataMap 不存储或varchar、text 一对一关系 M2O 模型/DataMap 不存储或varchar、text 多对一关系 O2M List<模型/DataMap> 不存储或varchar、text 一对多关系 M2M List<模型/DataMap> 不存储或varchar、text 多对多关系 表3-3-9-2 字段关系类型 多值字段或者关系字段需要存储,默认使用JSON格式序列化。多值字段数据库字段类型默认为varchar(1024);关系字段数据库字段类型默认为text。 关系字段 关联关系用于描述模型间的关联方式: 多对一关系,主要用于明确从属关系 一对多关系,主要用于明确从属关系 多对多关系,主要用于弱依赖关系的处理,提供中间模型进行关联关系的操作 一对一关系,主要用于多表继承和行内合并数据 图3-3-9-3 字段关联关系 名词解释 关联关系比较重要的名词解释如下: 关联关系:使用relation表示,模型间的关联方式的一种描述,包括关联关系类型、关联关系双边的模型和关联关系的读写 关联关系字段:业务类型ttype为O2O、O2M、M2O或M2M的字段 关联模型:使用references表示,自身模型关联的模型 关联字段:使用referenceFields表示,关联模型的字段,表示关联模型的哪些字段与自身模型的哪些字段建立关系 关系模型:自身模型 关系字段:使用relationFields表示,自身模型的字段,表示自身模型的哪些字段与关联模型的哪些字段建立关系 中间模型,使用through表示,只有多对多存在中间模型,模型的relationship=true 举例M2M关系类型 多对多关系,主要用于弱依赖关系的处理,提供中间模型进行关联关系的操作。这也是在业务开发中很常见用于描述单据间关系,该例子会举例两种方式描述多对多关系中间表,一是中间表没有在系统显示定义模型,二种是中间表显示定义模型。第一种往往仅是维护多对多关系,第二种往往用于多对多关系中间表自身也需要管理有业务含义,中间表模型还经常额外增加其他字段。 一是中间表没有在系统显示定义模型:如果出现跨模块的场景,在分布式环境下两个模块独立启动,有可能会导致系统关系表被删除的情况发生,因为没有显示定义中间表模型,中间表的模型所属模块会根据两边模型的名称计算,如果刚好被计算到非关系字段所属模型的模块。那么单独启动非关系字段所属模型的模块,则会导致删除关系表。 为什么不直接把中间表的模型所属模块设置为关系字段所属模型的模块?因为如果这样做,当模型两边都定义了多对多关系字段则会导致M2M关系表的所属模块出现混乱。 所以这里建议大家都选用:第二种中间表显示定义模型,不论扩展性还是适应性都会好很多。请用:through=XXXRelationModel.MODEL_MODEL 或者 throughClass=XXXRelationModel.class Step1 新建宠物达人模型,并分别为宠物商品和宠物商店增加 到宠物达人模型的字段 新建宠物达人模型PetTalent package pro.shushi.pamirs.demo.api.model; 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; } 图3-3-9-4 新建宠物达人模型PetTalent 修改宠物商品模型,新增many2many字段petTalents,类型为List ,并加上注解@Field.many2many(relationFields = {"petItemId"},referenceFields = {"petTalentId"},through = PetItemRelPetTalent.MODEL_MODEL),through为指定关联中间表。 package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.demo.api.tmodel.PetItemDetail; import pro.shushi.pamirs.meta.annotation.Field; import…

    2024年5月23日
    1.7K00
  • 接口日志

    记录每个PAPI接口执行日志,接口的响应结果、执行时间、执行时长等信息,可查看接口详情。 接口详情

    2023年5月23日
    1.4K00
  • 2.3 Oinone独特性之源,元数据与设计原则

    让我们来揭开Oinone元数据的神秘面纱,了解它的核心组成、获取方式、面向对象特性以及带来的好处。您或许会想,这些特性能否解决企业数字化转型中互联网架构遇到的挑战呢? 元数据是本文多次提到的重要概念。作为LCDP的基础,元数据支持企业所有研发范式。它数字化描述了软件本身,包括数据、行为和视图等方面。在描述数据时,元数据本身就是数据的数据;在描述行为时,它就是行为的数据;在描述视图时,它就是视图的数据。只有深入理解元数据,才能全面了解Oinone的其他特性。 本章节将介绍元数据的整体概览(如下图2-3所示),带领您了解其核心组成、面向对象特性以及组织方式。请注意,本章节将不会详细展开元数据的细节,这些细节将在后续的相关章程中深入介绍。 图2-3 元数据整体视图 一:以下是元数据的核心组成介绍: 模块(Module):它是将程序划分成若干个子功能,每个模块完成了一个子功能,再把这些模块总起来组成一个整体。它是按业务领域划分和管理的最小单元,是一组功能、界面的集合。 模型(Model):Oinone一切从模型出发,是数据及对行为的载体。它是对所需要描述的实体进行必要的简化,并用适当的变现形式或规则把它的主要特征描述出来所得到的系统模仿品。它包括元信息、字段、数据管理器和自定义函数。同时遵循面向对象设计原则,包括封装、继承和多态。 交互组件(UI Componment):它用菜单、视图和Action来勾绘出模块的前端交互拓扑,并且用组件化的方式统一管理、布局和视图。它用Action来描述所有可操作行为。 函数(Function):它是Oinone可执行逻辑单元,跟模型绑定则对应模型的方法。它描述满足数学领域函数定义,含有三个要素:定义域A、值域C{f(x),x属于A}和对应法则f。其中核心是对应法则f,它是函数关系的本质特征。它满足面向对象原则,可以设置不同开放级别,本地与远程智能切换。 元数据注册表:它以模块为单位的安装记录,在模块安装时,相关的元数据都会在元数据注册表中记录。 二:元数据的产生方式,既可以通过代码注解扫描获取,也可以通过可视化编辑器直接添加。 从代码注解中扫描获取,示例如下代码(如下图2-4所示)。 @Model.model(ResourceBank.MODEL_MODEL) @Model(displayName = "银行",labelFields = "name") public class ResourceBank extends IdModel { public static final String MODEL_MODEL = "resource.ResourceBank"; @Field.String @Field(required = true, displayName = "名称") private String name; @Field.String @Field(required = true, displayName = "银行识别号码", summary = "Bank Identifier Code, BIC 或者 Swift") private String bicCode; …… } 图2-4 从代码注解中扫描获取元数据 可视化的编辑器添加元数据,具体介绍详见7.1《Oinone的设计器》章节 三:Oinone是一种通用低代码开发平台,其元数据设计满足应用开发所需的所有元素,并支持所有研发范式。 它基于元数据的具体实现秉承以下原则: 部署与研发无关; 以模型驱动,符合面向对象设计原则; 代码与数据相互融合,编辑器产生的元数据以面向对象的方式继承扩展标准产品的元数据。 这些原则的集合使整个平台能够实现以下功能特性: 开发分布式应用与单体应用一样简单,部署方式由后期决定。如果要部署为分布式应用,则需要在boot工程中引入Oinone的rpc包。详见4.3《Oinone的分布式体验》一章节; 面向对象的特性使得每个需求都可以是独立模块,独立安装与卸载,让系统像乐高积木一样搭建; 支持两种元数据产生方式,融合的原则确保标准产品迭代与个性化保持独立,真正做到低无一体。 四:这些特性刚好也解决了2.2《互联网架构作为最佳实践为何失效》一章节中客户挑战的三个刺眼问题 互联网架构落地企业数字化转型面临的问题 Oinone应对的策略 不是说敏捷响应吗?为什么改个需求这么慢,不单时间更长,付出的成本也更高了? 特性1、特性2、特性3 不是说能力中心吗?当引入新供应商或有新场景开发的时候,为什么前期做的能力中心不能支撑了? 特性2、特性3 不是说性能好吗?为什么我投入的物理资源更多了? 特性1 表2-2互联网架构落地企业数字化转型面临的问题及Oinone应对策略

    2024年5月23日
    1.9K00
  • 6.5 权限体系(改)

    做好企业级软件,首先得过权限这一关 在企业的IT部门沟通中,权限是避免不了。自嘲下在我们刚出来创业时,为了收获客户对我们技术能力的信任,每每与跟客户沟通时都会说我们是阿里出来的,但在权限设计这个环节不那么灵验,反而被打上了不懂B端权限设计的标签,会问很多问题。我就很奇怪难道大厂就没有内部管理系统了?大厂只有C端交易,没有B端交易?但从侧面说明权限不简单还特别重要。做好企业级软件,首先得过权限这一关。 整体介绍 对于平台运行来说,权限是必须,但我们的auth模块不是必须的,auth模块只是我们提供的一种默认实现,客户可以根据平台的spi机制进行替换。auth模块利用了平台的Hook特性做到与业务无关,在我们开发上层应用的时,是不用感知它的存在的。 auth模块涉及:功能权限、数据权限 数据权限:行权限和列权限。备注:数据权限的控制只能用于【存储模型】 表级权限:表达的语义是:是否该表可读/写(修改和新增) 列权限:表达的语义是:是否该列可读/写(修改和新增) 行权限:表达的语义是:是否对该行可读/写(修改)/删除 功能权限:表达的语义是ServerAction/Function是否可执行/展示,viewAction是否可展示,菜单是否可以显示 范围说明: 配置多个权限项的时候,取并集 配置多个角色的时候,取并集 模型设计 产品体验 Step1 创建角色 通过App Finder 切换至【权限】应用,点击新增按钮创建一个名为oinone的角色 Step2 创建数据权限项 在【权限项列表】菜单,点击【创建】按钮新增一个名为【宠物达人数据权限项】,同时宠物达人的数据权限设置为只能查看性别为男的记录 配置说明: 名称: 代表该项配置的权限的名字(必填)(必须是全系统唯一) 权限模型: 选择需要拦截的数据所在的表,即为模型,可以搜索使用 描述:对该权限项的描述 权限条件的配置: 满足全部:对条件一和条件二要同时满足的数据才能被看见 满足任一:对条件一和条件二要任意满足的数据都能被看见 读权限:对该数据是否有读取的权限 写权限:对该数据是否有修改的权限 删除权限:对该条件内的数据是否有删除的权限 Step3 为角色配置权限 编辑oinone角色,只开通oinoneDemo工程应用 选中【数据权限】选项卡点击【添加】按钮,勾选宠物达人数据权限项点击【确定】按钮 整体点击保存,回到列表页记得点击【权限生效】按钮 Step4 新建用户绑定角色 切换到用户中心模块,点击【创建】按钮填写必要信息,并在角色选中oinone权限组。 退出admin用户,用oinone登陆,权限效果: 只能看见demo模块 oinone登陆只能看到性别为男的宠物达人记录 admin用户登陆 oinone用户登陆 因为宠物达人的页面没有把性别字段放出来,我们看下数据库数据 auth模块扩展 在日常项目开发中,难免会碰到一些针对权限管理的特殊需求,或是为提升性能做的特殊逻辑。接下来我给大家介绍auth模块扩展性。 权限全局配置 对所有权限角色都做限制,而且不想让用户感知,可以实现PermissionFunApi接口,API接口实现的配置方式【只能用于支持全局的数据权限配置】 实现接口PermissionFunApi 将实现托管给SpringAOP 接口的具体实现看下图的代码 package pro.shushi.pamirs.demo.core.service; import org.springframework.stereotype.Component; import pro.shushi.pamirs.auth.api.service.PermissionFunApi; import pro.shushi.pamirs.demo.api.model.PetTalent; @Component public class PetTalentPermissionFunApi implements PermissionFunApi { @Override public String permissionDomain(Object… args) { //获取当前组织中 return "name == '张学友'"; } @Override public String nameSpace() { return PetTalent.MODEL_MODEL; } } 不参与权限控制 如果某一接口不想做权限控制,则可以在启动工程的application-dev.yml文件中配置不需要权限过滤的接口 pamirs: auth: fun-filter: – namespace: demo.PetTalent fun: queryPage 换一个没有配置宠物达人权限的用户(除管理员以外)进入系统,则也可以看到数据。注意【权限全局控制】还是生效的 API 1. 获取当前用户对该模型的行权限 Result<String> result = CommonApiFactory.getApi(AuthApi.class).canReadAccessData("Model"); 返回值为 { 'data':"name=in=('hahaha')" 'success':true … } 用法 : 场景:前端发起的请求都会经过权限拦截,后端代码逻辑发起的数据请求都是不经过任何权限的过滤,但是某些特殊情况需要在后端代码逻辑发起的数据请求也带上权限过滤 入参:请求的模型 出参:Result 数据结构中data会存储一段字符串,该字符串为Rsql 将该Rsql追加到wrapper中 Result<String> result = CommonApiFactory.getApi(AuthApi.class).canReadAccessData("base.UeModule"); String data=result.getData(); QueryWrapper<UeModule> wrapper = Pops.query(); wrapper.setEntity(ueModule); if (!StringUtils.isBlank(data)) { wrapper.apply(data); }

    2024年5月23日
    1.6K00
  • 3.5.6.1 字段的配置

    字段组件类型 ttype可以配置哪些widget?本文这里把oinone平台默认支持的所有widget都进行了罗列,方便大家查阅。 字段组件匹配规则 字段组件没有严格的按组件名(widget)、字段类型(ttype)、视图组件类型(viewType)限定,而是一个匹配规则 按widget 最优先匹配 按最大匹配原则 ttype、viewType。每个属性权重一分 按后注册优先原则 通用属性 属性 属性描述 属性name 默认值 类型 标题 字段的标题名称 label – string 占位提示 一个字段的描述信息,常用于说明当前字典的范围、注意事项等 placeholder – string 描述说明 组件描述信息 hint – string 数据校验 与表单其他数据联动校验 validator – 表达式 数据校验不通过提示 数据校验不通过提示 validatorMessage 校验失败 string 只读 字段的状态,可见,不可编辑 readonly false boolean或者表达式 隐藏 字段的状态,不,不可编辑 invisible false boolean或者表达式 必填 字段是否必填 required false boolean或者表达式 禁用 字段是否禁用 disabled false boolean或者表达式 宽度 属性在页面中的宽度 colSpan 01/02 标题排列方式 标题排列方式: 水平 , 横向 layout vertical vertical | horizontal 默认值 默认值,多值逗号分割 defaultValue – 根据不同ttype有不同的默认值类型 表3-5-6-1 字段通用属性 字段组件大全 widget为"-"表明不需要指定,是该ttype默认widget 组件名称 widget 对应ttype 属性 属性描述 属性name 默认值 类型 单行文本 – STRING 通用属性 文本类型 密码:password; 文本: text type text string 最小长度 输入框填写数据时最少输入的长度值 minLength – number 最大长度 输入框填写数据时最多输入的长度值 maxLength – number 输入格式 单行文本组件特有的属性,通过规则校验内容,提供一些常用的,也支持自定义校验正则 pattern – 正则表达式 输入格式不通过 输入格式不通过提示语 tips 校验失败 string 显示计数器 设置输入框是否显示字数计数器 showCount FALSE boolean 显示清除按钮 设置输入框是否有一键清除的按钮及功能 allowClear TRUE TRUE 支持前缀 开启前缀 showPrefix FALSE boolean 支持后缀 开启后缀 showSuffix FALSE boolean 前缀类型 ICON: 图标; TEXT:文本 prefixType – string 后缀类型 ICON: 图标; TEXT:文本 suffixType – string 前缀内容 文本内容或者图标引用名 prefix – string 后缀内容 文本内容或者图标引用名 suffix – string 前缀存储 前缀存储, 仅前缀类型为文本可用 prefixStore – boolean 后缀存储 后缀存储, 仅后缀类型为文本可用 suffixStore – boolean 多行文本 -…

    2024年5月23日
    1.5K00

Leave a Reply

Please Login to Comment