• 沒有找到結果。

let oneComments = newComments();

– 变量为单数时,命名包含对象名称,如:level1Catalog、level2Catalog。

– 变量为复数时,命名包含集合名称,如:attributeRelationRuleList。

2.2.2 注释

本节包含脚本注释的原则和写法。

总体原则

*/@action.method({ input: "Input", output: "Output", label: 'queryProductDetailForCart' })

● 方法内关键业务语句前必须添加注释。

let password = input.password;

if (accountRecord["Password"] != password) { error.name = "CM-001003";

error.message = "Invalid loginId or password.";

throw error;

}

2.2.3 引用

本节包含脚本间相互引用的原则。

● 脚本中不要包含没用到的标准库或者对象的引用。

例如,如果实际没用到sys模块的任何方法,则不要包含如下语句:

import * as sys from 'sys';

如果实际上却只用到了CA_PC_Offering对象,则示例语句中其他对象需要删除:

@useObject(['CA_PC_FeeItemDef', 'CA_PC_Offering', 'CA_PC_CatalogOffering', 'CA_PC_Catalog', 'CA_PC_OfferingAttribute'])

● 只引用用到的对象,而不用import *。

【推荐】:

import { OfferingObjectQuery } from './ca_pc__getOfferingObject';

import { setI18nError } from 'context';

import { sql } from 'db';

【不推荐】:

import * as queryOfferingObjectAction from './ca_pc__getOfferingObject';

import * as context from 'context';

import * as db from 'db';

● @action.method属于必须的方法注解,写在方法上面。

@action.method({ input: "Input", output: "Output", label: 'queryClassification' }) queryClassification(input: Input): Output {

...}

● 为方便页面直接调用脚本,仅在脚本最后统一导出需要的对象,而不是导出所有 对象。如果不需要可以不用导出。

例如,getOfferingObject脚本中最后一行导出OfferingObjectQuery对象:

export let theAction = new OfferingObjectQuery();

● 可以把公共的对象Object定义按分类放在一些公共的脚本里(例如pc_XXX.ts、

cm_XXX.ts),其他需要的脚本直接引用即可。

2.2.4 定义

本节包含字段、对象、结构体等的定义规则。

● 每个字段的定义,均需要定义type、label、description、required、

isCollection。有默认值的非必填。

● 当字段为集合类型时候,需要定义成[]。

@action.param({

type: "Attribute", label: "Attribute",

description: "attributeList", required:false

isCollection: true })attributeList: Attribute[];

● @action.object时,需要在脚本中详细定义清楚Object。不要引用其他脚本的 Object。

● 如果是嵌套结构体则从下到上粒度依次变小。

export class ProductObject { //ignore

}

export class StockObject { //ignore

}

export class ProductStock {

@action.param({ type: 'Struct', label: "ProductObject", isCollection: false }) productInfomation: ProductObject;

@action.param({ type: 'Struct', label: "StockObject", isCollection: false }) stock: StockObject;

}

@action.object({ type: "param" }) export class Input {

@action.param({ type: 'String', label: "productId", isCollection: false }) productId: String;

}

● 不需要多定义Output对象,可以直接在方法使用定义的对象出参。

【不推荐】:但是如果出参对象包含从外部引入的对象,则还是要按该方式定 义。

@action.object({ type: "param" }) export class Input {

@action.param({ type: 'String', label: "productId", isCollection: false }) productId: String;

}

@action.object({ type: "param" }) export class Output {

@action.param({ type: 'Object', isCollection: false }) ProductStock: ProductStock;

}

@action.object({ type: "param" }) export class getProductStock {

@action.method({ input: "Input", output: "Output", label: 'getProductStock' }) getProductStock(input: Input): Output {

【推荐】:

@action.object({ type: "param" }) export class Input {

@action.param({ type: 'String', label: "productId", isCollection: false }) productId: String;

}

@action.object({ type: "param" }) export class getProductStock {

@action.method({ input: "Input", output: "ProductStock", label: 'getProductStock' }) getProductStock(input: Input): ProductStock {

● 除非业务有特殊要求,增修改脚本不返回结果码和结果信息。

2.2.5 调试

本节提供调试、日志代码的规则。

● try、catch

不需要做统一异常处理的话,都不用写try、catch语句,只需抛出统一的错误码。

【推荐】:

if (input.customerId == "") {

context.throwError("STO-001003");

};

【不推荐】:

if (input.customerId == "") {

context.setError('STO-001003', 'You have not login or the session has expired, please login again.');

return;

};

● 打印日志(Console.log())

看业务场景需要来控制打印日志语句,建议一个脚本最多不要超过三个打印日志

● 数字类型统一定义成Number、日期类型定义成Date。

@action.param({

type: "Date", label: "effectiveTime", description: "Date."

})effectiveTime: Date;

@action.param({

type: "Number", label: "salePrice", description: "salePrice."

})salePrice: number;

● 变量或者数组定义时要说明类型,集合需要加上泛型。

let isDone:Boolean = false;

let decLiteral : number =6;

let productList = new Array<productObject>();

● 遍历循环推荐用forEach,不推荐用for…in。

testArray.forEach((value, index, array )=>{

//ignore });

● 推荐用let变量声明,不推荐用var。

【不推荐】:

var offeringId = “aaa”;

【推荐】:

let offeringId : String = “aaa”;

● 前段代码有对象声明时,推荐使用.fieldName获取对象的字段,而不是用 [‘fieldName’]。

【不推荐】:

offeringStruct.id = result[i][‘base’][‘offeringId’];

【推荐】:

offeringStruct.id = result[i].base.offeringId;

● 在需要默认值的情况下,使用“||”代替“if”判断。

let getOfferingIdByConditionInput = { "OfferingIdRequest": {

let offeringIdRequest = new OfferingIdRequest();

offeringIdRequest.catalogList = catalogList;

offeringIdRequest.classificationList:=classificationList;

let getOfferingIdByConditionInput = { "OfferingIdRequest": offeringIdRequest }

● 函数定义

let traitRec = function(xxxx,xxx) { //ignore

}

只能在函数定义后的语句中使用该函数。

● 没有初始化值的变量申明,使用undefined,不要使用null。

let object = undefined;

● 局部变量需要在class内定义,不要在全局命名空间内定义类型/值(即不要在class 外定义变量),常量可以定义成全局。

let identityIdList = [];

● 使用lambda表达式代替匿名函数。

只有需要的时候才把arrow函数的参数括起来。下面是正确使用arrow的做法:

x => x + x (x,y) => x + y

<T>(x: T, y: T) => x === y

2.2.7 SQL

本节包括脚本中SQL的规则。

1. 不推荐用拼接SQL方法,避免注入风险。

【不推荐】:

let sql = "select id,name,ExternalCode,FeeType,FeeItemName,FeeItemDescription,Remark,Status from CA_PC_FeeItemDef where 1=1";

if (!InputParams.id && !InputParams.name && !InputParams.feeType && !InputParams.feeItemName

&& !InputParams.status) {

context.setI18nError("ca_pc__001013");

return;

}if (InputParams.id) {

sql += "and id ='" + InputParams.id + "'"

}

2. 多表复杂查询建议用sql.exec()或者sql.excute()方法。excute()方法比exec()多返 回字段集和操作成功数。

let result = execsql.exec("select

id,name,OfferingId,ParentId,SkuCode,ChannelId,Status,ProductLabel,PriceCode,DefaultChoose,Payment Type from CA_PC_Product where id in (" + str + ")",

{ params: productId });

多表复杂查询只能用拼接SQL方法,但是有限制,例如示例中的str要求如下:

– str如果来源于入参,则入参在拼接SQL之前需要进行校验,以免引入SQL注 入攻击;如果来源于内部数据,可以不进行校验。

– 不推荐直接在SQL中拼接入参,应该采用在SQL中拼接占位符,然后把入参放 入参数数组中的方式,例如:

attriSql = "select AttrDef from DE_DeviceDefAttri where DeviceDef=? and ExternalCode=? and AttrType='DYNAMIC' and ValueType='1'";

attriRecords = db.sql().exec(attriSql, { params: [deviceDefId, defExternalCode]

});

3. 对于单表查询和增删改SQL推荐用Orm接口方法。

Options选项可以选择返回字段、排序、聚合运算和分页等功能。

当value不存在时,默认为null类型。

let CA_PC_Stock = db.object('CA_PC_Stock');

let amountCount = 0;

if (UpdateStock.amount) {

let record = { Amount: UpdateStock.amount };

amountCount = CA_PC_Stock.updateByCondition({

"conjunction": "AND", "conditions": [{

"field": "SkuCode", "operator": "eq",

"value": UpdateStock.skuCode

}]

}, record);

}

4. 避免在循环中调用方法和操作数据库,可以用in来查询在集合中的结果。

let productIdList = input.productId;

let str = "";

let arr = [];

for (let i = 0; i < productIdList.length; i++) { arr[i] = "?";

}str = arr.toString();

let result = execsql.exec("select

id,name,OfferingId,ParentId,SkuCode,ChannelId,Status,ProductLabel,PriceCode,DefaultChoose,Payment Type from CA_PC_Product where id in (" + str + ")",

{ params: productIdList

var s = db.object('Customer__CST');

var records = [];

};records.push(record);

var ids = s.compositeInsert(records);

console.log("id list = ", ids);

count = s.count();

console.log("record count = ", count);

7. 匹配查询推荐用like,日期比较推荐用‘<’、‘>’

【不推荐】:

select id from t where substring(name,1,3)=’abc’

select id from t where datediff(day,createdate, ‘2005-11-30’)

【推荐】:

select id from t where name like ‘abc%’

select id from t where createdate>=’2005-11-30’ and createdate<’2005-12-1’

8. 使用exists替代in,使用not exists替代not in

【不推荐】:

SELECT * FROM EMP (基础表) WHERE EMPNO > 0 AND DEPTNO IN(SELECT DEPTNO FROM DEPT WHERE LOC =’MELB’)

【推荐】:

SELECT * FROM EMP (基础表) WHERE EMPNO > 0 AND EXISTS (SELECT ‘X’FROM DEPT WHERE DEPT.DEPTNO = EMP.DEPTNO AND LOC = ‘MELB’)

9. 避免在索引列上使用is null和is not null,会造成索引失效

【不推荐】:

SELECT … FROM DEPARTMENT WHERE DEPT_CODE IS NOT NULL

【推荐】:

SELECT … FROM DEPARTMENT WHERE DEPT_CODE >=0

10. 注意事项:

a. 尽量避免Select字句中使用“*”;

b. 尽量避免在where子句中使用!=或<>操作符;

c. 尽量避免在where子句中对字段进行函数操作;

d. 尽量避免在SQL中使用 “!=” 、“||”、“+”符号;

e. 尽量避免在where条件中做数据筛选;

f. 尽量避免查询中按字段排序;

l. 尽量利用对象做临时缓存:例如查询到DeviceDef后,按照id:Object的方式存 起来,后续查询时先判断缓存对象中是否已经存在,如果存在则直接获取不 再查询。

2.2.8 代码风格

本节包括代码风格建议。

● 每句话后面加分号,脚本写完右键选择“Format Document”统一格式。

● string类型赋值统一使用双引号,获取字段统一使用单引号。

● 相关代码写在一起,不相关逻辑最好以空行隔开。

function f(x: number, y: string): void { }

● 每个变量声明语句只声明一个变量

● 一个函数仅完成一件功能,即使是简单功能也应该编写单独的方法实现。

● 禁止通过字符串串联直接使用用户输入构造可执行SQL语句,降低SQL注入攻击的 风险。

● 禁止在代码和日志中存储敏感数据。

● 禁止密钥或帐号的口令以明文形式存储在数据库或文件中。

● 禁止使用自己开发的加密算法,必须使用公开、安全的标准加密算法。

2.2.10 调用约束

本节包括脚本的调用约束。

● 不允许在应用项目的Script中调用BO的script

例如, 设备管理应用的Script不可调用设备BO内的任何Script。

● 不允许跨BO调用script

例如,人员BO的Script不允许调用设备BO内的任何Script。

2.2.11 代码规范案例汇总

1. 脚本中的class和脚本名应该一致。

2. 不要把整个处理逻辑使用try catch包起来,这样会导致报错时无法知道原始错误 行,需要去掉try catch。

不推荐以下写法:

3. APP、BO中需要使用rest或者sdk来直接调用其它BO的内部服务编排。

4. APP、BO中需要使用rest或者sdk来直接调用其它BO的内部脚本。

5. 需要先定义错误码,才能正确的抛出错误码信息,否则会随意抛出错误。

6. 不允许跨BO、APP直接操作其它BO、APP的对象,应该调用rest或者sdk。

不推荐以下写法:

7. 对于确实需要查询所有数据的场景,需要考虑平台的查询记录数限制,并自行控 制游标查询。