6.6 消息

在我们系统研发过程中经常需要发送短信、邮件、站内信等,笔者在本文给大家介绍下如何使用Oinone的消息模块。

准备工作

如果通过我们工程脚手架工具生成的则已经引入了无需做更多的配置,如果不是则需要按以下步骤先配置依赖和增加启动模块

pamirs-demo-boot的pom文件中引入pamirs-message-core包依赖

<dependency>
  <groupId>pro.shushi.pamirs.core</groupId>
  <artifactId>pamirs-message-core</artifactId>
</dependency>

pamirs-demo-boot的application-dev.yml文件中增加配置pamirs.boot.modules增加message,即在启动应用中增加message模块

pamirs:
    boot:
    modules:
      - message

消息参数设置

发送邮件和短信需要设置对应的发送邮箱服务器和短信云,短信目前默认阿里云短信。我们通过代码示例来完成对应邮箱和短信的参数设置

Step1 增加pamirs-message-api依赖

pamirs-demo-core的pom文件中引入pamirs-message-api包依赖

<dependency>
  <groupId>pro.shushi.pamirs.core</groupId>
  <artifactId>pamirs-message-api</artifactId>
</dependency>

Step2 消息参数设置

请自行替换邮箱服务器和短信通道的账号信息

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.api.init.InstallDataInit;
import pro.shushi.pamirs.boot.common.api.init.UpgradeDataInit;
import pro.shushi.pamirs.demo.api.DemoModule;
import pro.shushi.pamirs.message.enmu.EmailSendSecurityEnum;
import pro.shushi.pamirs.message.enmu.MessageEngineTypeEnum;
import pro.shushi.pamirs.message.enmu.SMSChannelEnum;
import pro.shushi.pamirs.message.model.EmailSenderSource;
import pro.shushi.pamirs.message.model.SmsChannelConfig;

import java.util.Collections;
import java.util.List;

@Component
public class DemoMessageInit implements InstallDataInit, UpgradeDataInit {

    private void initEmail(){
        EmailSenderSource emailSenderSource = new EmailSenderSource();
        emailSenderSource.setName("邮件发送服务");
        emailSenderSource.setType(MessageEngineTypeEnum.EMAIL_SEND);
        //优先级
        emailSenderSource.setSequence(10);
        //发送账号 FIXME 自行替换
        emailSenderSource.setSmtpUser("");
        //发送密码 FIXME 自行替换
        emailSenderSource.setSmtpPassword("");
        //发送服务器地址和端口
        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.createOrUpdate();
    }

    private void initSms(){
        SmsChannelConfig smsChannelConfig = new SmsChannelConfig();
        smsChannelConfig.setType(MessageEngineTypeEnum.SMS_SEND);
        smsChannelConfig.setChannel(SMSChannelEnum.ALIYUN);
        //短信签名名称
        smsChannelConfig.setSignName("oinone");
        //阿里云账号信息  FIXME 自行替换
        smsChannelConfig.setAccessKeyId("");
        smsChannelConfig.setAccessKeySecret("");
        smsChannelConfig.setEndpoint("https://dysmsapi.aliyuncs.com");
        smsChannelConfig.setRegionId("cn-hangzhou");
        smsChannelConfig.setTimeZone("GMT");
        smsChannelConfig.setSignatureMethod("HMAC-SHA1");
        smsChannelConfig.setSignatureVersion("1.0");
        smsChannelConfig.setVersion("2017-05-25");
        smsChannelConfig.createOrUpdate();

        //初始化短信模版
        //目前支持阿里云短信通道:获取短信模板,如没有短信模板,需要先创建模板,并审核通过
        SmsTemplate smsTemplate = new SmsTemplate();
        smsTemplate.setName("通知短信");
        smsTemplate.setTemplateType(SMSTemplateTypeEnum.NOTIFY);
        smsTemplate.setTemplateCode("SMS_244595482");//从阿里云获取,自行提供 FIXME
        smsTemplate.setTemplateContent("尊敬的&{name},你的&{itemName}库存为&{quantity}");
        smsTemplate.setChannel(SMSChannelEnum.ALIYUN);
        smsTemplate.setStatus(SMSTemplateStatusEnum.SUCCESS);
        smsTemplate.createOrUpdate();
    }

    @Override
    public boolean init(AppLifecycleCommand command, String version) {
        initEmail();
        initSms();
        return Boolean.TRUE;
    }

    @Override
    public boolean upgrade(AppLifecycleCommand command, String version, String existVersion) {
        initEmail();
        initSms();
        return Boolean.TRUE;
    }

    @Override
    public List<String> modules() {
        return Collections.singletonList(DemoModule.MODULE_MODULE);
    }

    @Override
    public int priority() {
        return 0;
    }
}

消息发送举例

该例子以各种站内信、邮件、短信方式发送宠物商品库存记录信息为例,构建宠物商品库存信息发送服务并在宠物商品库存模型的表格页面创建发送入口

Step1 创建宠物商品库存信息发送服务接口

在Api工程新建PetItemInventoryMessageService接口,并定义发送站内信、邮件、短信的三个Function

package pro.shushi.pamirs.demo.api.service;

import pro.shushi.pamirs.demo.api.model.PetItemInventroy;
import pro.shushi.pamirs.meta.annotation.Fun;
import pro.shushi.pamirs.meta.annotation.Function;

@Fun(PetItemInventoryMessageService.FUN_NAMESPACE)
public interface PetItemInventoryMessageService {

    String FUN_NAMESPACE = "demo.PetItemInventoryMessageService";
    @Function
    void sendMail(PetItemInventroy data);
    @Function
    void sendEmail(PetItemInventroy data);
    @Function
    void sendSms(PetItemInventroy data);

}

Step2 发送站内信

在Core工程新建PetItemInventoryMessageService接口的实现类

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

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import pro.shushi.pamirs.demo.api.enumeration.DemoExpEnumerate;
import pro.shushi.pamirs.demo.api.model.PetItemInventroy;
import pro.shushi.pamirs.demo.api.service.PetItemInventoryMessageService;
import pro.shushi.pamirs.demo.core.session.DemoSession;
import pro.shushi.pamirs.message.engine.MessageEngine;
import pro.shushi.pamirs.message.engine.email.EmailSender;
import pro.shushi.pamirs.message.engine.message.MessageSender;
import pro.shushi.pamirs.message.engine.sms.SMSSender;
import pro.shushi.pamirs.message.enmu.MessageEngineTypeEnum;
import pro.shushi.pamirs.message.enmu.MessageGroupTypeEnum;
import pro.shushi.pamirs.message.enmu.MessageTypeEnum;
import pro.shushi.pamirs.message.enmu.SMSTemplateTypeEnum;
import pro.shushi.pamirs.message.model.PamirsMessage;
import pro.shushi.pamirs.message.model.SmsTemplate;
import pro.shushi.pamirs.message.tmodel.EmailPoster;
import pro.shushi.pamirs.message.tmodel.SystemMessage;
import pro.shushi.pamirs.meta.annotation.Fun;
import pro.shushi.pamirs.meta.annotation.Function;
import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j;
import pro.shushi.pamirs.meta.common.exception.PamirsException;
import pro.shushi.pamirs.meta.util.JsonUtils;
import pro.shushi.pamirs.user.api.model.PamirsUser;

import javax.mail.MessagingException;
import javax.mail.SendFailedException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Fun(PetItemInventoryMessageService.FUN_NAMESPACE)
@Slf4j
@Component
public class PetItemInventoryMessageServiceImpl implements PetItemInventoryMessageService {

    public static final String INVALID_ADDRESSES_CODE = "Invalid Addresses";
    public static final String INVALID_ADDRESSES_MSG = "非法的邮箱地址";
    public static final String CAN_NO_CONNECT_TO_SMTP_CODE = "Could not connect to SMTP";
    public static final String CAN_NO_CONNECT_TO_SMTP_MSG = "连接邮件服务失败";

    @Override
    @Function
    public void sendMail(PetItemInventroy data) {
        //接收对象列表
        PamirsUser user = DemoSession.getUser();

        List<Long> userIds = new ArrayList<>();
        userIds.add(user.getId());

        String subject = "商品库存信息";
        String body = data.getItemName() + "_" + data.getQuantity();
        MessageSender mailSender = (MessageSender) MessageEngine.get(MessageEngineTypeEnum.MAIL_SEND).get(null);
        PamirsMessage message = new PamirsMessage()
                .setName(subject)
                .setSubject(subject)
                .setBody(body)
                .setMessageType(MessageTypeEnum.NOTIFICATION);

        List<PamirsMessage> messages = new ArrayList<>();
        messages.add(message);
        SystemMessage systemMessage = new SystemMessage();
        systemMessage.setPartners(userIds.stream().map(i -> (PamirsUser) new PamirsUser().setId(i)).collect(Collectors.toList()))
                .setType(MessageGroupTypeEnum.SYSTEM_MAIL)
                .setMessages(messages);
        try {
            mailSender.sendSystemMail(systemMessage);
        } catch (Exception e) {
            // TODO: 可增加失败业务处理
            throw PamirsException.construct(DemoExpEnumerate.SYSTEM_ERROR, e).appendMsg("站内信发送失败").errThrow();
        }
    }

    @Override
    @Function
    public void sendEmail(PetItemInventroy data) {
        //发送一个邮件
        List<String> receiveEmails = new ArrayList<>();
        receiveEmails.add("testhaha@shushi.pro");//收件人邮箱,自行替换 FIXME 
        EmailSender emailSender = null;
        EmailPoster emailPoster = null;

        emailSender = (EmailSender) MessageEngine.get(MessageEngineTypeEnum.EMAIL_SEND).get(null);

        String title = data.getItemName();//"邮件标题";
        String body = data.getItemName() + "_" + data.getQuantity();//"邮件内容";
        String sender = DemoSession.getUserName();//"发件人";
        String replyEmail = "回复邮箱";

        emailPoster = new EmailPoster().setSender(sender).setTitle(title).setBody(body).setReplyTo(replyEmail);

        EmailPoster finalEmailPoster = emailPoster;
        EmailSender finalEmailSender = emailSender;

        StringBuilder errorMessages = new StringBuilder();
        receiveEmails.stream().distinct().collect(Collectors.toList()).forEach(
                email -> {
                    try {
                        if (!finalEmailSender.send(finalEmailPoster.setSendTo(email))) {
                            log.error("发送邮件失败:emailPoster:{}", JsonUtils.toJSONString(finalEmailPoster));
                            String message = "发送邮件失败,错误信息:" + "系统异常" + ",邮箱:" + email + ";";
                            errorMessages.append(message);
                        }
                    } catch (Exception e) {
                        log.error("发送邮件失败:emailPoster:{},异常:{}", JsonUtils.toJSONString(finalEmailPoster), e);
                        String errorMsg = transferEmailThrowMessage(e);
                        String message = "发送邮件失败,错误信息:" + errorMsg + ",邮箱:" + email + ";";
                        errorMessages.append(message);
                    }
                }
        );

        if (StringUtils.isNotBlank(errorMessages.toString())) {
            // TODO: 可增加处理邮件发送失败的业务逻辑
        }
    }

    private String transferEmailThrowMessage(Exception e) {
        String errorMessage = e.getMessage();
        Throwable cause = e.getCause();
        if (cause != null) {
            if (cause instanceof SendFailedException) {
                String message = cause.getMessage();
                if (INVALID_ADDRESSES_CODE.equals(message)) {
                    errorMessage = INVALID_ADDRESSES_MSG;
                }
            } else if (cause instanceof MessagingException) {
                String message = cause.getMessage();
                if (StringUtils.isNotBlank(message) && message.contains(CAN_NO_CONNECT_TO_SMTP_CODE)) {
                    errorMessage = CAN_NO_CONNECT_TO_SMTP_MSG;
                }
            }
        }
        return errorMessage;
    }

    @Override
    @Function
    public void sendSms(PetItemInventroy data) {
        StringBuilder errorMessages = new StringBuilder();

        List<String> receivePhoneNumbers = new ArrayList<>();
        receivePhoneNumbers.add("13777899044");//接收手机号,自行提供 FIXME 

        //目前支持阿里云短信通道:获取短信模板,如没有短信模板,需要先创建模板,并审核通过
        //如以下示例
        SmsTemplate smsTemplate = new SmsTemplate().setTemplateType(SMSTemplateTypeEnum.NOTIFY).setTemplateCode("SMS_246455054").queryOne();//从阿里云获取

        // 占位符处理
        Map<String, String> vars = new HashMap<>();
        vars.put("name", DemoSession.getUserName());
        vars.put("itemName", data.getItemName());
        vars.put("quantity", data.getQuantity().toString());

        SMSSender smsSender = (SMSSender) MessageEngine.get(MessageEngineTypeEnum.SMS_SEND).get(null);
        receivePhoneNumbers.stream().distinct().forEach(it -> {
            try {
                if (!smsSender.smsSend(smsTemplate, it, vars)) {
                    String message = "发送短信失败,错误信息:" + "系统异常" + ",手机号:" + it + ";";
                    errorMessages.append(message);
                }
            } catch (Exception e) {
                String message = "发送短信失败,错误信息:" + e.getMessage() + ",手机号:" + it + ";";
                errorMessages.append(message);
            }
        });
        if (StringUtils.isNotBlank(errorMessages.toString())) {
            // TODO: 可增加失败业务处理
        }
    }
}

Step3 为宠物商品库存模型的表格页面新增发送入口

把以下代码复制到PetItemInventroyAction类中

@Autowired
private PetItemInventoryMessageService petItemInventoryMessageService;
@Action(displayName = "发送站内信",bindingType = ViewTypeEnum.TABLE,contextType = ActionContextTypeEnum.SINGLE)
public PetItemInventroy sendMail(PetItemInventroy data) {
    petItemInventoryMessageService.sendMail(data);
    return data;
}

/**
 * 发送邮件
 */
@Action(displayName = "发送邮件",bindingType = ViewTypeEnum.TABLE,contextType = ActionContextTypeEnum.SINGLE)
public PetItemInventroy sendEmail(PetItemInventroy data){
    petItemInventoryMessageService.sendEmail(data);
    return data;
}

/**
 * 发送短息
 */
@Action(displayName = "发送短信",bindingType = ViewTypeEnum.TABLE,contextType = ActionContextTypeEnum.SINGLE)
public PetItemInventroy sendSms(PetItemInventroy data){
    petItemInventoryMessageService.sendSms(data);
    return data;
}

Step4 重启看效果

请分别点击发送邮件、发送短信、发送站内信测试效果,自行测试效果

image.png

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

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

(0)
史, 昂的头像史, 昂数式管理员
上一篇 2024年5月23日
下一篇 2024年5月23日

相关推荐

  • 合作伙伴中心

    合作伙伴中心包含“公司、个人、部门、岗位、员工”五个菜单。其中公司、个人为合作伙伴,是创建组织架构的前置条件;部门、岗位、员工为组织架构,是选择的公司下的内部架构。 合作伙伴 公司页面中包含常规的增删改查和查看详情。 公司的创建页面如下: 个人暂不支持创建。 组织架构 同样菜单中包含常规的增删改查和查看详情。 组织架构可以依次创建部门、岗位、员工。对应关系为,一个部门下有多个岗位,一个员工可以归属于多个部门和多个岗位。 部门创建页面如下: 岗位创建页面如下: 员工创建页面如下: 合作伙伴中心创建的员工会自动生成可登录的用户并进行绑定。 在用户中心创建的用户仅为可登录用户无法操作管理员工、关联部门。 流程设计器中可选到的操作人分类为“员工、部门、角色”。

    2024年6月20日
    1.1K00
  • 3.5.6.3 布局的配置

    布局是将页面拆分成一个一个的小单元,按照上下中左右进行排列。 前沿 在前端领域中,布局可以分为三大块「Float、Flex、Grid 」,Float可以说的上是上古时期的布局了,如今市面还是很少见的,除了一些古老的网站。 目前,平台主要支持通过配置XML上面的cols和span来进行布局。平台也同样支持自由布局,合理的使用row、col、containers和container四个布局容器相关组件,将可以实现各种类型的布局样式,换句话说,平台实现的自由布局功能是Flex和Grid的结合体。 这里主要是讲解Flex和Grid布局,以及目前新的模板布局实现的思路。 Flex布局 Flex布局采用的是一维布局,那么什么是一维布局呢,所谓的一维布局就是只有一个方向、没有体积、面积,比如一条直线。它适合做局部布局,就像我们原来的顶部菜单、面包屑导航,以及现在的主视图字段配置。 图3-5-6-19 Flex布局示意 图3-5-6-20 Flex布局示意 图3-5-6-21 Flex布局示意 从上图可以看看出,Flex布局只能在X、Y轴进行转换,它无法对上下左右四个方向同时处理,因为它没“面积”的概念。所以它最适合做局部布局。 优点 图3-5-6-22 Flex兼容性 Flex的兼容性,可以看得出来,目前主流的浏览器都支持该属性。所以Flex兼容性强,如果你想对局部做布局处理,Flex是最好选择。 缺陷 刚刚也提到了,用户想要的布局是千奇百怪的,如果他想要的布局在现有的功能中无法实现怎么办?让用户放弃?还是说服他使用现在的布局。 Grid布局 Grid布局系统采用的是二维布局,二维布局有四个方向:上、下、左、右,它只有面积没有体积,比如一张纸、网格。 Grid布局 <div id="grid-container-one"> <div class="one-1">Grid Item 1</div> <div>Grid Item 2</div> <div>Grid Item 3</div> <div>Grid Item 4</div> <div>Grid Item 5</div> <div class="one-6">Grid Item 6</div> </div> <div id="grid-container-two"> <div class="tow-1">Grid Item 1</div> <div class="tow-2">Grid Item 2</div> <div>Grid Item 3</div> <div>Grid Item 4</div> <div>Grid Item 5</div> <div>Grid Item 6</div> </div> <div id="grid-container-three"> <div>Grid Item 1</div> <div>Grid Item 2</div> <div class="grid">Grid Item 3</div> <div class="grid-column">Grid Item 4</div> <div>Grid Item 5</div> <div>Grid Item 6</div> <div>Grid Item 7</div> <div class="grid-column">Grid Item 8</div> </div> HTML CSSResult Skip Results Iframe EDIT ON * { box-sizing: border-box; padding: 0; margin: 0; line-height: 1.5; font-weight: bold; text-align: center; } #grid-container-one{ background-color: black; display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(2, 50px); gap: 10px; border: solid black 2px; margin-bottom: 20px; color: salmon; } #grid-container-one div { border: solid white 2px; padding: 10px; } #grid-container-one .one-1 { grid-area: span 1/span 3; text-aligin: center } #grid-container-one .one-6 { grid-column: 3 /4; } #grid-container-two{ background-color: CADETBLUE; display: grid; grid-template-columns: 15% repeat(2, 1fr);…

    2024年5月23日
    1.3K00
  • 4.5.2 研发辅助之SQL优化

    Oinone体系中是不需要针对模型写SQL的,默认提供了通用的数据管理器。在带来便利的情况下,也导致传统的sql审查就没办法开展。但是我们可以以技术的手段收集慢SQL和限制问题SQL执行。 慢SQL搜集目的:去发现非原则性问题的慢SQL,并进行整改 限制问题SQL执行:对应一些不规范的SQL系统上直接做限制,如果有特殊情况手动放开 一、发现慢SQL 这个功能并没有直接加入到oinone的版本中,需要业务自行写插件,插件代码如下。大家可以根据实际情况进行改造比如: 堆栈入口,例子中只是放了pamirs,可以根据实际情况改成业务包路径 对慢SQL的定义是5s还是3s,根据实际情况变 package pro.shushi.pamirs.demo.core.plugin; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.*; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.springframework.stereotype.Component; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; @Intercepts({ @Signature(type = Executor.class,method = "query",args = {MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class}) }) @Component @Slf4j public class SlowSQLAnalysisInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { long start = System.currentTimeMillis(); Object result = invocation.proceed(); long end = System.currentTimeMillis(); if (end – start > 10000) {//大于10秒 try { StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); StringBuffer slowLog = new StringBuffer(); slowLog.append(System.lineSeparator()); for (StackTraceElement element : stackTraceElements) { if (element.getClassName().indexOf("pamirs") > 0) { slowLog.append(element.getClassName()).append(":").append(element.getMethodName()).append(":").append(element.getLineNumber()).append(System.lineSeparator()); } } Object parameter = null; if (invocation.getArgs().length > 1) { parameter = invocation.getArgs()[1]; } MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; BoundSql boundSql = mappedStatement.getBoundSql(parameter); Configuration configuration = mappedStatement.getConfiguration(); String originalSql = showSql(configuration, boundSql); originalSql = originalSql.replaceAll("\'", "").replace("\"", ""); log.warn("检测到的慢SQL为:" + originalSql); log.warn("业务慢SQL入口为:" + slowLog.toString()); } catch (Throwable e1) { //忽略 } } return result; } public String showSql(Configuration configuration, BoundSql boundSql) { Object parameterObject = boundSql.getParameterObject(); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); String sql = boundSql.getSql().replaceAll("[\\s]+", " "); if (parameterMappings.size() > 0 && parameterObject != null) { TypeHandlerRegistry typeHandlerRegistry…

    Oinone 7天入门到精通 2024年5月23日
    1.2K00
  • 4.2.8 自定义组件与设计器结合(新)

    界面设计器组件管理页面添加组件 进入组件的元件管理页面 点击“添加元件” 设计元件的属性 这里以是否“显示清除按钮”作为自定义属性从左侧拖入到中间设计区域,然后发布 点击“返回组件” 鼠标悬浮到卡片的更多按钮的图标,弹出下拉弹出“低无一体”的按钮 在弹窗内点击“生成SDK”的按钮 生成完成后,点击“下载模板工程” 解压模板工程kunlun-sdk.zip 解压后先查看README.MD,了解一下工程运行要点,可以先运行 npm i 安装依赖 再看kunlun-plugin目录下已经有生成好的组件对应的ts和vue文件 下面在vue文件内增加自定义代码,可以运行 npm run dev 在开发模式下调试看效果 <template> <div class="my-form-string-input"> <oio-input :value="realValue" @update:value="change" > <template #prepend>MyPrepend</template> </oio-input> </div> </template> <script lang="ts"> import { defineComponent, ref } from 'vue'; import { OioInput } from '@kunlun/vue-ui-antd'; export default defineComponent({ name: 'customField1', components: { OioInput }, props: { value: { type: String }, change: { type: Function }, }, setup(props) { const realValue = ref<string | null | undefined>(props.value); return { realValue }; } }); </script> <style lang="scss"> .my-form-string-input { border: 1px solid red; } </style> 确定改好代码后运行 npm run build,生成上传所需的js和css文件 可以看到 kunlun-plugin目录下多出了dist目录,我们需要的是 kunlun-plugin.umd.js 和 kunlun-plugin.css 这2个文件 再次回到组件的“低无一体”管理弹窗页面,上传上面生成的js和css文件,并点击“确定”保存,到这里我们的组件就新增完成了。 下面我们再到页面设计器的页面中使用上面设计的组件(这里的表单页面是提前准备好的,这里就不介绍如何新建表单页面了) 将左侧组件库拉直最底部,可以看到刚刚新建的组件,将其拖至中间设计区域,我们可以看到自定义组件的展示结果跟刚刚的代码是对应上的(ps: 如果样式未生效,请刷新页面查看,因为刚刚上传的js和css文件在页面初始加载的时候才会导入进来,刚刚上传的动作未导入新上传的代码文件),再次点击设计区域中的自定义组件,可以看到右侧属性设置面板也出现了元件设计时拖入的属性。 最后再去运行时的页面查看效果,与代码逻辑一致!

    2024年5月23日
    1.2K00
  • 2.1 数字化时代软件业的另一个本质变化

    随着企业从信息化向数字化转变,软件公司提供的产品也由传统的企业管理软件向企业商业支撑软件发展。这一变化带来了许多技术上的挑战和机遇。在之前的章节中,我们提到企业的视角已经从内部管理转向业务在线和生态在线协同,这也带来了一系列新的需求。但是,我们常常会忽视这一变化所带来的对系统要求的变化。在本章中,我们将探讨这些技术上的变化,以及这些变化所带来的机遇和挑战。 图2-1 从信息化到数字化软件本质变化 在信息化时代,企业的业务围绕着内部管理效率展开,借鉴国外优秀的管理经验,企业将其管理流程固化下来,典型的例子是ERP项目。这类项目上线后往往长期稳定,不轻易更改,因此信息化时代软件的技术流派侧重于通过模型对业务进行全面支持。例如,SAP具有丰富的配置能力,将已有企业管理思想抽象到极致。其功能基本上可以通过配置来实现,因此其模型设计特别复杂。但是,我们也应该清楚地了解到,配置是面向已知问题的。在数字化时代,创新和业务迭代速度非常快,这种方法可能就不太适合了。我们知道,模型抽象是在设计时具有前瞻性的,一旦不适合,修改起来就会异常困难。 随着数字化时代的到来,企业主的关注点已经从单一企业内部管理转变为了围绕企业上下游价值链的协同展开。这种变化给企业信息化系统提出了更高的要求,例如业务需求的响应速度、系统性能和用户体验等方面。现在,企业对软件不仅是管理需求的承载,更是业务在线化的承载。传统的重模型设计软件模式已经不再适用,因为业务本身不断创新和变化。因此,数字化时代需要新的软件技术流派,这种流派必须是轻模型加上低代码技术的结合体。通过模型抽象80%的通用场景,剩余的20%个性化需求可以通过技术手段来完成。这样的设计可以让每家企业的研发人员轻松理解模型,而不像ERP模型那样异常复杂,无法进行修改。此外,配合低代码技术可以快速研发和上线。如果说配置化是面向已知问题的,那么低代码就是面向未知问题设计的。虽然低代码的概念可以追溯到上个世纪80年代,当时是为了满足企业内部部门之间有协同需求,但又没有专业软件支撑,定制化开发又不划算的辅助场景。但现在它的核心原因是企业数字化的核心场景不稳定,变化很快,每家企业都有强烈的个性化需求。因此,低代码成为解决这些问题的核心手段,数字化时代的低代码需要具备处理复杂场景的能力,而不仅仅是围绕着内部管理展开。 企业在数字化转型的过程中需要考虑到不仅是成熟的全链路业务解决方案,还要应对数字化场景的快速变化和持续创新的需求。为此,Oinone打造了一站式低代码商业支撑平台,从业务与技术两个维度来帮助企业建立开放、链接、安全的数字化平台。这将在水平和垂直两个维度上全面推动企业数字化转型。 另外,低代码的另一个好处是完成了软件本身的数字化建设。通过基于元数据设计,元数据成为软件中数据、逻辑和交互的数据,软件结合AI可以有更多的创造可能。想象一下,AI了解软件的元数据后可以自我运作,人在极少情况下才需要参与,人机交互也会发生大的改变。未来的软件交互不再需要研发提前预设,而是能够实现用户所需即所呈现的效果。作为一家帮助企业进行数字化转型的软件公司,请问您的数字化转型是否已经完成呢?

    2024年5月23日
    1.3K00

Leave a Reply

登录后才能评论