跳到主要内容

自定义业务服务

服务元素是 JitAI 平台中负责业务逻辑处理的核心组件,用于封装数据处理、业务计算和系统交互逻辑。

服务元素的分层结构为 Meta(services.Meta)→ Type(services.NormalType)→ 实例。开发者可通过 JitAI 的可视化开发工具快捷创建服务实例,也可以手动编写代码实现复杂的业务逻辑。

服务目录结构

每个服务元素使用独立的文件夹,路径规则:[应用根目录]/services/[服务名称]

[应用根目录]/services/MyBusinessService/
├── e.json # 服务元素的声明文件
├── service.py # 服务元素的实现文件
└── __init__.py # 服务元素所在包的初始化文件

e.json 服务声明文件

用于定义服务的元数据、函数签名及参数配置。在此处声明的函数,需要在 service.py 中进行具体实现。

e.json
{
"title": "我的业务服务",
"type": "services.NormalType",
"backendBundleEntry": ".",
"functionList": [
{
"name": "calculateTotal",
"title": "计算总价",
"args": [
{
"name": "amount",
"title": "金额",
"dataType": "Numeric"
},
{
"name": "discount",
"title": "折扣率",
"dataType": "Numeric"
}
],
"returnType": "Numeric",
"argsToDatatype": 1
}
]
}

e.json 配置项

属性类型必填说明
titleString服务标题,显示在开发工具中
typeString固定值 "services.NormalType"
functionListArray服务函数定义列表
eventDescsArray自定义事件
functionList 函数定义
属性类型说明
nameString函数名,需与 py 文件中方法名一致
titleString函数标题
argsArray参数列表
returnTypeString返回值类型 (如 Stext, Numeric, RowData 等)
argsToDatatypeInteger推荐设为 1。自动将入参转为 datatypes 对象
args 参数定义
属性类型说明
nameString参数名
dataTypeString参数数据类型
genericString泛型配置(针对 List/Map/RowData)

service.py 实现文件

service.py 是服务逻辑的载体。e.jsonfunctionList 声明的每一个函数,都必须在 service.py 的服务类中有一个同名的成员函数实现。

service.py
# -*-coding:utf-8-*-
from datatypes.Meta import datatypes
from services.NormalType import NormalService

class MyBusinessService(NormalService):
def calculateTotal(self, amount, discount):
# 定义返回变量
result = datatypes.Numeric.new({"title": "结果"}, 0)

# 业务逻辑计算 (使用公式函数,而非原生运算符)
# result = amount * (1 - discount)
temp = datatypes.Numeric.new({}, (SUB(1, discount.value)))
result.value = (MUL(amount.value, temp.value))

return result.value

__init__.py 初始化文件

__init__.py
# -*-coding:utf-8-*-
from .service import MyBusinessService

服务开发规范

在编写 service.py 时,必须遵循 JitAI 的特定开发规范,以确保服务在运行时平台中正确执行。

变量声明与赋值

为了确保全代码开发可视化开发之间的无缝切换,强烈建议所有变量均使用 Jit数据类型 对象进行封装。使用 Python 原生类型(如 int, str, dict)编写的逻辑虽然可以正常运行,但这些变量将无法在可视化开发界面中显示和操作。

声明格式: 变量 = datatypes.<类型>.new(<配置字典>, <初始值>)

# 1. 基础类型
count = datatypes.Numeric.new({"title": "计数"}, 0)
name = datatypes.Stext.new({"title": "名称"}, "JitAI")

# 2. 字典/对象 (JitDict)
# variableList 描述字典内部结构
result = datatypes.JitDict.new({
"variableList": [
{"name": "code", "dataType": "Numeric"},
{"name": "msg", "dataType": "Stext"}
]
}, {"code": 200, "msg": "success"})

# 3. 动态映射 (JitMap)
# generic 描述 Value 的类型
provinceMap = datatypes.JitMap.new({
"generic": "Numeric",
"valueConfig": {"dataType": "Numeric"}
}, {})

# 4. 列表 (JitList)
nameList = datatypes.JitList.new({
"generic": "Stext"
}, [])

# 5. 模型数据行 (RowData)
user = datatypes.RowData.new({
"generic": "models.UserModel"
}, {})

赋值与取值:

  • 赋值:使用 .value 属性(Address 等复合类型除外)。
  • 取值:使用 .value 获取原始值用于计算。
# 赋值
variable.value = 100
varA.value = varB.value

# 公式赋值(必须使用括号包裹公式函数)
total.value = (SUM(price.value, tax.value))

所有变量必须用 datatypes.*.new 包装,配置不能为空datatypes.*.new 的第一个参数(配置字典)不能为空,特别是容器类型(JitDict, JitMap, JitList, RowData, RowList)必须指定泛型(generic)或结构定义。

类型必需配置示例
JitDictvariableList 不能为空{"variableList": [{"name": "x", "title": "X ,"dataType": "Numeric"}]}
JitMapvalueConfiggeneric 不能为空{"generic": "Numeric", "valueConfig": {"dataType": "Numeric"}}
JitListgenericvariableConfig{"generic": "JitDict","variableConfig": {"dataType": "JitDict", "variableList": [{"name": "x", "title": "X, "dataType": "Numeric"}]}}
RowData/RowListgeneric 不能为空{"generic": "models.CustomerModel"}

变量用法建议 不建议将对象的属性赋值给临时变量后再使用,直接使用完整路径即可。

  • prov = item.fc02.province; map.set(prov, ...)
  • map.set(item.fc02.province, ...)

逻辑控制与运算

**强烈推荐使用 datatypes 提供的对象方法或公式函数**进行业务对象的比较和计算。

虽然使用 Python 原生运算符(如 +, -, >, == 等)编写的代码在运行时通常也能正常工作,但这类代码逻辑无法被 JitAI 的可视化开发工具解析和展示,会导致该服务函数无法在可视化界面中进行后续的查看和编辑。

操作❌ 不推荐 (原生运算符)✅ 推荐 (JitAI API)说明
比较if age > 18:if age.gt(18):可视化兼容
相等if status == 1:if status.isEqual(1):可视化兼容
判空if name is None:if name.isNull():可视化兼容
非空if name:if name.isNotNull():可视化兼容
加法sum = a + bsum.value = (SUM(a.value, b.value))需导入公式函数
拼接str = "Hello" + namestr.value = (CONCAT("Hello", name.value))需导入公式函数

常用判断方法:

  • .gt(val): 大于 (Greater Than)
  • .gte(val): 大于等于 (Greater Than or Equal)
  • .lt(val): 小于 (Less Than)
  • .lte(val): 小于等于 (Less Than or Equal)
  • .isEqual(val): 等于
  • .notEqual(val): 不等于
  • .isNull(): 为空
  • .isNotNull(): 不为空

循环遍历

遍历列表(如查询结果)时,直接迭代 RowListJitList 对象。

# result.rowDatas 是一个 RowList
for item in result.rowDatas:
# 直接访问字段
if item.age.gt(60):
item.category.value = "Senior"
item.save()

函数入参与返回规范

1. 入参处理 当服务函数被调用时,系统会根据 e.json 中的配置处理入参:

  • 作为被调用方(定义函数时):无论调用方传入的是原始值还是对象,函数内部接收到的参数始终是 datatypes 对象(前提是 e.json 中配置了 "argsToDatatype": 1)。开发者应直接把入参当作对象处理。
  • 作为调用方(调用其他函数时):调用其他服务函数时,既可以传递 datatypes 对象,也可以传递原始值(如 100, "test")。系统会自动将原始值封装为对应的 datatypes 对象。

2. 返回值规范 为了保持接口的通用性,服务函数建议返回原始值(即 result.value),而不是 datatypes 对象本身。

  • 这样调用方可以直接获取结果数据,无需关心对象的内部结构。
  • 如果调用方是前端或其他外部系统,返回原始值(JSON 兼容格式)是标准做法。

上下文与常用工具

在服务开发中,经常需要获取当前用户信息、系统时间,记录运行日志或调用其他服务。

全局变量

JitAI 提供了 GlobalVar 对象来访问当前用户和系统时间等上下文信息。

当前用户 (currentUser)

获取当前登录用户的上下文信息。currentUser 属于工作成员类型 (Member),支持获取用户ID、名称等属性。

当前时间 (currentTime)

获取系统当前时间。currentTime 属于日期时间类型 (DateTime),支持获取年、月、日等时间分量。

# 需导入 GlobalVar
from globals.GlobalVar import GlobalVar

def my_function(self):
# 1. 获取当前用户
current_user_name = GlobalVar.currentUser.getName()

# 2. 获取当前时间
current_year = GlobalVar.currentTime.year

日志记录

推荐使用 jit.commons.utils.logger 记录业务日志,便于排查问题。

from jit.commons.utils.logger import log

def processPayment(self, orderId):
log.info(f"开始处理订单支付: {orderId}")

try:
# ... 支付逻辑 ...
log.debug("支付接口调用成功")
except Exception as e:
log.error(f"支付失败: {str(e)}")
log.exception(e) # 记录完整堆栈

调用其他服务

在服务中调用其他服务主要有两种方式:

方式一:直接引用 (app.<fullName>) (推荐)

直接通过 app 对象链式调用。语法更简洁,且IDE通常能提供更好的补全支持(如果已生成桩代码)。

def checkout(self, userId, items):
# 1. 直接调用用户服务
isValid = app.services.UserService.checkUserStatus(userId)

# 2. 直接调用库存服务
app.services.InventoryService.deduct(items)

方式二:使用 app.getElement(<fullName>)

通过字符串形式获取服务实例。这种方式更动态,适合在无法确定服务是否存在时使用。

def checkout(self, userId, items):
# 1. 调用用户服务检查状态
userSvc = app.getElement("services.UserService")
isValid = userSvc.checkUserStatus(userId)

# 2. 调用库存服务扣减库存
inventorySvc = app.getElement("services.InventoryService")
inventorySvc.deduct(items)

核心功能开发

模型数据操作

在服务中调用模型(Models)进行增删改查是核心场景。

查询数据 (Query)

使用Q表达式进行条件筛选。

# 构造返回结构
result = datatypes.JitDict.new({
"variableList": [
{"name": "rowDatas", "dataType": "RowList", "generic": "models.CustomerModel"},
{"name": "totalCount", "dataType": "Numeric"}
]
}, app.models.CustomerModel.query(
Q(Q("age", "gt", 18)), # 筛选条件
None, # 排序字段
None, # 查询字段(None=全部)
1, # 页码
-1, # 分页大小(-1=全部)
2 # 查询模式
))

数据保存 (Save)

# 1. 创建新数据
user = datatypes.RowData.new({"generic": "models.UserModel"}, {})
user.name.value = "ZhangSan"
user.save()

# 2. 修改已有数据
user = app.models.UserModel.get("Q(Q('id', '=', 1))", [])
user.email.value = "new@example.com"
user.save()

事件机制

服务元素支持定义和触发自定义事件,与其他业务逻辑解耦。

定义事件

在服务的声明文件 e.json 中,通过 eventDescs 字段定义事件。

e.json
{
"eventDescs": [
{
"name": "onOrderCreated",
"title": "订单创建完成",
"desc": "当新订单创建成功后触发"
}
]
}

触发事件

service.py 中,使用 app.event.publish 方法触发已定义的事件。

def createOrder(self, orderInfo):
# ... 业务逻辑:创建订单 ...

# 触发事件
# sender: 事件的全限定名,格式为 "services.[服务名].[事件名]"
# args: 位置参数元组
# kwargs: 关键字参数字典
app.event.publish(
sender="services.MyBusinessService.onOrderCreated",
args=(),
kwargs={"orderId": 12345, "amount": 100.0}
)

事件订阅

详情请见自定义事件参考

完整代码示例

以下示例展示了一个完整的 OrderService,包含了依赖引用、上下文获取、数据查询、逻辑计算、异常处理及事件触发。

e.json

services/OrderService/e.json
{
"title": "订单服务",
"type": "services.NormalType",
"backendBundleEntry": ".",
"functionList": [
{
"name": "createOrder",
"title": "创建订单",
"args": [
{"name": "skuId", "title": "商品ID", "dataType": "Numeric"},
{"name": "count", "title": "数量", "dataType": "Numeric"}
],
"returnType": "JitDict",
"argsToDatatype": 1
}
],
"eventDescs": [
{"name": "onOrderPayed", "title": "订单已支付"}
]
}

service.py

services/OrderService/service.py
# -*-coding:utf-8-*-
from datatypes.Meta import datatypes
from services.NormalType import NormalService
from jit.commons.utils.logger import log

class OrderService(NormalService):

def createOrder(self, skuId, count):
# 1. 初始化返回结果
result = datatypes.JitDict.new({
"variableList": [
{"name": "code", "dataType": "Numeric"},
{"name": "msg", "dataType": "Stext"},
{"name": "orderId", "dataType": "Numeric"}
]
}, {"code": 200, "msg": "Success", "orderId": 0})

try:
log.info(f"User {GlobalVar.currentUser.name} creating order for SKU {skuId.value}")

# 2. 检查库存 (调用其他服务)
hasStock = app.services.InventoryService.checkStock(skuId, count)

if not hasStock:
result.code.value = 400
result.msg.value = "库存不足"
return result.value

# 3. 计算金额 (模型查询与公式计算)
sku = app.models.SkuModel.get(Q(Q('id', '=', {skuId.value})), [])
totalPrice = datatypes.Numeric.new({}, (MUL(sku.price.value, count.value)))

# 4. 保存订单 (模型保存)
order = datatypes.RowData.new({"generic": "models.OrderModel"}, {})
order.creator.value = currentUser.id
order.skuId.value = skuId.value
order.count.value = count.value
order.amount.value = totalPrice.value
order.save()

# 更新返回结果
result.orderId.value = order.id.value

# 5. 触发事件
app.event.publish(
sender="services.OrderService.onOrderPayed",
args=(),
kwargs={"orderId": order.id.value}
)

log.info(f"Order created successfully: {order.id.value}")
return result.value

except Exception as e:
log.error("Create order failed")
log.exception(e)
result.code.value = 500
result.msg.value = str(e)
return result.value