华为云用户手册

  • 创建“生成工单”脚本 在“我的应用”中,单击“设备维修管理系统”,进入应用。 在“WorkOrder”目录中,将鼠标放在“Script”上,单击界面上出现的“+”,在弹出菜单中选择“脚本”。 在弹窗中,选中“创建一个新脚本”,在“名称”文本框中输入“createWorkOrder”,单击“添加”。 当编辑已有脚本时,为防止编辑时多人篡改,编辑前请单击进行锁定。 在代码编辑器中插入如下脚本代码。 脚本中红色内容请替换为实际的对象名、字段名。 //本脚本用于创建工单 import * as db from 'db';//导入处理object相关的标准库 import * as context from 'context';//导入上下文相关的标准库 import * as date from 'date'; //定义入参结构,入参包含1个参数:workOrder对象,为必填字段 @action.object({ type: "param" }) export class ActionInput { @action.param({ type: 'Object', required: true, label: 'object' }) workOrderData: object; } //定义出参结构,出参包含1个参数,workOrder的记录id @action.object({ type: "param" }) export class ActionOutput { @action.param({ type: 'String' }) id: string; } //使用数据对象HW__WorkOrder__ CS T @useObject(['HW__WorkOrder__CST']) @action.object({ type: "method" }) export class CreateWorkOrder { //定义接口类,接口的入参为ActionInput,出参为ActionOutput @action.method({ input: 'ActionInput', output: 'ActionOutput' }) public createWorkOrder(input: ActionInput): ActionOutput { let out = new ActionOutput(); //新建出参ActionOutput类型的实例,作为返回值 let error = new Error(); //新建错误类型的实例,用于在发生错误时保存错误信息 try { let workOrderData = input.workOrderData; //将入参赋值给workOrderData变量,方便后面使用 let s = db.object('HW__WorkOrder__CST'); //获取HW__WorkOrder__CST这个Object的操作实例 let userName = context.getUserName(); workOrderData['HW__status__CST'] = '待派单'; workOrderData['HW__assignedFme__CST'] = '派单员'; workOrderData['HW__createdBy__CST'] = userName; let id = s.insert(workOrderData); if (id) { out.id = id; } else { error.name = "WOERROR"; error.message = "无法创建工单!"; throw error; } } catch (error) { console.error(error.name, error.message); context.setError(error.name, error.message); } return out; } } 单击编辑器上方,保存脚本。 测试脚本能否正常执行。 单击编辑器上方的,执行脚本。 在界面底部输入测试数据,单击测试窗口右上角执行图标。 测试报文样例如下: { "workOrderData":{ "createdDate":"2020-09-30 12:00:00", "HW__recoveryTime__CST":"2020-09-30 12:00:00", "HW__arriveTime__CST":"2020-09-30 12:00:00", "HW__title__CST":"电梯无法关门", "HW__priority__CST":"高", "HW__workOrderId__CST":"WD0000123456", "HW__instanceId__CST":"" } } 执行成功,会在“输出”页签返回查询结果。 图1 输出工单ID号 测试成功,单击编辑器上方的,启用脚本。
  • 操作步骤 进入创建“设备维修管理系统”应用中创建的应用。 单击页面下方的“服务”,进入公共接口创建页面。 图1 创建公共接口入口 单击“新建”,进入公共接口创建页面。 图2 公共接口创建 创建“删除设备”脚本对应的“HW__deleteEquipment”的公共接口。 设置接口参数信息,设置操作名称为“deleteEquipment”,版本为“1.0.0”,URL为“/deleteEquipment”,“类型”选择“脚本”,“资源”为“HW__deleteEquipment”,方法为“DELETE”,然后单击“保存”。 如果在“资源”下拉框中,未找到需要关联的脚本或服务编排,请检查相关脚本和服务编排是否已启用。 参照上一步,创建表1中其他的公共接口,详细接口信息如表1所示。 “HW__”请修改为实际命名空间前缀。 表1 公共接口 设置操作 版本 URL 方法 类型 资源 deleteEquipment(上一步已创建) 1.0.0 /deleteEquipment DELETE 脚本 HW__deleteEquipment equipmentSelectListQuery 1.0.0 /equipmentSelectListQuery GET 脚本 HW__equipmentSelectListQuery queryEquipments 1.0.0 /queryEquipments POST 服务编排 HW__queryEquipments
  • 验证 单击脚本编辑器上方的,执行脚本。 在界面底部单击测试窗口右上角执行图标,进行测试。 如果不设置输入参数,会出现类似如下样例的测试结果(设备名称和设备id)。 { "equipList": [ { "display": "百草园B栋2单元2号", "value": "cQue000000e1qnhgtCng" } ] } 如果设置如下输入参数。 { "id":"cQue000000e1qnhgtCng" } 则脚本返回设备的详细信息,样例如下。 测试成功,单击编辑器上方的,发布脚本。
  • 操作步骤 进入创建“设备维修管理系统”应用中创建的应用。 在“Equipment”目录中,将鼠标放在“Script”上,单击界面上出现的“+”,在弹出菜单中选择“脚本”。 选中“创建一个新脚本”,在“名称”文本框中输入“equipmentSelectListQuery”,单击“添加”。 在代码编辑器中插入如下脚本代码。 脚本中红色内容请替换为实际的对象名、字段名。 import * as context from 'context'; import * as decimal from 'decimal'; import * as db from 'db'; //使用数据库对象HW__Equipment__CST @useObject(['HW__Equipment__CST']) @action.object({ type: "param" }) export class ActionInput { @action.param({ type: 'String' }) id: string; } @action.object({ type: "param" }) export class ActionOutput { @action.param({ type: 'Any', label: 'object', isCollection: true }) equipList: object[]; @action.param({ type: 'Any', label: 'object' }) equipment: object; } @action.object({ type: "method" }) export class EquipmentSelectListQuery { @action.method({ input: 'ActionInput', output: 'ActionOutput' }) public equipmentSelectListQuery(input: ActionInput): ActionOutput { let out = new ActionOutput(); let error = new Error; try { let objAct = db.object('HW__Equipment__CST'); if (input.id) { out.equipment = objAct.query(input.id); return out; } let option = { "options": { "orderby": [ { "field": "lastModifiedDate", "order": "desc" } ], "skip": 0, "limit": 1000 } }; let equipList = objAct.queryByCondition(null, option); let selectValue = equipList.map(function (v, i, a) { return { 'value': v['id'], 'display': v['name'] } }); out.equipList = selectValue; } catch (error) { console.error(error.name, error.message); context.setError(error.name, error.message); } return out; } } 单击脚本编辑器上方的,保存脚本。
  • 操作步骤 进入创建“设备维修管理系统”应用中创建的应用。 在“Equipment”目录中,将鼠标放在“Script”上,单击界面上出现的“+”,在弹出菜单中选择“脚本”。 选中“创建一个新脚本”,在“名称”文本框中输入“deleteEquipment”,单击“添加”。 当编辑已有脚本时,为防止编辑时多人篡改,编辑前请单击进行锁定。 在代码编辑器中插入如下脚本代码。 脚本中红色内容请替换为实际的对象名、字段名。 //本脚本用于删除设备 import * as db from 'db';//导入处理object相关的标准库 import * as context from 'context';//导入上下文相关的标准库 //定义入参结构,入参包含1个参数:Equipment对象,为必填字段 @action.object({ type: "param" }) export class ActionInput { @action.param({ type: 'String', required: true, label: 'String' }) id: string; } //定义出参结构,出参包含1个参数,Equipment的记录id @action.object({ type: "param" }) export class ActionOutput { @action.param({ type: 'String' }) id: string; } //使用数据对象HW__Equipment__CST @useObject(['HW__Equipment__CST']) @action.object({ type: "method" }) export class DeleteEquipment { //定义接口类,接口的入参为ActionInput,出参为ActionOutput @action.method({ input: 'ActionInput', output: 'ActionOutput' }) public deleteEquipment(input: ActionInput): ActionOutput { let out = new ActionOutput(); //新建出参ActionOutput类型的实例,作为返回值 let error = new Error(); //新建错误类型的实例,用于在发生错误时保存错误信息 try { let id = input.id; let s = db.object('HW__Equipment__CST'); //获取HW__Equipment__CST这个Object的操作实例 //查询条件 let condition = { "conjunction": "AND", "conditions": [{ "field": "id", "operator": "eq", "value": id }] }; let isDeleted = s.deleteByCondition(condition); if (isDeleted) { out.id = id; } else { error.name = "EQERROR"; error.message = "删除设备失败!"; throw error; } } catch (error) { console.error(error.name, error.message); error.Error(error.name, error.message); } return out; } } 单击脚本编辑器上方的,保存脚本。
  • 操作步骤 进入创建“设备维修管理系统”应用中创建的应用。 在“Equipment”目录中,将鼠标放在“Flow”上,单击界面上出现的,在弹出菜单中选择“服务编排”。 选中“创建一个新的服务编排”,在“标签”和“名称”文本框中输入“queryEquipments”,并设置类型为“Autolaunched Flow”,单击“添加”。 系统实际创建的脚本名称为“HW__queryEquipments”,“HW__”前缀由租户命名空间namespace决定。新建创建的服务编排,默认是当前用户锁定状态,可以进行编辑保存等操作。 当编辑已有服务编排时,为防止编辑时多人篡改,编辑前请单击进行锁定。 定义服务编排用到的变量。 单击页面右侧的,再单击结构体中的“新增”,在弹出窗口中输入结构体类型名称“Equipment”,单击“保存”。 图1 创建私有结构体 单击,再单击“对象变量”后的“+”,在弹出窗口中定义私有结构体变量“equipments”,选中“是否为数组”(该变量将作为服务编排的输出参数,且因为查询结果可能是多条记录,所以要选)。单击“保存”,如图2所示。 图2 新增私有结构体变量 单击“变量”后的“+”,设置参数名称为“name”。 单击变量后的“...”,选择“设置”,修改变量的名称、类型,选择默认值“{!$GlobalConstant.EmptyString}”信息,如图4所示。 图3 新增变量 图4 设置变量 重复上一步,定义表1中的其他变量。 表1 服务编排变量说明 变量唯一标识 描述 数据类型 默认值 name(上一步已定义) 设备名称 文本 {!$GlobalConstant.EmptyString} fullAddress 完整地址 文本 {!$GlobalConstant.EmptyString} limit 分页查询的条目数 数字 {!$GlobalConstant.Null} start 分页查询开始条目 数字 {!$GlobalConstant.Null} total 查询到的总条目数 文本 不设置 在服务编排中添加并配置脚本图元。 从左侧拖拽脚本图元到画布中。 修改图元的基本信息,“标签”和“名称”都设置为“queryEquipments”。 图5 设置脚本图元基本信息 单击,指定图元对应的脚本名称 (HW__queryEquipments),并配置脚本的输入输出参数,如果下拉选项中没有目标脚本,请检查脚本是否已启用。 单击“全局上下文”,显示变量列表。在“输入参数”中,单击“新增行”,在下拉框中选择输入参数、依次拖拽变量到输入参数的“源”下。 在“输出参数”中,单击“新增行”,在下拉框中选择输出参数,并从变量和对象变量中拖拽“目标”,如图6所示。 请直接从全局上下文拖拽变量到输入输出参数下的对应位置,请勿手动输入,手动输入的值系统可能不识别。 图6 拖拽脚本的输入输出参数 定义服务编排的输入、输出参数,并保存服务编排。 在画布上,把鼠标放在起点图元图元上,从“+”拖动鼠标,在起点图元和脚本图元间增加连线;即将当前脚本设置为服务编排的起始节点。 鼠标在画布空白处点一下,单击右侧,设置服务编排的输入输出参数,如图7所示。 图7 拖拽服务编排的输入输出参数 单击服务编排界面上方的,保存服务编排。 测试服务编排能否正常执行。 单击服务编排编辑器上方的,执行服务编排。 在“输入参数”中,输入测试数据,单击“运行”。 执行成功,界面上会返回设备对象中的全部信息,样例如下: 图8 返回样例 (可选)在服务编排编辑器单击“跟踪”,可以查看到上一步的执行日志,方便定位错误。 测试成功,单击服务编排编辑器上方的,发布服务编排。
  • 操作步骤 进入创建“设备维修管理系统”应用中创建的应用。 在“Equipment”目录中,将鼠标放在“Script”上,单击界面上出现的,在弹出菜单中选择“脚本”。 在弹窗中,选中“创建一个新脚本”,在“名称”文本框中输入“queryEquipments”,单击“添加”。 系统实际创建的脚本名称为“HW__queryEquipments”,“HW__”前缀由租户命名空间namespace决定。新建创建的脚本,默认是当前用户锁定状态,可以进行编辑保存等操作。 当编辑已有脚本时,为防止编辑时多人篡改,编辑前请单击进行锁定。 在代码编辑器中,插入如下脚本代码。 脚本中红色内容请替换为实际的对象名、字段名。 /**************************** * 本脚本用于按条件查询设备列表 * **************************/ import * as db from 'db';//导入处理object相关的标准库 import * as context from 'context';//导入上下文相关的标准库 import * as decimal from 'decimal';//导入decimal数据类型相关的标准库 //定义入参结构 @action.object({ type: "param" }) export class ActionInput { @action.param({ type: 'String' }) name: string;//设备名称,需要按设备名称查询时传入 @action.param({ type: 'String' }) fullAddress: string;//完整地址,需要按完整地址查询时传入 @action.param({ type: 'Number', min: 0 }) start: decimal.Decimal;//分页信息,表示从第几条数据开始查询 @action.param({ type: 'Number', min: 0 }) limit: decimal.Decimal;//分页信息,表示一次查询几条数据 } //定义出参结构 @action.object({ type: "param" }) export class ActionOutput { @action.param({ type: 'Any', isCollection: true, label: 'object' }) equipments: object[];//设备列表 @action.param({ type: 'String' }) total: string;//总共查到几条数据 } @useObject(['HW__Equipment__CST']) //使用数据库对象HW__Equipment__CST @action.object({ type: "method" }) export class QueryEquipments { @action.method({ input: 'ActionInput', output: 'ActionOutput' }) public queryEquipments(input: ActionInput): ActionOutput { let out = new ActionOutput(); //新建出参ActionOutput类型的实例,作为返回值 try { //获取HW__Equipment__CST这个Object的操作实例 let s = db.object('HW__Equipment__CST'); //option是db标准库queryByCondition方法的入参(选项选项),用于对查询结果排序、分组、聚合计算等 //这里构造的是按创建时间逆序返回查询结果 let option = { "options": { "orderby": [ { "field": "createdDate", "order": "desc" } ], } }; //如果有分页 if (input.start && input.limit) { let start = decimal.toNumber(input.start);//将decimal类型转换为接口需要的number类型 let limit = decimal.toNumber(input.limit); option.options['limit'] = limit; option.options['skip'] = start; } //condition是db标准库queryByCondition方法的入参(查询条件) let condition = { "conjunction": "AND", "conditions": [] }; //基本查询条件 condition.conditions.push({ "field": "id", "operator": "isnotnull", }); condition.conditions.push({ "field": "id", "operator": "ne", "value": "" }); //按设备名称查询 if (input.name && input.name != "") { condition.conditions.push({ "field": "name",//与对象中的字段名保持一致 "operator": "contains", "value": input.name }); } //按设备地址查询 if (input.fullAddress && input.fullAddress != "") { condition.conditions.push({ "field": "HW__fullAddress__CST", "operator": "contains", "value": input.fullAddress }); } //调用按条件查询HW__Equipment__CST的接口 out.equipments = s.queryByCondition(condition, option); //构造实时监控页面需要的属性字段longitude__CST、latitude__CST、fullAddress__CST和equipmentSn__CST for (let equip of out.equipments || []) { equip['longitude__CST'] = equip['HW__longitude__CST'];//与对象中的字段名保持一致 equip['latitude__CST'] = equip['HW__latitude__CST']; equip['fullAddress__CST'] = equip['HW__fullAddress__CST']; equip['equipmentSn__CST'] = equip['HW__equipmentSN__CST']; } //调用查询符合condition条件的数据总数的接口 out.total = s.count(condition) + ""; } catch (error) { console.error(error.name, error.message); context.setError(error.name, error.message); } return out; } } 单击编辑器上方的,保存脚本。 测试脚本能否正常执行。 单击编辑器上方的,执行脚本。 在界面底部,直接单击测试窗口右上角,执行脚本。 执行成功后,会在“输出”页签返回全部设备信息。 测试成功,单击编辑器上方的,启用脚本。
  • 了解服务编排 在传统的开发中程序员一般是基于代码进行开发,程序员需要学习内容较多,开发效率相对低一些,开发门槛也高。AstroZero的服务编排功能,类似于编程中一段有流程、条件处理、判断逻辑的程序。这段程序有输入参数和输出参数、可以独立成为一个对外调用的方法。同时,在程序内部,也可以调用其他的方法。 AstroZero中的服务编排是将原来基于代码编程改变为用图形化,拖拉拽的方式去编程。如图1所示,服务编排界面是图形化、模板化的,您甚至不需要任何编程经验,将左侧面板区的组件拖拽到右侧画布、做必要的配置,就可以完成服务编排的开发。 图1 服务编排界面 服务编排界面中,可以编排如下组件: 基本组件:在服务编排引用脚本或者另一个服务编排,增/改/删/查记录,以及发送邮件、发送事件等。 逻辑组件:在服务编排中实现变量赋值Assignment、循环Loop、跳出循环Break、决策Decision和等待Wait。 商业对象:将封装好的BO能力作为服务编排中的一个节点。 连接器:将短信发送、支付等第3方连接器作为当前服务编排中的一个节点。 除了图形化编排,AstroZero也支持服务编排的在线测试验证,以及问题跟踪调试,方便您及时发现并解决问题。 服务编排测试通过、发布后,既可以直接被前端页面调用,也可以作为restful接口被第三方系统调用,也可以包装成公共接口后被调用。本节中主要是将服务编排包装成一个公共接口后,供页面调用,“管理设备”功能中涉及的业务逻辑,以及服务编排与脚本关系如表1下所示,详细操作方式及说明请参见创建业务逻辑。
  • 操作步骤 在应用中,单击下方“服务”,进入公共接口创建页面。 图1 创建公共接口入口 单击“新建”,进入新建公共接口页面。 图2 公共接口创建 创建“编辑设备”脚本“HW__editEquipment”的公共接口。 设置接口参数信息,设置标签和操作名称为“editEquipment”,版本为“1.0.0”,URL为“/editEquipment”,“类型”选择“脚本”,“资源”为“HW__editEquipment”,方法为“POST”,然后单击“保存”。 如果在“资源”下拉框中,未找到需要关联的脚本,请检查相关脚本是否已启用。 图3 设置“编辑设备”脚本的公共接口参数 在应用开发页面,单击左下角的“服务”,进入公共接口页面,查看上一步中新建的自定义接口URL“/service/HW__MyApp/1.0.0/editEquipment”,后续开发页面时,会使用这个URL。 图4 查看自定义接口URL 参照上一步,创建“按ID查询设备详情”脚本“HW__queryEquipmentDetail”的公共接口,详细接口信息如图5所示。 标签和操作名称为“queryEquipmentDetail”,版本为“1.0.0”,URL为“/queryEquipmentDetail”,“类型”选择“脚本”,“资源”为“HW__queryEquipmentDetail”,“方法”为“GET”。 图5 “HW__queryEquipmentDetail”的公共接口参数
  • 操作步骤 进入创建“设备维修管理系统”应用中创建的应用。 在“Equipment”中,将鼠标放在“Script”目录上,单击界面上出现的“+”,选择“脚本”。 在弹窗中,选中“创建一个新脚本”,在“名称”文本框中输入“queryEquipmentDetail”,单击“添加”。 系统实际创建的脚本名称为“HW__queryEquipmentDetail”,“HW__”前缀由租户命名空间namespace决定。新建创建的脚本,默认是当前用户锁定状态,可以进行编辑保存等操作。 当编辑已有脚本时,为防止编辑时多人篡改,编辑前请单击进行锁定。 在代码编辑器中插入如下脚本代码。 脚本中红色内容请替换为实际的对象名、字段名。 /***************************** * 本脚本用于按设备ID查询设备详情 * ***************************/ import * as db from 'db';//导入处理object相关的标准库 import * as context from 'context';//导入上下文相关的标准库 //定义入参结构 @action.object({ type: "param" }) export class ActionInput { @action.param({ type: 'String', required: true }) equipmentId: string;//设备ID } //定义出参结构 @action.object({ type: "param" }) export class ActionOutput { @action.param({ type: 'Any', label: 'equipment' }) equipment: object;//设备对象 } @useObject(['HW__Equipment__CST'])//使用数据库对象HW__Equipment__CST @action.object({ type: "method" }) export class QueryEquipmentDetail { @action.method({ input: 'ActionInput', output: 'ActionOutput' }) public queryEquipmentDetail(input: ActionInput): ActionOutput { let out = new ActionOutput(); //新建出参ActionOutput类型的实例,作为返回值 let error = new Error(); //新建错误类型的实例,用于在发生错误时保存错误信息 try { //必填校验 if (!input.equipmentId || input.equipmentId == "") { error.name = "EQM"; error.message = "Equipment id is required."; throw error; } //获取HW__Equipment__CST这个Object的操作实例 let s = db.object('HW__Equipment__CST'); //查询字段(全部) let option = {}; //查询条件 let condition = { "conjunction": "AND", "conditions": [{ "field": "id", "operator": "eq", "value": input.equipmentId }] }; //调用按条件查询Equipment__CST的接口 let record = s.queryByCondition(condition, option); //如果查询到数据 if (record && record[0]) { //拼接前台省市区级联选择器的数据模型 let cascaderAddress = []; if (record[0].HW__installationSiteProvince__CST) { cascaderAddress.push(record[0].HW__installationSiteProvince__CST); if (record[0].HW__installationSiteCity__CST) { cascaderAddress.push(record[0].HW__installationSiteCity__CST); if (record[0].HW__installationSiteArea__CST) { cascaderAddress.push(record[0].HW__installationSiteArea__CST); } } } record[0].cascaderAddress = cascaderAddress; //将结果挂入输出对象中 out.equipment = record[0] } } catch (error) { console.error(error.name, error.message); context.setError(error.name, error.message); } return out; } } 单击编辑器上方的,保存脚本。
  • 验证并发布 测试新增逻辑能否正常执行。 单击编辑器上方的,执行脚本。 在界面底部输入测试数据,单击测试窗口右上角执行图标。 测试报文样例如下,equipmentId可参考创建“编辑设备”脚本验证新增设备时生成的设备ID: { "equipmentId": "cQue000000e1qnhgtCng" } 执行成功,会在“输出”页签返回查询结果,返回结果是上一节中新增的设备信息。 图1 查询返回的设备信息 测试成功,单击编辑器上方的,启用脚本。
  • 公共接口 公共接口是对脚本、服务编排和对象进行再包装的一种方式。将创建的脚本、服务编排、对象包装成一个新公共服务,可以使得接口的URL地址的表达形式更规范,方便让前端页面或第三方系统进行调用。 因此,在创建完后台逻辑后(服务编排、脚本、对象),就需要先将此接口包装成标准的公共接口,才能被调用。本章节中创建了2个业务脚本,因此需要对应创建2个公共接口,供前端页面调用。 创建公共接口的入口:在APP视图下,单击下方“服务”,即可进入公共接口创建页面。 图1 创建公共接口入口 图2 公共接口基本信息 图3 公共接口URL 父主题: 背景和原理
  • 样例代码解读 通过下面详细的脚本代码内容解读,使您对脚本有一个更具体的认识。 一般情况下,编写脚本的大致流程为: 按需引入平台标准库。 图2 引入平台标准库 定义出参、入参结构。 图3 定义入参 图4 定义出参 定义方法以及使用的对象。 图5 定义方法及使用对象 进行数据库操作。 图6 数据库相关操作 下面通过解读以下脚本样例,了解一个脚本的总体结构框架、编写要求。 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'; 除了平台预置标准库,还可以声明对其他自定义模块的引用。例如,已经提前开发了一个脚本circle,可以用如下方式加载。 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可实现在日志里打印过程输出,方便代码调试。
  • 了解应用目录及菜单 一个业务系统通常包括前端页面、后台逻辑和数据库表。因此,AstroZero采用类似的结构管理应用程序。 如图1所示,应用管理页面包含三部分(红框1、红框2和红框3): 红框1中是当前应用的页面、模型和逻辑。在后续章节中,开发的页面、对象及后台逻辑,推荐按以下规划的方式,将应用资源分别放在对应的目录下: Object:数据对象 Script:脚本 Flow:服务编排 Bpm:BPM Page:前端页面 红框2中是应用管理功能,例如预览应用、编译打包、发布应用等。 红框3中是应用设置功能,例如设置应用导航、页面设置等。 红框4中是AstroZero应用开发环境的导航菜单,单击“首页”可返回开发环境首页。 图1 应用的管理目录 父主题: 创建应用
  • 背景信息 工作队列是在业务场景中,用来记录可以受理相同具体业务的用户群体。 这种记录一直保留在队列中,直到用户接受它们并进行处理,或它们被转移到另一个队列。您可以指定每个队列支持的对象集合,以及允许从队列检索记录的用户组(用户、业务用户等)。 创建工作队列主要包含两方面内容: 添加队列的成员。 队列成员可以是单个用户、公共组、单个角色或带有下级角色的角色,以及业务用户;如果队列的成员是角色,此队列将包含角色中所有的用户。 配置队列支持的对象。 配置了支持的对象后,涉及特定数据对象的触发器、待审批任务才能进入该队列。
  • 入门实践 当您购买了Astro轻应用实例后,可以根据业务需要搭建低代码、零代码和大屏应用。本文介绍Astro轻应用常见的搭建实践,帮助您更好的使用Astro轻应用。 表1 常用最佳实践 实践 描述 通过AstroZero开发设备维修管理应用 本最佳实践以IoT领域电梯设备运维管理和维修的应用场景为主线,由浅入深的向您介绍如何在AstroZero中开发该应用。 使用AstroZero将客户与订单数据关联并同步修改 本最佳实践主要向您介绍如何将两个对象进行关联,实现对象数据的同步修改功能。 通过AstroZero中的脚本实现表格数据的增加和删除 本最佳实践向您介绍如何通过脚本,实现表格数据的增加和删除。 通过AstroZero流程模板创建出差审批应用 本最佳实践通过创建一个出差审批应用,帮助您快速熟悉AstroZero中的工作流。 为AstroZero调查问卷应用新增调查项 本最佳实践主要向您介绍如何通过对象模型,为调查问卷应用新增调查项。 为AstroZero标准页面中的表格增加求和等计算能力 本最佳实践主要向您介绍如何为标准页面中的表格添加计算能力。 在AstroZero高级页面中使用花瓣图展示订单数据 本最佳实践主要向您介绍如何在高级页面中,通过玫瑰花饼图展示订单数据。 通过AstroZero中的连接器上传并识别身份证图片 本最佳实践主要向您介绍如何通过OCR连接器,实现身份证信息的识别和存储能力。 将AstroZero中的应用发布成 WeLink 轻应用 本最佳实践以Astro轻应用调查问卷轻应用为例,向您介绍如何把AstroZero上开发的应用发布成WeLink(蓝标)轻应用。
  • 自定义 域名 访问介绍与配置 当以自定义域名访问OBS桶时,需要先将该自定义域名同对应OBS桶访问域名进行绑定,相关配置请参见自定义域名绑定简介,自定义域名绑定配置。 当在自定义域名上配置了 CDN加速 服务,即自定义域名为CDN服务的加速域名时,需要额外对CDN服务进行配置,以保证可以正常使用自定义域名访问OBS服务。 以华为云CDN服务为例,相关配置如下所示: 登录华为云CDN服务,从CDN服务左侧列表中选择域名管理项,在该项中可以查看到所有配置的CDN服务域名信息。 配置源站。单击要使用的自定义域名项,进入域名配置界面,编辑源站配置,选择主源站类型为源站域名类型,对应源站为要访问的OBS桶域名。 配置回源HOST。回源HOST必须指定为加速域名即访问OBS服务时访问的自定义域名,否则可能会出现回源鉴权失败的问题。
  • 代理设置失效 1、sdk Windows端 demo中设置代理时出现如下问题,程序报错且代理设置失败 问题根因:某些sdk版本demo头文件eSDKOBS.h与sdk的eSDKOBS.h未同步更新,导致option中设置的代理失效 解决方法:将yourSDKpath\source\eSDK_OBS_API\eSDK_OBS_API_C++\inc\eSDKOBS.h 替换掉‪yourSDKpath\source\eSDK_OBS_API\eSDK_OBS_API_C++\build\obs\demo\eSDKOBS.h 同时demo做如下改动来适配eSDKOBS.h的更改(适配过程以3.22.7版本为例,其他版本可能略有不同) 在文件yourSDKpath\source\eSDK_OBS_API\eSDK_OBS_API_C++\build\obs\demo\ demo_windows.cpp中4749行新增obs_upload_file_server_callback server_callback; 同时4750行中,函数upload_file第四个参数后增加, server_callback,如下图 2、设置了代理还是连接失败,可能是因为sdk的 request.c的 get_api_version函数中未设置代理 可以参考sdk的 request.c的 setup_curl函数中设置代理的方式在get_api_version函数中添加向curl中设置代理(CURLOPT_PROXY项与CURLOPT_PROXYUSERPWD项)的逻辑进行修复 父主题: 常见问题
  • 常见编译问题 本章节收录了编译sdk或者sdk demo可能遇到的一些编译问题,对应版本的问题可能在其他旧版本中也出现,都可以参考 v3.23.3版本反映的常见问题: 问题1: 编译sdk的vs工程报错: 报错文件:目录${yourSDKPath}\platform\eSDK_LogAPI_V2.1.10\C\include下面的eSDKLogAPI.h、eSDKLogDataType.h这两个文件 vs编译器错误码:c2018 解决方案: 修改上述目录下的两个文件(eSDKLogAPI.h、eSDKLogDataType.h)的行尾序列为CRLF 问题2:sdk的vs工程编译成功后还需要手动拷贝lib、dll到demo工程目录下才能运行demo程序,不够方便 解决方案(以Debug,x64为例): 1、编译Debug,x64的obs.sln解决方案 2、打开obs_demo.sln解决方案,调整属性为Debug,x64,按图操作后,填入值: PATH=%PATH%;$(ProjectDir)..\..\eSDK_OBS_API_C++\build\vc100\Debug;$(ProjectDir)..\..\eSDK_OBS_API_C++\bin\win64_x64_msvc\release;$(ProjectDir)..\..\..\..\platform\eSDK_LogAPI_V2.1.10\C\release_x64;$(LocalDebuggerEnvironment) v3.22.7版本反映的Sdk本体常见编译问题 问题1: 解决方案:通过下面的脚本去编译securec组件,并将产物拷贝到对应目录 export Your_SDK_path='Your_SDK_path' cd ${Your_SDK_path}/platform/huaweisecurec/src/ make mkdir ${Your_SDK_path}/platform/huaweisecurec/lib/linux cp ${Your_SDK_path}/platform/huaweisecurec/lib/libsecurec.so ${Your_SDK_path}/platform/huaweisecurec/lib/linux cd ${Your_SDK_path}/source/eSDK_OBS_API/eSDK_OBS_API_C++ #需要先cd到目录下,因为脚本按相对路径查找依赖项 sh build.sh 问题2: 解决方案: 在source\eSDK_OBS_API\eSDK_OBS_API_C++\src\object\download_file.c 中删除26、27行(如下) pthread_mutex_t g_mutexThreadCheckpoint; pthread_mutex_t g_mutexThreadCheckpoint_download; 在source\eSDK_OBS_API\eSDK_OBS_API_C++\src\object\object_common.c 中删除31、32行(如下) pthread_mutex_t g_mutexThreadCheckpoint; pthread_mutex_t g_mutexThreadCheckpoint_download; 之后编译即可 问题3: 这个问题一般是在其他路径中执行build.sh导致的,需要先cd到对应目录下再执行,因为脚本按相对路径查找依赖项,执行如下脚本编译 export Your_SDK_path='Your_SDK_path' cd ${Your_SDK_path}/source/eSDK_OBS_API/eSDK_OBS_API_C++ sh build.sh v3.22.7版本反映的常见Sdk Demo编译问题 问题1: 需要拷贝cjson头文件以及相关库(如果是arm需要把路径中的linux替换为arm) 同时需要改Makefile export Your_SDK_path='Your_SDK_path' export Your_DEMO_path='Your_DEMO_path' cp ${Your_SDK_path}/build/script/Provider/build/linux/cjson-1.7.15/include/cJSON.h ${Your_DEMO_path}/include cp ${Your_SDK_path}/build/script/Provider/build/linux/cjson-1.7.15/lib/libcjson.so* ${Your_DEMO_path}/lib 打开${Your_DEMO_path}/demo/Makefile 将16行(LIB=-leSDKOBS -lsecurec)替换为LIB=-lcjson -leSDKOBS -lsecurec cd ${Your_DEMO_path}/demo make 父主题: 常见问题
  • 设置方式 修改lib目录下的OBS.ini,配置日志的大小、个数以及级别(其中*_Run参数的配置为最常用的配置项): ;Every line must be less than 1024 [LogConfig] ;Log Size: unit=KB, 10MB = 10KB * 1024 = 10240KB LogSize_Interface=10240 LogSize_Operation=10240 LogSize_Run=10240 ;Log Num LogNum_Interface=10 LogNum_Operation=10 LogNum_Run=10 ;Log level: debug = 0,info = 1,warn = 2,error = 3 LogLevel_Interface=0 LogLevel_Operation=0 LogLevel_Run=0 ;LogFilePermission LogFilePermission=0600 [ProductConfig] ;Product Name sdkname=eSDK-OBS-API-Linux-C [LogPath] ;Log Path is relative to the path of configuration file LogPath=../logs
  • 其他日志相关配置 Windows下,OBS.ini中LogPath字段可以以wchar_t格式读取,需要通过设置文件路径编码来实现,案例如下: set_file_path_code(UNICODE_CODE);//默认是ANSI_CODE 设置后,以下方法的本地文件路径也需要是wchar_t类型(参数是以char*格式传入,内部处理逻辑是wchar_t) 表1 影响函数 说明 download_file 参数download_file_config的downLoad_file、check_point_file成员需要是wchar_t类型,以char*格式传入 upload_file 参数upload_file_config的upload_file、check_point_file成员需要是wchar_t类型,以char*格式传入 set_obs_log_path 参数log_path需要是wchar_t类型,以char*格式传入
  • 日志级别 当系统出现问题需要定位且当前的日志无法满足要求时,可以通过修改日志的级别来获取更多的信息。其中DEBUG(0)日志信息最丰富,ERROR(3)日志信息最少。 具体说明如下: DEBUG(0):调试级别,如果设置为这个级别,除了打印INFO级别的信息外,还将打印其它帮助调试的信息等。 INFO(1):信息级别,如果设置为这个级别,除了打印WARN级别的信息外,还将打印OBS接口的调用过程和关键信息等。 WARN(2):告警级别,如果设置为这个级别,除了打印ERROR级别的信息外,还将打印一些关键事件的信息,如curl_global_init初始化失败等。 ERROR(3):错误级别,如果设置为这个级别,仅打印发生异常时的错误信息。
  • 参数说明 加解密参数server_side_encryption_params说明如下表所示: 字段名 类型 说明 encryption_type obs_encryption_type 服务端加密方式 OBS_ENCRYPTION_KMS:使用SE-KMS加密方式 OBS_ENCRYPTION_SSEC:SSE-C加密使用 kms_server_side_encryption char * 使用该参数表示服务端加密是SSE-KMS方式。目标对象使用SSE-KMS方式加密。 kms_key_id char * SSE-KMS方式下使用该参数,表示加密目标对象使用的主密钥,如果用户没有提供该头域,那么默认的主密钥将会被使用。 ssec_customer_algorithm char * SSE-C方式下使用该参数,表示加密目标对象使用的算法。 ssec_customer_key char * SSE-C方式下使用该参数,表示加密目标对象使用的密钥。 des_ssec_customer_algorithm char * SSE-C方式下使用该参数,表示解密源对象使用的算法。 des_ssec_customer_key char * SSE-C方式下使用该参数,表示解密源对象使用的密钥。用于解密源对象。
  • 静态网站托管简介 开发过程中,您有任何问题可以在github上提交issue,或者在华为云 对象存储服务 论坛中发帖求助。 您可以将静态网站文件上传至OBS的桶中作为对象,并对这些对象赋予公共读权限,然后将该桶配置成静态网站托管模式,以实现在OBS上托管静态网站的目的。第三方用户在访问您网站的时候,实际上是在访问OBS的桶中的对象。在使用静态网站托管功能时,OBS还支持配置请求重定向,通过重定向配置您可以将特定的请求或所有请求实施重定向。 更多关于静态网站托管的内容请参考静态网站托管。 父主题: 静态网站托管
  • 参数描述 字段名 类型 约束 说明 option 请求桶的上下文,配置option 必选 桶参数。 logging_message_data bucket_logging_message * 必选 当前桶日志管理配置情况,bucket_logging_message说明请参看下表。 handler obs_response_handler* 必选 回调函数。 callback_data void * 可选 回调数据。 当前桶日志管理配置结构bucket_logging_message描述如下表:
  • 设置对象过期时间 以下代码展示了如何设置最新版本对象和历史版本对象的过期时间: static void test_set_bucket_lifecycle_configuration2() { obs_options option; obs_status ret_status = OBS_STATUS_BUTT; // 设置option init_obs_options(&option); option.bucket_options.host_name = HOST_NAME; option.bucket_options.bucket_name = bucket_name; //从环境变量读取ak/sk option.bucket_options.access_key = getenv("AC CES S_KEY_ID"); option.bucket_options.secret_access_key = getenv("SECRET_ACCESS_KEY"); // 设置完成的回调函数 obs_response_handler response_handler = { NULL, &response_complete_callback }; obs_lifecycle_conf bucket_lifecycle_conf; memset(&bucket_lifecycle_conf, 0, sizeof(obs_lifecycle_conf)); //生命周期规则的id bucket_lifecycle_conf.id = "test1"; // 指定前缀"test" bucket_lifecycle_conf.prefix = "test"; // 指定满足前缀的对象创建10天后过期 bucket_lifecycle_conf.days = "10"; // 指定满足前缀的对象的历史版本20天后过期 bucket_lifecycle_conf.noncurrent_version_days = "20"; // 该生命周期规则生效 bucket_lifecycle_conf.status = "Enabled"; set_bucket_lifecycle_configuration(&option, &bucket_lifecycle_conf, 1, &response_handler, &ret_status); if (OBS_STATUS_OK == ret_status) { printf("set bucket lifecycle configuration success.\n"); } else { printf("set bucket lifecycle configuration failed(%s).\n", obs_get_status_name(ret_status)); } }
  • 生命周期管理简介 开发过程中,您有任何问题可以在github上提交issue,或者在华为云对象存储服务论坛中发帖求助。 OBS允许您对桶设置生命周期规则,实现自动转换对象的存储类型、自动淘汰过期的对象,以有效利用存储特性,优化存储空间。针对不同前缀的对象,您可以同时设置多条规则。一条规则包含 规则ID,用于标识一条规则,不能重复。 受影响的对象前缀,此规则只作用于符合前缀的对象。 最新版本对象的转换策略,指定方式为: 指定满足前缀的对象创建后第几天时转换为指定的存储类型。 直接指定满足前缀的对象转换为指定的存储类型的日期。 最新版本对象过期时间,指定方式为: 指定满足前缀的对象创建后第几天时过期。 直接指定满足前缀的对象过期日期。 历史版本对象转换策略,指定方式为: 指定满足前缀的对象成为历史版本后第几天时转换为指定的存储类型。 历史版本对象过期时间,指定方式为: 指定满足前缀的对象成为历史版本后第几天时过期。 是否生效标识。 更多关于生命周期的内容请参考生命周期管理。 对象过期后会被OBS服务端自动删除。 对象转换策略中的时间必须早于对象过期时间;历史版本对象转换策略中的时间也必须早于历史版本对象的过期时间。 桶必须开启多版本状态,历史版本对象转换策略和历史版本对象过期时间配置才能生效。 父主题: 生命周期管理
  • 参数描述 字段名 类型 约束 说明 option 请求桶的上下文,配置option 必选 桶参数。 version_status char * 必选 表示桶的多版本状态:OBS_VERSION_STATUS_ENABLED,OBS_VERSION_STATUS_SUSPENDED。 handler obs_response_handler * 必选 回调函数。 callback_data void * 可选 回调数据。
  • 临时授权访问示例 开发过程中,您有任何问题可以在github上提交issue,或者在华为云对象存储服务论坛中发帖求助。 您可以通过SDK接口传入temp_auth_configure结构体参数生成临时授权访问的URL。以下代码展示了如何生成常用操作的URL,包括:创建桶、上传对象、下载对象、列举对象、删除对象。 以下示例中生成的URL记录在tmpAuthUrl.txt文件中,你可以使用生成的URL执行对应的操作。 生成创建桶的URL 生成上传对象的URL 生成下载对象的URL 生成列举对象的URL 生成删除对象的URL 父主题: 临时授权访问
  • 什么是临时授权访问 开发过程中,您有任何问题可以在github上提交issue,或者在华为云对象存储服务论坛中发帖求助。 临时授权访问是指通过访问密钥、请求方法类型、请求参数等信息生成一个临时访问权限的URL,这个URL中会包含鉴权信息,您可以使用该URL进行访问OBS服务进行特定操作。在生成URL时,您需要指定URL的有效期。生成临时授权访问的URL是通过设置结构体temp_auth_configure来实现的。 temp_auth_configure结构体存在于obs_options结构体中。该方法适用于每个C SDK接口。 参数 作用 SDK中对应的结构体 expires 生成的临时URL的有效期 obs_options. temp_auth_configure temp_auth_callback 回调函数用于返回生成的临时URL callback_data 回调数据 如果遇到跨域报错、签名不匹配问题,请参考以下步骤排查问题: 未配置跨域,需要在控制台配置CORS规则,请参考配置桶允许跨域请求。 签名计算问题,请参考URL中携带签名排查签名参数是否正确;比如上传对象功能,后端将Content-Type参与计算签名生成授权URL,但是前端使用授权URL时没有设置Content-Type字段或者传入错误的值,此时会出现跨域错误。解决方案为:Content-Type字段前后端保持一致。 父主题: 临时授权访问
共100000条