Oinone协同开发源码分析

前提

源码分析版本是 5.1.x版本

什么是协同开发模式

协同开发模式解决的是不同开发,在开发同一个模型时,不会相互影响,也不会影响到测试环境
详见:Oinone协同开发使用手册

协同开发原理

在协同模式下,本地开发的元数据,配置pamirs.data.distribution.session.ownSign参数后,元数据前缀加ownSign值,然后只存在redis缓存,不落库。其它环境无法直接访问到该数据。
测试环境,或其它环境访问,需要在url上加ownSign等于设置的,则读redis数据时,除了加载通用数据,也会合并ownSign前缀的redis数据,显示出来

注意事项

  1. 协同开发仅支持界面设计器,其他设计器均不支持
  2. 不支持权限配置
  3. 不支持工作流触发

版本支持

完整支持5.1.0及以上

功能详解

启动时操作

  1. 做元数据保护检查
  2. 配置ownSign,则key拼接为 ownSign + ':' + key
  3. 清除掉ownSign的redis缓存数据;非ownSign不用清理
  4. 计算差量数据
  5. 有差量数据,放入ownSign标识数据,并清理本地标识
  6. dubbo注册服务,group拼接group + ownSign 后进行注册

读取时操作

读本地

  1. 组装key: ownSign + ':' + key
  2. 本地缓存有数据,更新缓存本地数据,返回
  3. 本地没有数据,读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]

处理逻辑如下

  1. 看redis是否启用保护标识的值
  2. 获取pamirs.distribution.session.ownSign配置
  3. 没有启动参数 且redis没有值,则retrun
  4. 如果有启动参数且配置了ownSign,报错 在使用元数据保护模式下,不允许设置 [pamirs.distribution.session.ownSign]
  5. 如果有启动参数且 redis没有值或启动参数设置 -P metaForceProtected,则写入redis
  6. 如果有启动参数, 且启动参数跟redis值不同,则报错[公共环境开启了元数据保护模式,本地开发环境需配置[pamirs.distribution.session.ownSign]]
  7. 如果没有启动参数且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方式

  1. 看header是否有ownSign这个标识
  2. header没有,则从配置里取,并放到header里
    ownSign的获取核心代码
    CdDistributionSessionFillOwnSignApi

    @Override
    public String getCdOwnSign() {
        String cdOwnSign = null;
        // 看header是否有ownSign这个标识
        Map<String, String> headers = PamirsSession.getRequestVariables().getHeaders();
        if (MapUtils.isNotEmpty(headers)) {
            cdOwnSign = headers.get(OWN_SIGN);
        }
        // 页面没有ownSign标识,则从配置里取,并放到header里
        if (StringUtils.isBlank(cdOwnSign)) {
            cdOwnSign = getConfigCdOwnSign();
            if (headers != null) {
                headers.put(OWN_SIGN, cdOwnSign);
            }
        }
        return cdOwnSign;
    }

清理redis缓存

ownSign的rediskey组装形式,会在原有key前加入ownSign值 + ":"
ownSign + ':' + key
CdDistributionSessionCacheApi

    SEPARATOR_COLON = ":";
    default String keyOwnSign(String key) {
        String cdOwnSign = SessionFillOwnSignApiHolder.get().getCdOwnSign();
        if (StringUtils.isNotBlank(cdOwnSign)) {
            key = cdOwnSign + SEPARATOR_COLON + key;
        }
        return key;
    }
  1. 如果配置ownSign设置,则进行清理工作
  2. 组装Key: ownSign + ':' + key
  3. 执行redis删除
    核心代码 CdModelL2Cache

    public void clear() {
    
        if (handleOwnSign()) {
            SessionRedisUtils.deletePattern(keyOwnSign(super.getModelDefPattern()));
            SessionRedisUtils.deletePattern(keyOwnSign(super.getModelNamePattern()));
        }
    }

数据写入缓存

以模型为例
插入缓存入口,updateMetaData

public <T extends MetaBaseModel> void updateMetaData(String model, List<T> dataList) {
        if (CollectionUtils.isEmpty(dataList)) {
            return;
        }

        // 初始化. Init要放在allMetaRefresh的判断前面
        this.init();
        if (allMetaRefresh) {
            return;
        }

        switch (model) {
            case ModuleDefinition.MODEL_MODEL:
            case UeModule.MODEL_MODEL:
                asyncExecution(model, dataList.size(), MetaUpgradeCheckApi.UPDATE_OPERATOR, () -> fillSessionModule((List<ModuleDefinition>) dataList));
                break;
            ....
        }
    }

每个model轮询处理
    private boolean fillSessionModel(List<ModelDefinition> dataList, Boolean needFunctions) {
        ....
        return reliableExecution(() -> {
            for (ModelDefinition modelDefinition : dataList) {
                doOneModel(modelDefinition, needFunctions);
            }
        }, "模型元数据[变更]");
    }

    private void doOneModel(ModelDefinition modelDefinition, Boolean needFunctions) {
        // 构造模型配置
        ModelConfig modelConfig = SessionsHelper.fetchModelConfig(modelDefinition);
        String modelModel = modelConfig.getModel();
        String modelUniqueKey = modelModel + ":" + Optional.ofNullable(modelDefinition.getModelFields()).map(List::size).orElse(0);
        try {
            if (ObjectRepeatHelper.isNotRepeat(DistributionDataSupplier.getModelRepeats(), modelUniqueKey)) {
            // 放入缓存
                modelCache.put(modelModel, modelConfig);
            }
        } catch (Exception e) {
            ....
        }

        // 全量情况下metaData.getStandAloneFunctionList()的Function不全;需要把模型的Function也进行缓存
        if (needFunctions) {
            fillSessionModelFunction(modelConfig.getFunctionList());
        }
    }

放入缓存
CdModelL2Cache

    private void cdPut(String key, ModelConfig value) {
        String modelDefCacheKey = cdModelDefCacheKey(key);
        String modelNameCacheKey = cdModelNameCacheKey(value.getName());
        byte[] bytes = MetadataSerializationHelper.serialize(value.getModelDefinition());
        SessionRedisUtils.set(modelDefCacheKey, bytes);
        SessionRedisUtils.set(modelNameCacheKey, bytes);
        //从模型设计器保存后,需要清除掉本地空的标识。后面再get的时候会从redis中获取
DistributionMetaDataLocalCache.removeModelDef(modelDefCacheKey);
        DistributionMetaDataLocalCache.removeModelDef(modelNameCacheKey);
    }

其它处理ownSign数据对象有:

CdFunctionByNameL2Cache
CdFunctionL2Cache
CdActionL2Cache
CdViewL2Cache
....

写入dubbo服务

写入dubbo链路看文章Oinone远程调用链路源码分析

  1. 在group加后缀 group = group + ownSign
  2. 调用dubbo泛化调用

核心代码DefaultRemoteRegistry

    // 泛化调用
    public void registryService(Function function) {
        String interfaceName = RegistryUtils.getRegistryInterface(function);
        String group = getGroup(function, true);
        String version = getVersion(function);
        Integer timeout = getTimeout(function);
        Integer retries = function.getRetries();
        if (log.isDebugEnabled()) {
            log.debug("register service. interfaceName: {}, group: {}, version: {}, timeout: {}, retries: {}", interfaceName, group, version, timeout, retries);
        }
        remoteRegistryComponent.registryGenericService(interfaceName, group, version, timeout, retries);
}

// group加前缀
private String getGroup(Function function, boolean usingOwnSign) {
        String group = function.getGroup();
...
        if (usingOwnSign) {
            String ownSign = Spider.getDefaultExtension(SessionFillOwnSignApi.class).getCdOwnSign();
            if (StringUtils.isNotBlank(ownSign)) {
                group = group + ownSign;
            }
        }
        return group;
    }

读缓存

  1. 如果配置ownSign参数,则走ownSign处理逻辑
  2. 组装key: cdOwnSign + SEPARATOR_COLON + key
  3. 本地有缓存,更新本地缓存,并返回
  4. 本地没有缓存,读取redis数据,后放入本地缓存,返回

CdModelL2Cache

public ModelConfig get(String key) {
        ModelConfig modelConfig;
        if (handleOwnSign()) {
            modelConfig = cdGet(key);
            if (modelConfig == null) {
                modelConfig = super.get(key);
            }
        } else {
            modelConfig = super.get(key);
        }
        return modelConfig;
    }

private ModelConfig cdGet(String key) {
        if (DistributionBaseKeyer.MODEL_MODEL_BASE_KEYS.contains(key)) {
            return CommonApiFactory.getApi(DistributionL1CacheProxy.class).getModelL1Cache().get(key);
        }
        String modelDefCacheKey = cdModelDefCacheKey(key);
        ModelDefinition modelDef = DistributionMetaDataLocalCache.getModelDef(modelDefCacheKey);
        // 本地有数据,返回,没有则放入本地缓存,再返回
        if (modelDef != null || DistributionMetaDataLocalCache.keyModelDef(modelDefCacheKey)) {
            return super.covertModelConfig(modelDef);
        }
        // 读取redis数据
        byte[] bytes = SessionRedisUtils.get(modelDefCacheKey);
        modelDef = KryoSerializer.deserialize(bytes);
        if (modelDef != null) {
            // 放入本地缓存DistributionMetaDataLocalCache.setModelDef(modelDefCacheKey, modelDef);
        }
        return super.covertModelConfig(modelDef);
    }

读dubbo服务

读取dubbo链路看文章Oinone远程调用链路源码分析
RemoteComputer.invoke()

  1. 在group加后缀 group = group + ownSign
  2. 调用dubbo泛化调用

核心代码DefaultRemoteRegistry

    public GenericService registryConsumer(Function function) {
        String interfaceName = RegistryUtils.getRegistryInterface(function);
        // 
        String group = getGroup(function, true);
        String version = getVersion(function);
        Integer timeout = getTimeout(function);
        Integer retries = function.getRetries();
        if (log.isDebugEnabled()) {
            log.debug("register consumer. interfaceName: {}, group: {}, version: {}, timeout: {}, retries: {}", interfaceName, group, version, timeout, retries);
        }
        return remoteRegistryComponent.registryGenericConsumer(interfaceName, group, version, timeout, retries);
    }

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

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

(0)
oinone的头像oinone
上一篇 2024年9月11日 am9:57
下一篇 2024年9月12日 pm6:12

相关推荐

  • Oinone离线部署设计器镜像

    概述 Oinone平台为合作伙伴提供了多种部署方式,这篇文章将介绍如何在私有云环境部署Oinone平台Docker镜像。 本文以5.2.20.1版本为例进行介绍,使用amd64架构的体验镜像进行部署。具体版本号以数式提供的为准 部署环境要求 包含全部中间件及设计器服务的环境要求 CPU:8 vCPU 内存(RAM):16G以上 硬盘(HDD/SSD):60G以上 仅设计器服务的环境要求 CPU:8 vCPU 内存(RAM):8G以上 硬盘(HDD/SSD):40G以上 部署准备 一台安装了Docker环境的服务器(私有云环境);以下简称部署环境; 一台安装了Docker环境的电脑(可访问公网);以下简称本地环境; 部署清单 下面列举了文章中在本地环境操作结束后的全部文件: 设计器镜像:oinone-designer-full-v5-5.2.20.1-amd64.tar 离线部署结构包:oinone-designer-full-standard-offline.zip Oinone许可证:****-trial.lic(实际文件名以Oinone颁发的许可证为准) 第三方数据库驱动包(非MySQL数据库必须) PS:如需一次性拷贝所有部署文件到部署环境,可以将文档步骤在本地环境执行后,一次性将所有文件进行传输。 在部署环境创建部署目录 mkdir -p /home/admin/oinone-designer-full mkdir -p /home/admin/oinone-designer-full/images 检查部署环境服务器架构 确认部署环境是amd64还是arm64架构,若本文提供的查看方式无法正确执行,可自行搜索相关内容进行查看。 使用uname命令查看 uname -a PS:此步骤非常重要,如果部署环境的服务器架构与本地环境的服务器架构不一致,将导致镜像无法正确启动。 在本地环境准备镜像 在Oinone发布版本一览中选择最新版本的发布日志,找到需要部署的镜像版本。 登录Oinone镜像仓库(若已登录,可忽略此步骤) docker login https://harbor.oinone.top # input username # input password 获取Oinone平台镜像 docker pull harbor.oinone.top/oinone/oinone-designer-full-v5.2:5.2.20.1-amd64 保存镜像到.tar文件 docker save -o oinone-designer-full-v5-5.2.20.1-amd64.tar oinone-designer-full-v5.2:5.2.20.1-amd64 若报错`Error response from daemon: reference does not exist`脚本改成下面这个: docker save -o oinone-designer-full-v5-5.2.20.1-amd64.tar harbor.oinone.top/oinone/oinone-designer-full-v5.2:5.2.20.1-amd64 # docker save [OPTIONS] IMAGE [IMAGE…] 上传.tar到部署环境 scp ./oinone-designer-full-v5-5.2.20.1-amd64.tar admin@127.0.0.1:/home/admin/oinone-full/images/ PS:若无法使用scp方式上传,可根据部署环境的具体情况将镜像文件上传至部署环境的部署目录。 在部署环境加载镜像 加载镜像文件到Docker中 cd /home/admin/oinone-full/images docker load -i oinone-designer-full-v5-5.2.20.1-amd64.tar 查看镜像是否正确加载 docker images 查看输出内容,对比REPOSITORY、TAG、IMAGE ID与本地环境完全一致即可。 设计器服务部署 为了方便起见,服务器操作文件显得不太方便,因此,我们可以在本地环境将部署脚本准备妥善后,传输到部署环境进行部署结构包(oinone-designer-full-standard-offline.)需上传到要部署的服务器中,后面的操作均在这个目中进行 下载离线部署结构包(以数式发出的为准) oinone-designer-full-standard-offline.zip 将Pamirs许可证移动到config目录下,并重命名为****-trial.lic(实际文件名以Oinone颁发的许可证为准) mv ****-trial.lic config/****-trial.lic 加载非MySQL数据库驱动(按需) 将驱动jar文件移动到lib目录下即可。 以KDB8数据库驱动kingbase8-8.6.0.jar为例 mv kingbase8-8.6.0.jar lib/ PS:lib目录为非设计器内置包的外部加载目录(外部库),可以添加任何jar包集成到设计器中。 修改脚本中的配置 修改启动脚本startup.sh 修改对应的镜像版本号, 将IP从192.168.0.121改成宿主机IP configDir=$(pwd) version=5.1.16 IP=192.168.0.121 修改mq/broker.conf 修改其中brokerIP1的IP从192.168.0.121改成宿主机IP brokerClusterName = DefaultCluster namesrvAddr=127.0.0.1:9876 brokerIP1=192.168.0.121 brokerName = broker-a brokerId = 0 deleteWhen = 04 fileReservedTime = 48 brokerRole = ASYNC_MASTER flushDiskType = ASYNC_FLUSH autoCreateTopicEnable=true listenPort=10991 transactionCheckInterval=1000 #存储使用率阀值,当使用率超过阀值时,将拒绝发送消息请求 diskMaxUsedSpaceRatio=98 #磁盘空间警戒阈值,超过这个值则停止接受消息,默认值90 diskSpaceWarningLevelRatio=99 #强制删除文件阈值,默认85 diskSpaceCleanForciblyRatio=97 执行startup.sh脚本启动 sh startup.sh 访问服务 使用http://127.0.0.1:88访问服务

    2024年11月1日
    1.3K00
  • 如何通过传输模型完成页面能力

    介绍 在业务中我们经常能遇到这种场景,我们的数据是通过调用第三方接口获取的,在业务系统中没有对应的存储模型,但是我们又需要展示这些数据,这时候可以利用传输模型不建表的特性完成这个功能。 定义传输模型 package pro.shushi.pamirs.demo.api.tmodel; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; import pro.shushi.pamirs.meta.base.TransientModel; @Model.model(DemoCreateOrder.MODEL_MODEL) @Model(displayName = "下单页面模型") public class DemoCreateOrder extends TransientModel { public static final String MODEL_MODEL = "demo.DemoCreateOrder"; @Field.Integer @Field(displayName ="下单人uid") private Long userId; } 定义action,由于传输模型用于表现层和应用层之间的数据交互,本身不会存储,没有默认的数据管理器,只有数据构造器,所以需要手动添加所需的queryOne、create、update等方法 注意:传输模型没有数据管理器能力,所以不提供类似queryPage的方法,后续版本考虑支持中 package pro.shushi.pamirs.demo.core.action; import org.springframework.stereotype.Component; import pro.shushi.pamirs.demo.api.tmodel.DemoCreateOrder; import pro.shushi.pamirs.meta.annotation.Action; import pro.shushi.pamirs.meta.annotation.Function; import pro.shushi.pamirs.meta.annotation.Model; import pro.shushi.pamirs.meta.api.dto.condition.Pagination; import pro.shushi.pamirs.meta.api.dto.wrapper.IWrapper; import pro.shushi.pamirs.meta.constant.FunctionConstants; import pro.shushi.pamirs.meta.enmu.FunctionOpenEnum; import pro.shushi.pamirs.meta.enmu.FunctionTypeEnum; import pro.shushi.pamirs.meta.enmu.ViewTypeEnum; import static pro.shushi.pamirs.meta.enmu.FunctionOpenEnum.*; @Component @Model.model(DemoCreateOrder.MODEL_MODEL) public class DemoCreateOrderAction { @Function.Advanced(type = FunctionTypeEnum.QUERY) @Function.fun(FunctionConstants.queryByEntity) @Function(openLevel = {LOCAL, REMOTE, API}) public DemoCreateOrder queryOne(DemoCreateOrder query) { return query; } @Action.Advanced(name = FunctionConstants.create, managed = true) @Action(displayName = "创建", label = "确定", summary = "添加", bindingType = ViewTypeEnum.FORM) @Function(name = FunctionConstants.create) @Function.fun(FunctionConstants.create) public DemoCreateOrder create(DemoCreateOrder data) { return data; } @Action.Advanced(name = FunctionConstants.update, managed = true) @Action(displayName = "确定", summary = "修改", bindingType = ViewTypeEnum.FORM) @Function(name = FunctionConstants.update) @Function.fun(FunctionConstants.update) public DemoCreateOrder update(DemoCreateOrder data) { return data; } }

    2024年5月24日
    1.2K00
  • 导出导入翻译

    http://168.138.179.151/pamirs/file 导出翻译项: mutation { excelExportTaskMutation { createExportTask( data: { workbookDefinition: { model: "file.ExcelWorkbookDefinition" name: "excelLocationTemplate" } } ) { name } } } { "path": "/file", "lang": "en-US" } 导入翻译项: mutation { excelImportTaskMutation { createImportTask( data: { workbookDefinition: { model: "file.ExcelWorkbookDefinition" name: "excelLocationTemplate" } file: { url: "https://minio.oinone.top/pamirs/upload/zbh/test/2024/06/03/导出国际化配置模板_1717390304285_1717391684633.xlsx" } } ) { name } } } PS:导入自行修改url进行导入

    2024年6月28日
    1.7K00
  • 【Oracle】后端部署使用Oracle数据库

    Oracle数据库配置 驱动配置 jdbc仓库 https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc8 Maven配置(11g版本可用) <ojdbc.version>23.2.0.0</ojdbc.version> <dependency> <groupId>com.oracle.database.jdbc</groupId> <artifactId>ojdbc8</artifactId> <version>${ojdbc.version}</version> </dependency> JDBC连接配置 pamirs: datasource: base: type: com.alibaba.druid.pool.DruidDataSource driverClassName: oracle.jdbc.OracleDriver url: jdbc:oracle:thin:@//127.0.0.1:1521/orcl username: YOUR_SCHEMA_NAME password: xxxxxx Oracle默认为每个用户创建了一个与当前用户名同名的模式,每个用户应该只使用该模式(DBA用户除外),因此平台使用Oracle时应该通过username处指定与该模式同名的用户名来指定模式。(Oracle多数据源时每一个数据库创建一个用户) 创建用户时用户名应全大写。 连接url配置 官方文档 https://odbc.postgresql.org/docs/config-opt.html url格式 jdbc:oracle:thin:@//ip:端口号/服务名或SID 每一个Oracle进程默认为一个Oracle数据库实例,使用服务名或sid登录该Oralce数据库实例。一个Oracle sid 对应一个数据库实例,而一个服务名可以标识多个数据库实例。远程连接时推荐使用服务名进行连接。可以在安装Oracle的机器上打开SQLPlus,用SYSTEM用户登录上去后使用SELECT SYS_CONTEXT('USERENV', 'INSTANCE_NAME') AS SID FROM DUAL;查询登录使用的sid;也可以使用SELECT VALUE AS SERVICE_NAME FROM V$PARAMETER WHERE NAME = 'service_names';查询登录使用的服务名。 其他连接参数如需配置,可自行查阅相关资料进行调优。 方言配置 pamirs方言配置 pamirs: dialect: ds: base: type: Oracle version: 11.2 major-version: 11g pamirs: type: Oracle version: 11.2 major-version: 11g plus: configuration: jdbc-type-for-null: "NULL" using-model-as-property: true using-statement-handler-dialect: true mapper: batch: useAffectRows global: table-pattern: '${table_30}' column-pattern: '${column_30}' 数据库版本 type version majorVersion 11g – 11.2.0.1.0 Oracle 11.2 11g 12c – 12.2.0.1.0 Oracle 12.2 12c PS:由于方言开发环境为Oracle Database 11g Enterprise Edition Release 11.2.0.1.0版本,其他类似版本(11.2.x)原则上不会出现太大差异,如出现其他版本无法正常支持的,可在文档下方留言。 schedule方言配置 pamirs: event: enabled: true schedule: enabled: true dialect: type: Oracle version: 11.2 major-version: 11g 其他配置 逻辑删除的值配置 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 Oracle数据库用户初始化及授权 — 以下命令均使用dba账户执行 — 创建用户 ONE_TEST (用户名需全大写) 密码 123456 CREATE USER ONE_TEST IDENTIFIED BY 123456; — 解锁用户 ALTER USER ONE_TEST ACCOUNT UNLOCK; — 将用户的默认表空间设置为USERS,临时表空间设置为TEMP ALTER USER ONE_TEST DEFAULT TABLESPACE USERS; ALTER USER ONE_TEST TEMPORARY TABLESPACE TEMP; — 可以用以下命令查询某用户的表空间: SELECT…

    2025年7月10日
    55200
  • 如何发送邮箱、手机短信以及设置

    1.邮件发送 1.1 邮件服务设置 1.1.1 方法一:通过yaml文件配置 pamirs: email: smtp: smtpHost: smtp.exmail.qq.com smtpUser: xxx@xxx.com smtpPassword: xxxxxx smtpPort: 465 smtpSecurity: SSL #邮件模块可后续后台自行添加 templates: – name: 邮箱注册邮件 title: '${code}是你此次注册的验证码' body: '<div>Hi ${realname},</div><div>你正在使用验证码注册。</div>' 1.1.2 方法二:工程启动加入初始化设置方法 /** * 初始化邮件模板 */ private void initEmailTemplate(){ EmailSenderSource emailSenderSource = new EmailSenderSource(); emailSenderSource.setName("邮件发送服务"); emailSenderSource.setType(MessageEngineTypeEnum.EMAIL_SEND); //优先级 emailSenderSource.setSequence(10); //发送账号 emailSenderSource.setSmtpUser("xxx@xxx.com"); //发送密码 emailSenderSource.setSmtpPassword("xxxxxx"); //发送服务器地址和端口 emailSenderSource.setSmtpHost("smtp.exmail.qq.com"); emailSenderSource.setSmtpPort(465); //" None: SMTP 对话用明文完成。" + //" TLS (STARTTLS): SMTP对话的开始时要求TLS 加密 (建议)" + //" SSL/TLS: SMTP对话通过专用端口用 SSL/TLS 加密 (默认是: 465)") emailSenderSource.setSmtpSecurity(EmailSendSecurityEnum.SSL); emailSenderSource.setActive(true); emailSenderSource.createOrUpdate(); List<EmailTemplate> templates = new ArrayList<>(); templates.add(new EmailTemplate().setName("重置密码邮件模板").setTitle("请确认你的密码修改请求").setBody("<div>Hi ${realname},</div><div>你正在使用验证码注册。</div>").setModel(PamirsUser.MODEL_MODEL).setEmailSenderSource(emailSenderSource)); new EmailTemplate().createOrUpdateBatch(templates); } 1.2 调用邮件发送组件发送邮件 /** * 代码中使用消息组件发送Email */ public void sendEmailByTemplate(){ try { EmailSender emailSender = (EmailSender) MessageEngine.get(MessageEngineTypeEnum.EMAIL_SEND).get(null);; EmailTemplate template = new EmailTemplate().setName("邮件模版名称").queryOne(); //标题:${name} //内容:${fieldInt}次数 String sendTo = "xxx@xxx.com"; String copyTo = "yyy@yyy.com"; Map<String, Object> objectData = new HashMap<>(); objectData.put("name","张三"); objectData.put("fieldInt",999); Boolean aBoolean = emailSender.send(template, objectData, sendTo, copyTo); if (null == aBoolean || !aBoolean) { log.error("发送邮件失败"); } } catch (Exception e) { log.error("发送确认邮件失败:,异常:{}", e); } } 2.发送短信 2.1 短信通道设置 2.1.1 方法一:通过yaml文件配置 pamirs: sms: aliyun: signatureMethod: HMAC-SHA1 endpoint: https://dysmsapi.aliyuncs.com version: '2017-05-25' accessKeyId: xxxxxxxxxxxxx signatureVersion: '1.0' accessKeySecret: xxxxxxxxxxxx regionId: cn-hangzhou timeZone: GMT signName: xxxxxx 2.1.2 方法二:工程启动加入初始化设置方法 private void…

    后端 2023年11月6日
    1.7K00

Leave a Reply

登录后才能评论