/**
- 变量声明,只支持使用let关键字进行变量声明,需要指定变量类型
- @example let a: Integer = 1;
- @example let b: Decimal = 1.0;
- @example let d: { a: String, b: Decimal, c: Integer } = { a: 'str', b: 0.0, c: 0 }; */
/**
- 相等判定,采用'=='运算符进行判定
- 对于Integer、Decimal、Boolean、String、Date、Time、DateTime类型,进行值比较;对于List、Map类型,进行引用比较
- @example
- str == 'abc' // 判断str是否等于'abc'
- number == 123 // 判断number是否等于123
- date == '2024-01-01' // 判断date是否等于'2024-01-01' */
/**
- 基本数学运算
- NASL支持的基本数学运算包括加法、减法、乘法、除法和取余运算
- 加法:+,减法:-,乘法:*,除法:/,取余:%
- 运算结果类型:
-
- 对于加法、减法、乘法、取余,结果类型与操作数类型相同
-
- 对于除法,结果类型为Decimal */
/**
- 枚举类型定义和类型表示
- @example
- namespace app.enums {
- export class OrderStatus extends BaseEnum {
-
static readonly PENDING = new OrderStatus('PENDING', '待支付'); -
static readonly PAID = new OrderStatus('PAID', '已支付'); - }
- export class ProductCategory extends BaseEnum {
-
static readonly 0 = new ProductCategory(0, '电子产品'); -
static readonly 1 = new ProductCategory(1, '服装'); - }
- export class ApprovalStatus extends BaseEnum {
-
static readonly 0 = new ApprovalStatus(0, '待提交'); -
static readonly 1 = new ApprovalStatus(1, '审批中'); - }
- // 0开头数字字符串枚举示例
- export class TaskPriority extends BaseEnum {
-
static readonly '001' = new TaskPriority('001', '高'); -
static readonly '002' = new TaskPriority('002', '中'); -
static readonly '003' = new TaskPriority('003', '低'); - }
- }
- @attention 枚举项等号左边的键 "VALUE0" 与 new 的第1个参数值 "VALUE0" 必须相同
- @attention 数字字符串的枚举项等号左边的键 "001" 与 new 的第1个参数值 "001" 必须相同,取值时用 ['001'] 的方式
- @example let status: app.enums.OrderStatus = app.enums.OrderStatus['PENDING'];
- @example let category: app.enums.ProductCategory = app.enums.ProductCategory[0];
- @example let status: app.enums.ApprovalStatus = app.enums.ApprovalStatus[0]
- @example let priority: app.enums.TaskPriority = app.enums.TaskPriority['001'] */ declare class BaseEnum<V extends String | Integer> {}
/**
- for...of 语句
- 用于遍历列表中的元素,禁止使用for循环语句的其他形式。语法如下:
- for (const [index, item] of ListEntries(list, start, end)) {}
- 其中:
-
- index: Long,当前索引
-
- item: T,当前元素
- @note [index, item] 为解构赋值,顺序不可颠倒
- @note index和item可以替换为其他变量名,但不能用下划线(_)作为变量名
- ListEntries 仅用于for...of循环,声明为: function ListEntries(list: List, start?: Long, end?: Long): List<[Long, T]>;
- @param list: List,需要遍历的列表,必填
- @param start: Long,开始索引,默认值 0
- @param end: Long,结束索引,默认值 list.length
- ListEntries([1, 2, 3]) => [[0, 1], [1, 2], [2, 3]]
- ListEntries([1, 2, 3], 1, 2) => [[1, 2]]
- @note ListEntries不在任何namespace下定义,可以直接使用
- @note 遍历列表
- @example for (const [index, item] of ListEntries(list, start, end)) {} // 遍历列表, 从start到end
- @example for (const [index, item] of ListEntries(list, start)) {} // 遍历列表, 从start到列表末尾
- @example for (const [index, item] of ListEntries(list)) {} // 遍历列表, 从0到列表末尾
- @note 配合ListRange, 可实现特定范围的遍历功能
- @example for (const [index, item] of ListEntries(nasl.util.ListRange(10))) {} // 遍历从0到9的整数
- @example for (const [index, item] of ListEntries(nasl.util.ListRange(2, 5, 1))) {} // 遍历从2到4的整数
- @note 配合ListRange的step参数,可以实现反向遍历
- @example for (const [index, item] of ListEntries(nasl.util.ListRange(10, 0, -2))) {} // 遍历从10到1的偶数
- @example for (const [index, item] of ListEntries(nasl.util.ListRange(nasl.util.Length(list_var)-1, -1, -1))) {} // 遍历列表反向的位置索引
- @note 配合Split函数,可以实现按字符遍历字符串
- @example for (const [index, item] of ListEntries(nasl.util.Split(str, ''))) {} // 按字符遍历字符串
- @example for (const [index, item] of ListEntries(nasl.util.Split(str, ' '))) {} // 按单词遍历字符串
- @note 错误示例
- @example for (const [_, _] of ListEntries(list)) {} // 错误示例,使用了下划线作为变量名
- @example for (const [, item] of ListEntries(list)) {} // 错误示例,索引必须要有变量名 */
/**
- while语句
- 用于循环执行一段代码,直到指定条件不满足。
- @example while (condition) {} */
/**
- 循环语句内关键字 break/continue
- break: 终止循环
- continue: 跳过当前循环,继续下一个循环
- 可以在for...of和while语句中使用
- @note for...of和while循环内,禁止使用return语句,请使用break语句跳出循环再返回 */
禁止在服务端 Logic 中使用前端变量:
- ✅ 必须使用:
app.backend.variables.currentUser.userId或nasl.auth.getCurrentUser() - ❌ 禁止使用:
nasl.auth.userInfo(前端变量,服务端无法访问)
// ✅ 正确
if (nasl.util.HasValue(app.backend.variables.currentUser.userId)) {
let userId = app.backend.variables.currentUser.userId;
}
// ❌ 错误
// let userName = nasl.auth.userInfo.UserName;
- 在 for、ListTransform、ListFilter、ListSort、ListGroupBy、ORDER_BY 等回调或循环中,禁止使用 NASL 系统关键字作为参数名(如 role、element、event、length、list、map 等)。
- 统一使用:单回调用
item、resultItem,多回调用item、item1、item2,for...of 用[index, item]。
- httpRequest 对象,可在逻辑中使用,用于获取当前请求的属性,例如:cookie、header、ip等
- httpResponse 对象,可用于全局逻辑中,可返回header、cookie等
- 低代码默认处理好了 http 的很多机制,这些是高级用法,需要 HTTP 特殊定制时才使用
let nameObj: extensions.strUtils.structures.NameStructure;
nameObj = extensions.strUtils.logics.splitName('John Doe');let queryResult: List<apis.someService.structures.QueryResult>;
queryResult = apis.someService.interfaces.queryPhoneNumbers('John Doe');let variable1: List<connectors.QQ_email_connector.structures.MailBody>;
variable1 = connectors.QQ_email_connector.connect('defaultConn').getEmail(10);
variable2 = connectors.QQ_email_connector.namespace2.someFunc(10, 'test');提醒:更多扩展库、接口、连接器的定义和使用说明,需要调用【LoadKnowledge】工具,传入{ useBackendDependencies: true }加载【相关知识】。
注意:
- 如果需求和技术设计中提到的扩展库、API 接口、连接器确实存在,请认真使用并实现相关功能;
- 如果确实不存在,不要硬写,你也无法自行补充。简单模拟实现和返回即可,并在逻辑中注释说明情况。
// 每个逻辑的命名规则如下:[逻辑的用途名][引用的组件名][根据情况唯一标识(可选)]
$Logic({ title: '获取学生列表', description: '从数据库中获取学生列表', returnDescription: '学生列表分页结果', directory: 'student_management(学生管理)' }) export function loadStudent_eltable1( /** 页码 */ page: Integer, /** 每页条数 */ size: Integer, /** 排序字段 */ sort: String, /** 排序方向 */ order: String, /** 学生过滤条件 */ filter: app.dataSources.defaultDS.entities.Student, /** 学校名称过滤 */ filterSchoolName: String ): { /** 学生和学校关联列表 */ list: List<{ /** 学生信息 */ student: app.dataSources.defaultDS.entities.Student, /** 学校信息 */ school: app.dataSources.defaultDS.entities.School }>, /** 总条数 */ total: Integer } { $Return({ description: '学生列表分页结果', autoInferType: true }) let result; result = PAGINATE(FROM(app.dataSources.defaultDS.entities.StudentEntity, Student => $() .LEFT_JOIN(app.dataSources.defaultDS.entities.SchoolEntity, School => ON(Student.schoolId == School.id) .WHERE(LIKE(Student.name, filter.name) && Student.age == filter.age && LIKE(School.name == filterSchoolName) .SELECT({ student: Student, school: School, }) .ORDER_BY((resultItem) => [[resultItem[sort], order]]) )), page, size); return result; } $Logic({ title: '获取书籍作者列表', description: '获取书籍作者列表并排序', returnDescription: '书籍作者列表排序结果', directory: 'book_management(书籍管理)' }) export function getSortedAuthors( /** 书籍作者 Map(书名 -> 作者名) */ bookAuthorMap: Map ): List { $Return({ description: '书籍作者列表排序结果', autoInferType: true }) let authors: List; authors = nasl.util.MapValues(bookAuthorMap); authors = nasl.util.ListSort(authors, (item) => ({ by: item, asc: true })); return authors; } - In NASL language, the return type of the function can only be one type; - 不要写 async 和 Promise 类型,语言底层都是异步实现了; - tuple can be treated as a list; - Structure operations of data structures such as List/Map should use functions in nasl.util; - NASL language NOT SUPPORTS: - try-catch, raise, or exception handling;语言底层自动做了异常处理; - bitwise operation, such as `&`, `|`, `^`, `~`, `<<`, `>>` - Only template string syntax can be used for concatenation between variables and strings, it will automatically convert the variable to a string; - NASL language is a statically typed language, please convert type explicitly. - It is not allowed to use `Any` type in parameter or return type of user-defined functions, please specify the exact type. - DO NOT use `char` as a variable name, use `character` instead. - In NASL language, `==` is value comparison for basic types and string types, and reference comparison for other types. - NASL language is a statically typed language, use `nasl.util.Convert(value)` to convert basic types. - Consider the boundary case processing of the input when writing code to ensure the robustness of the code, such as empty lists, empty strings, empty Maps, etc. - Unless explicitly required to print, the results should be returned as return values. - 变量一般不用初始化,基础类型默认为 undefined,复合类型默认会 new。 - **Decimal 类型赋值**:所有需要 Decimal 类型的场景(变量初始化、对象字面量字段、列表元素添加、三元 fallback 等),整数字面量必须写成 `0.0` 而非 `0`,避免产生 `Decimal | Integer` 混合类型。✅ `let total: Decimal = 0.0` / `{ amount: 0.0 }` / `HasValue(x) ? x : 0.0` / `ListAdd(decimalList, 0.0)`,❌ `let total: Decimal = 0` / `{ amount: 0 }` / `HasValue(x) ? x : 0` - **禁止使用 union 类型**:函数参数、局部变量(`let`)、函数返回类型中均不得使用 `A | B | C` 形式的 union 类型,List 泛型参数同样禁止(`List` ❌)。可空字段统一改用可选字段语法 `field?: T`,适用于所有类型(`Integer | null` → `field?: Integer`,`DateTime | null` → `field?: DateTime`,不只是 String)。多实体场景应将对象拆分为多个独立字段(如 `typeADetail?: EntityA`、`typeBDetail?: EntityB`),各分支只赋值对应字段,而非用 union 类型汇聚到同一字段。 - 【特别注意】变量、参数、页面名、属性名等取名,不要与以下关键字冲突: - JS、TS 和 Java 的关键字,如 package, import, class, constructor 等等 - 禁止使用 char, current, event 作为变量名,这些是保留字。 - 实体名称不能与常用数据库的关键字冲突,如 select, where, join 等等 NASL 关键字请参考 nasl-book/K001-nasl--keywords.md 文件 - NASL Natural TS 中变量不是块级作用域,页面、逻辑、lambda 函数、for 循环的参数和变量请尽量区别开。如: function unwrapList(list1: List<{ student: app.structures.Student }>) { let item: app.structures.Student; // item 用于某在一种地方 let list2 = ListTransform(list1, (item2) => item2.student); // 用 item2 去区别 ... } - 在服务端逻辑中调用其他逻辑,必须要写 app.logics.xxx();在页面中调用页面逻辑,不要写 app.logics 前缀,如 viewLogic()。 - 【特别注意】逻辑的实现过程中,**尽量添加注释**,讲述你的实现思路,方便后续维护。 - httpRequest, httpResponse, currentUser 为服务端只读全局变量,不可修改,只可使用 - `currentUser` 的字段含义、与 `nasl.auth.getCurrentUser()` / 前端 `nasl.auth.userInfo` 的端侧区分见 nasl-book/K011-nasl--current-user-and-nasl-auth.md - **NASL 基础类型**:请参考 nasl-book/K002-nasl--types.md 文件 - **当前用户与 nasl.auth(服务端 currentUser / getCurrentUser)**:请参考 nasl-book/K011-nasl--current-user-and-nasl-auth.md 文件 - **NASL 逻辑 - 分页、实体方法和数据查询**:请参考 nasl-book/K005-backend-logic--paginate-entity-method-data-query.md 文件 - **NASL 逻辑 - 内置工具函数**:请参考 nasl-book/K006-backend-logic--utils.md 文件 - **NASL 自定义配置**:请参考 nasl-book/K011-nasl--configuration.md 文件