云服务器内容精选

  • 脚本开发流程 脚本开发功能提供如下能力: 提供在线脚本编辑器,支持进行SQL、Shell、Python等脚本在线代码开发和调测。 支持导入和导出脚本。 支持使用变量和函数。 提供编辑锁定能力,支持多人协同开发场景。 支持脚本的版本管理能力,支持生成保存版本和提交版本。 保存版本时,一分钟内多次保存只记录一次版本。对于中间数据比较重要时,可以通过“新增版本”按钮手动增加保存版本。 支持单击右键,可快速复制脚本名称,同时可以快速的关闭已打开的脚本页签。 在 MRS API连接模式下,MRS Spark SQL和MRS Hive SQL脚本运行完以后,在执行结果中查看运行日志,增加一键跳转MRS Yarn查看日志的链接。 企业模式下,开发脚本时,鼠标放置在上,单击“前往发布”跳转到任务发布页面。 支持对“已提交”和“未提交”的脚本进行筛选。未提交的脚本通过红色进行标识。 系统支持脚本参数以弹框的形式进行展示,参数名不能修改,参数值可以修改。你可以单击“测试参数”查看脚本中所引用的参数信息,同时可以查看环境中已配置的环境变量信息,不可修改,SQL语句中的参数可以按照参数名进行排序。 支持SQL编辑器风格配置。鼠标放置在上,单击“风格配置”,可以对编辑器、操作栏、注释模板进行配置、以及查询SQL脚本编辑器可使用的快捷键。 SQL查询结果展示支持表格和列表两种展示方式。单击“风格配置”,在“编辑器配置”里面可以对SQL查询结果展示进行配置。 企业模式下,支持从脚本开发界面快速前往发布。鼠标放置在上,单击“前往发布”,进入待发布任务界面。 支持Hive SQL、 DLI SQL、DWS SQL、RDS SQL和Impala SQL脚本可以查看右侧的数据表,单击表名前面的单选框,可以查看该数据的列名、字段类型和描述。 支持通过“责任人”和“更新时间”进行过滤查询,方便快速过滤出最近更新的脚本。 脚本开发支持细粒度权限管控,在数据安全模块对数据开发脚本目录权限管控策略进行配置。 脚本开发的使用流程如下: 图1 脚本开发流程 新建脚本:新建相应类型的脚本。具体请参见新建脚本。 开发脚本:基于新建的脚本,进行脚本的在线开发、调试和执行。具体请参见开发脚本。 提交版本并解锁:脚本开发完成后,您需要提交版本并解锁,提交版本并解锁后才能正式地被作业调度运行,便于其他开发者修改。具体请参见提交版本。 (可选)管理脚本:脚本开发完成后,您可以根据需要,进行脚本管理。具体请参见(可选)管理脚本。 发布脚本。企业模式下需要发布脚本,具体请参见发布脚本任务。 父主题: 脚本开发
  • 标准库模块 低代码平台中有些模块会被编译成二进制,预先加载到内存中。例如,decimal、context都是系统预置的标准库模块。 在低代码平台中,会优先加载标准库模块, 例如: import * as http from 'http'; 始终返回内置的HTTP模块,即使有同名自定义模块。如果需要返回自定义的模块,请使用相对路径语法: import * as http from './http';
  • 循环依赖 当循环调用模块时,一个模块可能在未完成执行时被返回。因此,需要仔细的规划模块间调用,以允许循环模块依赖在应用程序内正常工作。 例如,脚本a: console.log('a 开始'); exports.done = false; import * as b from 'b'; console.log('在 a 中,b.done = ', b.done); exports.done = true; console.log('a 结束'); 脚本b: console.log('b 开始'); exports.done = false; import * as a from 'a'; console.log('在 b 中,a.done = ', a.done); exports.done = true; console.log('b 结束'); 脚本main: console.log('main 开始'); import * as a from 'a'; import * as b from 'b'; console.log('在 main 中,a.done = ', a.done ',b.done = ', b.done); 当main加载a时,a又加载b。 此时,b又会尝试去加载a。 为了防止无限的循环,会返回一个a的exports对象的未完成的副本给b模块。然后b完成加载,并将exports对象提供给a模块。当main加载这两个模块时,它们都已完成加载,因此该程序的输出会是: main 开始 a 开始 b 开始 在 b 中,a.done = false b 结束 在 a 中,b.done = true a 结束 在 main 中,a.done=true,b.done=true
  • 扩展名 因为脚本实际上是存在数据库中的,所以脚本没有路径的概念,扩展名也没有特别的意义。 导入模块时,尽量采用不带扩展的方式。 import * as circle from './circle'; 平台只允许“.ts”扩展名文件存在,不允许包含“.js”后缀的模块,请尽量不要使用待扩展名的导入方式。如下方式等同上面的举例,但不推荐。 import * as circle from './circle.ts';
  • 规则7:SELECT语句中严禁使用“select from...”形式查询语句 严禁使用“select ...”形式查询语句,请指出select的具体字段。 错误代码示例 import * as db from 'db'; let errorDemo = db.sql().exec("select from object_demo where object_name = 'test'") 正确代码示例 import * as db from 'db'; let correctDemo = db.sql().exec("select id, object_type from object_demo where object_name = 'test'")
  • 规则8:SELECT语句中拼接的参数值请谨慎使用入参变量 SELECT语句中,拼接的参数值请谨慎使用入参变量,以免引起SQL注入的风险。 错误代码示例 import * as db from 'db'; let errorDemo = "select id,name from object_demo where id = "; errorDemo += input.parameter let errorDemoResult = db.sql().exec(errorDemo) 其中,“input.parameter”为脚本入参。 正确代码示例 import * as db from 'db'; let correctDemo = "select id,name from object_demo where id = ?"; let correctDemoResult = db.sql().exec(correctDemo, { params: [input.parameter] }) 其中,“input.parameter”为脚本入参。
  • 规则10:SELECT语句中谨慎使用order by SELECT语句中,请谨慎使用order by。如果需要使用order by,请为排序字段增加索引,以提高查询效率。如果无法增加索引,需要关注是否存在查询性能低下的风险。 错误代码示例 import * as db from 'db'; let errorDemo = db.sql().exec("select object_name from object_demo where object_id = 'A' Orde by createdDate") 表“object_demo”中的“createdDate”,并没有创建索引。 正确代码示例 import * as db from 'db'; let correctDemo = db.sql().exec("select object_name from object_demo where id = 'A' Order by createdDate") “object_demo”中的“createdDate”,创建了索引。
  • 规则4:SELECT语句中查询字段不在表的索引库中 如果SELECT语句where条件中,查询字段并未创建索引,请判断该字段是否需要创建索引,以提高代码查询效率。 错误代码示例 import * as db from 'db'; let errorDemo = db.sql().exec("select object_name from object_demo where object_id = ?") 表“object_demo”中的“object_id”并没有创建索引。 正确代码示例 import * as db from 'db'; let correctDemo = db.sql().exec("select object_name from object_demo where id = ?") 表“object_demo”中的“id”创建了索引。
  • 规则6:SELECT语句中聚集函数必须增加别名 SELECT语句中,聚合函数必须使用别名方式存储查询结果,以免因聚合函数返回的结果不一致,导致存在兼容性问题。 错误代码示例 import * as db from 'db'; let errorDemo = db.sql().exec("select count(*) from object_demo where object_name = 'A'") 正确代码示例 import * as db from 'db'; let correctDemo = db.sql().exec("select count(*) as count from object_demo where object_name = 'A'") 推荐代码示例 ***聚合函数示例 **** select count(*) as count_res, select max(*) as max_res, select min(*) as min_res, select avg(*) as avg_res, select sum(*) as sum_res
  • 创建空白脚本 参考登录经典应用设计器中操作,登录经典版应用设计器。 将光标放在某个文件夹(例如Logic)上,单击,选择“脚本”。 图1 选择创建图标“+” 选择“创建一个新脚本”,设置脚本名称(如httpTest),单击“添加”,进入脚本编辑器。 图2 新增脚本 在脚本编辑器中,将以下脚本代码粘贴到代码编辑区。 import * as http from 'http' // 导入http库 let client = http.newClient() // 实例化 let resp = client.get("https://www.example.com/")// 请求网址 console.log(resp.headers) // 打印返回headers 单击页面上方的,保存脚本。 运行测试脚本。 单击编辑器上方的,执行脚本。 在页面底部单击测试窗口右上角的,返回消息头信息。 { Age: [ '10309' ], Cache-Control: [ 'no-store' ], Content-Type: [ 'text/html; charset=utf-8' ], Date: [ 'Thu, 01 Dec 2022 03:41:39 GMT' ], Expires: [ 'Wed, 30 Nov 2022 22:01:57 GMT' ], Pragma: [ 'no-cache' ], Processtime: [ '0.049' ], Server: [ 'PAAS-WEB' ], Set-Cookie: [ 'HMF_CI=596020aa4dbe4b6***42343ba04a; Expires=Sat, 31-Dec-22 03:41:39 GMT; Path=/' ], Strict-Transport-Security: [ 'max-age=31536000' ], Ws-S2h-Acc-Level: [ '1' ], X-Content-Type-Options: [ 'nosniff' ], X-Download-Options: [ 'noopen' ], X-Frame-Options: [ 'SAMEORIGIN' ], X-Powered-By: [ 'ASP.NET' ], X-Via: [ '1.1 PSjsczsxga60:2 (Cdn Cache Server V2.0), 1.1 uzhoudianxin101:13 (Cdn Cache Server V2.0), 1.1 wdx18:10 (Cdn Cache Server V2.0)' ], X-Ws-Request-Id: [ '63882273_wdx18_41265-37840' ], X-Xss-Protection: [ '1; mode=block' ] } 以上步骤已完成了一个简单脚本的编写,如果后续其他脚本、服务编排、编译打包等需要继续使用该脚本,则必须单击页面上方的,启用该脚本。如果未启用,其他组件无法查询到该脚本,查询时会报错该脚本不存在或未启用。 后续在新版本功能中如果需要更新该脚本,可单击编辑器上方的,选择“新建版本”,在新建的版本中更改脚本并保存、测试及启用。单击编辑器上方的,选择对比版本,可将当前版本与历史版本进行比对。
  • 开发一个业务场景脚本实例 本小节创建的业务场景脚本相对于开发一个简单脚本实例,具有更为复杂的业务逻辑。 本脚本实例实现的业务场景:根据对象的数据生成资源列表数据,用于前台数据呈现,启用本脚本后,可实现数据导入模板使用。 创建脚本之前,需要先创建脚本中操作的对象ApprovedResource,其字段如表1所示。 表1 对象说明 字段标签 字段名称 字段类型 取值 读写权限 含义 ownerId ownerId 文本 长度:255 全选 资源拥有者ID ResourceName ResourceName 文本 长度:255 全选 资源名称 Type Type 选项列表 枚举值如下: File Folder 全选 资源类型 SubmitDate SubmitDate 日期/时间 不涉及 全选 资源提交时间 ApproveDate ApproveDate 日期/时间 不涉及 全选 资源审批时间 Status Status 选项列表 枚举值如下: Waiting Submitted Approved 全选 审批状态
  • 如何定制已有脚本 您可以全新创建一个脚本,也可以根据业务需要修改已有脚本,如使用如何通过模板进行数据导入的BulkInsert脚本为例。 在以下两种场景下,您需要修改已有脚本: 场景1:在已有脚本基础上新建版本。业务需要,脚本要实现的能力变化,此时您可以基于原有脚本修改,并保存为脚本的新版本。在此场景下,原有脚本作为老版本自动失效。 场景2:在已有脚本基础上新建脚本。您需要开发的新脚本与已有的某个脚本类似,此时您可以基于原有脚本修改,再保存为新的脚本名称。在此场景下,原有脚本仍然有效。新脚本和老脚本也没有关联关系,各自独立。 场景1:在已有脚本基础上新建版本: 根据已有的“BulkInsert”版本1.0.1的脚本 ,开发“BulkInsert”版本1.0.2的脚本。 在应用开发工作台Logic目录下,单击“BulkInsert”脚本。 单击编辑器上方的另存为图标,选择“新建版本”,单击“保存”。 图5 另存为“BulkInsert”脚本 图6 保存新建版本 编辑新脚本版本的内容,修改后单击页面上方,保存修改。 图7 修改版本后保存与启用 测试脚本,确保其运行结果符合预期后,再单击启用脚本。 场景2:在已有脚本基础上新建脚本: 根据已有的“BulkInsert”脚本 ,开发“BulkInsertOther”。 在应用开发工作台Logic目录下,单击“BulkInsert”脚本。 单击编辑器上方的另存为图标,选择“新建脚本”,输入新脚本名“BulkInsertOther”,单击“保存”。 图8 新建脚本 编辑新脚本的内容,修改后单击上方图标,进行保存。 测试脚本,确保其运行结果符合预期后,再单击启用脚本。
  • 场景说明 根据对象的数据生成资源列表数据,用于前台数据呈现,启用本脚本后,可实现数据导入模板使用。 创建脚本前,需要先创建脚本中操作的对象ApprovedResource,其字段如表1所示。 表1 对象说明 字段标签 字段名称 字段类型 取值 读写权限 含义 ownerId ownerId 文本 长度:255 全选 资源拥有者ID ResourceName ResourceName 文本 长度:255 全选 资源名称 Type Type 选项列表 枚举值如下: File Folder 全选 资源类型 SubmitDate SubmitDate 日期/时间 不涉及 全选 资源提交时间 ApproveDate ApproveDate 日期/时间 不涉及 全选 资源审批时间 Status Status 选项列表 枚举值如下: Waiting Submitted Approved 全选 审批状态
  • 样例代码解读 通过以下详细的脚本代码内容解读,对脚本有一个更具体的认识。 一般情况下,编写脚本的大致流程为: 按需引入平台标准库。 图11 引入平台标准库 定义出参、入参结构。 图12 定义入参 图13 定义出参 定义方法以及使用的对象。 图14 定义方法及使用对象 进行数据库操作。 图15 数据库相关操作 以下将通过解读一个脚本样例,带您了解脚本的总体结构框架及编写要求。 import * as decimal from 'decimal'; @action.object({type: "param"}) export class ActionInput { @action.param({type: 'String', required: true, label: 'your name', description: 'please input your name'}) name: string; @action.param({type: 'Number', required: true, min: 1, max: 100, message: 'age must during [1, 100]'}) age: decimal.Decimal; @action.param({type: 'Date', pattern: 'yyyy-MM-dd'}) birthday: Date; @action.param({type: 'String', isCollection: true}) schools: string[]; @action.param({type: 'Boolean'}) married: boolean; @action.param({type: 'MyObject'}) obj: MyObject; } @action.object({type: "param"}) export class MyObject { @action.param({type: 'String'}) something: string; @action.param({type: 'Number'}) otherthing: decimal.Decimal; } @action.object({type: "param"}) export class ActionOutput { @action.param({type: 'String', isCollection: true}) greets: string[]; } @action.object({type: "method"}) export class ActionDemo { @action.method({ label: 'greeting something', description: 'greeting something.', input: 'ActionInput', output: 'ActionOutput' }) public greet(inarg: ActionInput): ActionOutput { console.log('name = ', inarg.name); console.log('age = ', inarg.age); console.log('birthday = ', inarg.birthday); console.log('schools = ', inarg.schools); console.log('married = ', inarg.married); console.log('obj = ', inarg.obj); let out = new ActionOutput(); out.greets = ['hello', 'hi', 'how are you', 'how old are you', 'long time no see']; return out; } } 上述示例脚本主要分为如下三部分: 导入标准库或其他模块。 上例中,第1行表示使用平台提供的decimal库。 import * as decimal from 'decimal'; 除了平台预置的标准库,还可以声明对其他自定义模块的引用。例如,已提前开发了一个脚本cirle,可以用如下方式加载它。 import * as circle from './circle'; 定义输入、输出变量。 脚本可以有多个输入、输出参数,也可以没有。所有的输入或输出参数,必须封装在一个class中,作为实例成员。 本例中,脚本有6个输入参数,被封装为ActionInput。每个参数都必须定义其参数类型,同时还可以定义是否必填、标签、最大值、最小值等可选属性。 @action.object({type: "param"}) export class ActionInput { @action.param({type: 'String', required: true, label: 'your name', description: 'please input your name'}) name: string; @action.param({type: 'Number', required: true, min: 1, max: 100, message: 'age must during [1, 100]'}) age: decimal.Decimal; @action.param({type: 'Date', pattern: 'yyyy-MM-dd'}) birthday: Date; @action.param({type: 'String', isCollection: true}) schools: string[]; @action.param({type: 'Boolean'}) married: boolean; @action.param({type: 'MyObject'}) obj: MyObject; } 因为第6个输入参数“obj”的参数类型为自定义对象,所以还需要给出“ MyObject”的定义。 @action.object({type: "param"}) export class MyObject { @action.param({type: 'String'}) something: string; @action.param({type: 'Number'}) otherthing: decimal.Decimal; } 脚本中有1个输出参数,被封装为ActionOutput。 @action.object({type: "param"}) export class ActionOutput { @action.param({type: 'String', isCollection: true}) greets: string[]; } 定义方法 样例中,ActionDemo是外部调用的class,使用export导出。ActionDemo定义了一个action method,使用action.method装饰,表明调用脚本时从此方法入口。greet是class的实例方法,其输入、输出参数就是前面定义的ActionInput和ActionOutput。在一个脚本文件中,action.method只能使用一次。 @action.object({type: "method"}) export class ActionDemo { @action.method({ label: 'greeting something', description: 'greeting something.', input: 'ActionInput', output: 'ActionOutput' }) public greet(inarg: ActionInput): ActionOutput { console.log('name = ', inarg.name); console.log('age = ', inarg.age); console.log('birthday = ', inarg.birthday); console.log('schools = ', inarg.schools); console.log('married = ', inarg.married); console.log('obj = ', inarg.obj); let out = new ActionOutput(); out.greets = ['hello', 'hi', 'how are you', 'how old are you', 'long time no see']; return out; } } 脚本编辑页面不支持单步调试,样例里的console.log可实现在日志里打印过程输出,方便代码调试。
  • 提供AI代码补全功能 代码补全引擎会从开发者历史创建脚本中,学习其编码习惯,从当前代码脚本中获得代码上下文语法知识。开发者在编写脚本代码过程中,系统根据代码上下文和历史代码,对将要编写的代码进行提示,从而自动构建智能代码补全服务,增强IDE代码补全能力。 AI代码补全功能具体使用方法:开发者在代码编辑器中,编写脚本代码过程中,敲击字符时自动触发代码推荐,选中后按Enter补全。开发者将鼠标移动到待查看详细信息的字段,将字段悬浮窗中的文字选中复制粘贴到代码中。 如果想深入了解AI代码补全功能,请继续阅读以下内容: 常规补全:开发者编码过程中,输入任意字符均会触发的补全提示。 例如:开发者键入“@u”,如下图所示,系统根据意图在代码编辑器中给出最可能使用的方法。 定制补全:低代码平台脚本开发中,提供了内部预置系统级依赖库,供开发者调用。用户也可以在前台页面配置对象数据、系统参数、错误码、事件和工作流后,在脚本中引用这些内容。定制补全是指在脚本开发中引用依赖库和各配置项时,IDE触发的对潜在内容名称的补全提示。 依赖库补全:脚本代码中引入依赖的代码行时,对依赖库的名称进行推荐提示。如下图所示,开发者键入“import * as xxx from”后,提示z开头的依赖库名称。 表名(即对象名)补全:脚本代码引入对象数据的代码行中,推荐提示对象名称。如下图所示,开发者键入“@useObject”后提示m开头的对象名。 表字段提示:脚本代码中引用表字段时,鼠标移动到表名上,悬浮窗会对表名进行提示。如下图所示,开发者鼠标放在表名上,展示所有字段。 错误码补全:脚本中引入数据表的代码行中,对表名称进行推荐提示。如下图所示,开发者键入“setI18nError”后,提示相关错误码名。 错误码内容提示:脚本代码中引入数据表的代码行中,对表名称进行推荐提示。如下图所示,鼠标放在错误码名称上,展示相关描述。 系统参数补全:脚本代码中引入系统参数的代码行中,对参数名称进行推荐提示。如下图所示,开发者键入“sys.getParameter”后,提示相关系统参数。