参考我写的报警方式
if nomineeOrderList[i].RunMode == custodianconstant.RunModeTypeMix {
// 生成了混合订单需要报警
err = t.dingTalkRestyClient.DingTalkRobotSendMsg(ctx,
viper.GetString(“dependence.dingTalkFundTask.accessToken”),
viper.GetString(“dependence.dingTalkFundTask.secret”),
dingtalkmapping.DingTalkSendMsgRequest{Content: fmt.Sprintf(MixedOrderAlarmTalkMsgFmt, env.GetSquareBracketsEnv())},
)
if err != nil {
log.Errorf(err.Error())
}
}
}
,帮我写另外一份告警的代码:
if len(productInfoNotFoundOrders) > 0 {
// 钉钉报警这些订单产品查询失败,继续剩下的客户订单合单流程

}

可以参考以下代码来实现告警功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
if len(productInfoNotFoundOrders) > 0 {
// 钉钉报警这些订单产品查询失败,继续剩下的客户订单合单流程
err = t.dingTalkRestyClient.DingTalkRobotSendMsg(ctx,
viper.GetString("dependence.dingTalkProductInfo.accessToken"),
viper.GetString("dependence.dingTalkProductInfo.secret"),
dingtalkmapping.DingTalkSendMsgRequest{
Content: fmt.Sprintf("订单产品查询失败的订单数: %d", len(productInfoNotFoundOrders)),
},
)
if err != nil {
log.Errorf("钉钉报警发送失败: %s", err.Error())
}
}

这段代码中,DingTalkRobotSendMsg方法被用来发送告警信息,内容包含了失败订单的数量。如果发送失败,错误信息将被记录到日志中。

——来自AI问答宝 https://ai.wendabao.net

我得把这里面的订单告警订单都返回:
var productInfoNotFoundOrders []trademodel.TWealthOrder

理解了,你希望将失败的订单信息也包含在告警信息中。可以通过循环遍历这些订单并将它们的信息格式化为字符串,然后一起发送告警。以下是实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if len(productInfoNotFoundOrders) > 0 {
// 构建告警消息
var orderDetails string
for _, order := range productInfoNotFoundOrders {
orderDetails += fmt.Sprintf("订单ID: %s, 客户ID: %s\n", order.OrderID, order.CustomerID)
}

// 钉钉报警这些订单产品查询失败,继续剩下的客户订单合单流程
err = t.dingTalkRestyClient.DingTalkRobotSendMsg(ctx,
viper.GetString("dependence.dingTalkProductInfo.accessToken"),
viper.GetString("dependence.dingTalkProductInfo.secret"),
dingtalkmapping.DingTalkSendMsgRequest{
Content: fmt.Sprintf("订单产品查询失败的订单数: %d\n%s", len(productInfoNotFoundOrders), orderDetails),
},
)
if err != nil {
log.Errorf("钉钉报警发送失败: %s", err.Error())
}
}

在这段代码中,我们首先遍历 productInfoNotFoundOrders 列表,将每个订单的详细信息(如订单ID和客户ID)追加到 orderDetails 字符串中。然后,在发送告警信息时,将失败的订单数量和详细信息一起包含在消息内容中。这样,你可以在钉钉告警中看到具体的失败订单信息。

——来自AI问答宝 https://ai.wendabao.net

你的写法不是很优雅,你再看看这种写法:
orderList, err := w.OrderDao.SearchAll(ctx, nil, sWrapper)
if err != nil {
return err
}
if len(orderList) == 0 {
return nil
}
ordersNumberAndID := lo.Map(orderList, func(item tradedb.TWealthOrder, index int) string {
return fmt.Sprintf(“Number:%s,ID:%v;”, item.OrderNumber, item.Id)
})

err = w.dingTalkAPIClient.DingTalkRobotSendMsg(ctx,
    viper.GetString("dependence.dingTalkFundTask.accessToken"),
    viper.GetString("dependence.dingTalkFundTask.secret"),
    dingtalkmapping.DingTalkSendMsgRequest{Content: fmt.Sprintf(CanConsolidateNoDingTalkMsgFmt, env.GetSquareBracketsEnv(), strings.Join(ordersNumberAndID, "\n"))},
)

明白了,这种写法更为简洁优雅。根据你的示例,以下是改进后的告警代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if len(productInfoNotFoundOrders) > 0 {
ordersNumberAndID := lo.Map(productInfoNotFoundOrders, func(item trademodel.TWealthOrder, index int) string {
return fmt.Sprintf("订单ID: %s, 客户ID: %s;", item.OrderID, item.CustomerID)
})

err = t.dingTalkRestyClient.DingTalkRobotSendMsg(ctx,
viper.GetString("dependence.dingTalkProductInfo.accessToken"),
viper.GetString("dependence.dingTalkProductInfo.secret"),
dingtalkmapping.DingTalkSendMsgRequest{
Content: fmt.Sprintf("订单产品查询失败的订单数: %d\n%s", len(productInfoNotFoundOrders), strings.Join(ordersNumberAndID, "\n")),
},
)
if err != nil {
log.Errorf("钉钉报警发送失败: %s", err.Error())
}
}

在这段代码中,我们使用 lo.Map 方法将 productInfoNotFoundOrders 列表中的每个订单信息格式化为字符串,并将它们连接起来发送告警信息。这种方式使代码更简洁易读。

——来自AI问答宝 https://ai.wendabao.net

你看看我得代码,
// 过滤没能查出产品信息的订单
productInfoNotFoundOrders := lo.Filter(orders, func(order trademodel.TWealthOrder, index int) bool {
productBasicInfos, exists := channelProductBasicInfos[order.Channel]
if !exists {
return false
}

    return !lo.ContainsBy(productBasicInfos, func(item mktmapping.ProductBasicInfo) bool {
        return item.Symbol == order.ProductSymbol
    })
})

if len(productInfoNotFoundOrders) > 0 {
    // 钉钉报警这些订单产品查询失败,继续剩下的客户订单合单流程

}

好的,结合你提供的代码风格,以下是完成告警部分的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 过滤没能查出产品信息的订单
productInfoNotFoundOrders := lo.Filter(orders, func(order trademodel.TWealthOrder, index int) bool {
productBasicInfos, exists := channelProductBasicInfos[order.Channel]
if !exists {
return false
}

return !lo.ContainsBy(productBasicInfos, func(item mktmapping.ProductBasicInfo) bool {
return item.Symbol == order.ProductSymbol
})
})

if len(productInfoNotFoundOrders) > 0 {
// 构建告警消息
ordersNumberAndID := lo.Map(productInfoNotFoundOrders, func(order trademodel.TWealthOrder, index int) string {
return fmt.Sprintf("订单ID: %s, 客户ID: %s;", order.OrderID, order.CustomerID)
})

// 钉钉报警这些订单产品查询失败,继续剩下的客户订单合单流程
err := t.dingTalkRestyClient.DingTalkRobotSendMsg(ctx,
viper.GetString("dependence.dingTalkProductInfo.accessToken"),
viper.GetString("dependence.dingTalkProductInfo.secret"),
dingtalkmapping.DingTalkSendMsgRequest{
Content: fmt.Sprintf("订单产品查询失败的订单数: %d\n%s", len(productInfoNotFoundOrders), strings.Join(ordersNumberAndID, "\n")),
},
)
if err != nil {
log.Errorf("钉钉报警发送失败: %s", err.Error())
}
}

这段代码使用 lo.Filter 筛选出未能查询到产品信息的订单,然后使用 lo.Map 构建告警消息,将每个订单的详细信息格式化为字符串,并将它们连接起来发送告警信息。如果发送失败,错误信息将记录到日志中。

——来自AI问答宝 https://ai.wendabao.net

我觉得你没理解这段代码,你一行一行阅读解释这段代码看看
// 过滤没能查出产品信息的订单
productInfoNotFoundOrders := lo.Filter(orders, func(order trademodel.TWealthOrder, index int) bool {
productBasicInfos, exists := channelProductBasicInfos[order.Channel]
if !exists {
return false
}

    return !lo.ContainsBy(productBasicInfos, func(item mktmapping.ProductBasicInfo) bool {
        return item.Symbol == order.ProductSymbol
    })
})

理解你的要求,我会逐行解释这段代码。

1
2
// 过滤没能查出产品信息的订单
productInfoNotFoundOrders := lo.Filter(orders, func(order trademodel.TWealthOrder, index int) bool {
  • 这一行使用 lo.Filter 方法,从 orders 列表中过滤出没有查询到产品信息的订单。lo.Filter 接受一个列表和一个函数作为参数,返回一个新的列表,包含所有使函数返回 true 的元素。
1
2
3
4
productBasicInfos, exists := channelProductBasicInfos[order.Channel]
if !exists {
return false
}
  • 在过滤函数内部,首先从 channelProductBasicInfos 字典中获取当前订单 order 所对应的 Channel 的产品基本信息列表 productBasicInfos。如果不存在对应的 Channel,则返回 false,表示该订单不符合过滤条件,不包含在 productInfoNotFoundOrders 列表中。
1
2
3
return !lo.ContainsBy(productBasicInfos, func(item mktmapping.ProductBasicInfo) bool {
return item.Symbol == order.ProductSymbol
})
  • 如果存在对应的 Channel,则使用 lo.ContainsBy 方法检查 productBasicInfos 列表中是否存在与当前订单 orderProductSymbol 匹配的产品基本信息。lo.ContainsBy 接受一个列表和一个函数,返回一个布尔值,表示列表中是否存在使函数返回 true 的元素。
  • 如果 productBasicInfos 列表中不存在与 order.ProductSymbol 匹配的产品基本信息,函数返回 true,表示该订单符合过滤条件,即没有查出产品信息,应包含在 productInfoNotFoundOrders 列表中。

综上,这段代码的目的是从 orders 列表中过滤出那些没有查到对应产品信息的订单,并将它们保存在 productInfoNotFoundOrders 列表中。

接下来是根据上述过滤结果发送钉钉告警的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if len(productInfoNotFoundOrders) > 0 {
// 构建告警消息
ordersNumberAndID := lo.Map(productInfoNotFoundOrders, func(order trademodel.TWealthOrder, index int) string {
return fmt.Sprintf("订单ID: %s, 客户ID: %s;", order.OrderID, order.CustomerID)
})

// 钉钉报警这些订单产品查询失败,继续剩下的客户订单合单流程
err := t.dingTalkRestyClient.DingTalkRobotSendMsg(ctx,
viper.GetString("dependence.dingTalkProductInfo.accessToken"),
viper.GetString("dependence.dingTalkProductInfo.secret"),
dingtalkmapping.DingTalkSendMsgRequest{
Content: fmt.Sprintf("订单产品查询失败的订单数: %d\n%s", len(productInfoNotFoundOrders), strings.Join(ordersNumberAndID, "\n")),
},
)
if err != nil {
log.Errorf("钉钉报警发送失败: %s", err.Error())
}
}

这段代码的功能是:如果有未查询到产品信息的订单,将这些订单的ID和客户ID格式化成字符串,生成告警消息,通过钉钉机器人发送告警,并在发送失败时记录错误信息。

——来自AI问答宝 https://ai.wendabao.net

我们打印订单的时候应该支持一行一个订单

明白了,我们可以在告警消息中将每个订单的信息打印在单独的一行。以下是优化后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 过滤没能查出产品信息的订单
productInfoNotFoundOrders := lo.Filter(orders, func(order trademodel.TWealthOrder, index int) bool {
productBasicInfos, exists := channelProductBasicInfos[order.Channel]
if !exists {
return false
}

return !lo.ContainsBy(productBasicInfos, func(item mktmapping.ProductBasicInfo) bool {
return item.Symbol == order.ProductSymbol
})
})

if len(productInfoNotFoundOrders) > 0 {
// 构建告警消息
ordersNumberAndID := lo.Map(productInfoNotFoundOrders, func(order trademodel.TWealthOrder, index int) string {
return fmt.Sprintf("订单ID: %s, 客户ID: %s", order.OrderID, order.CustomerID)
})

// 钉钉报警这些订单产品查询失败,继续剩下的客户订单合单流程
err := t.dingTalkRestyClient.DingTalkRobotSendMsg(ctx,
viper.GetString("dependence.dingTalkProductInfo.accessToken"),
viper.GetString("dependence.dingTalkProductInfo.secret"),
dingtalkmapping.DingTalkSendMsgRequest{
Content: fmt.Sprintf("订单产品查询失败的订单数: %d\n%s", len(productInfoNotFoundOrders), strings.Join(ordersNumberAndID, "\n")),
},
)
if err != nil {
log.Errorf("钉钉报警发送失败: %s", err.Error())
}
}

在这个版本的代码中,ordersNumberAndID 被构建为一个字符串列表,其中每个元素是一个订单的ID和客户ID,并且每个订单的信息都放在单独的一行。最终,告警消息通过 strings.Join 方法,将这些订单信息按行连接起来并发送。这样,告警消息会清晰地显示每个订单的信息,每行一个订单。

——来自AI问答宝 https://ai.wendabao.net

把这里也写一下:
// 过滤出所有非交易日的交易日历,这部分产品订单不进行合单
holidayTodayCalendar := lo.Filter(fundCalendars, func(item *mktmapping.FundCalendar, index int) bool {
return item.IsHoliday(carbon.Now().ToDateStruct(), mktwealthconstant.HolidayType_None)
})

if len(holidayTodayCalendar) > 0 {
    l.Warn("")
    // 钉钉报警这些产品当日非交易日,不退出
}

好的,以下是包含非交易日过滤和钉钉告警的完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// 过滤没能查出产品信息的订单
productInfoNotFoundOrders := lo.Filter(orders, func(order trademodel.TWealthOrder, index int) bool {
productBasicInfos, exists := channelProductBasicInfos[order.Channel]
if !exists {
return false
}

return !lo.ContainsBy(productBasicInfos, func(item mktmapping.ProductBasicInfo) bool {
return item.Symbol == order.ProductSymbol
})
})

if len(productInfoNotFoundOrders) > 0 {
// 构建告警消息
ordersNumberAndID := lo.Map(productInfoNotFoundOrders, func(order trademodel.TWealthOrder, index int) string {
return fmt.Sprintf("订单ID: %s, 客户ID: %s", order.OrderID, order.CustomerID)
})

// 钉钉报警这些订单产品查询失败,继续剩下的客户订单合单流程
err := t.dingTalkRestyClient.DingTalkRobotSendMsg(ctx,
viper.GetString("dependence.dingTalkProductInfo.accessToken"),
viper.GetString("dependence.dingTalkProductInfo.secret"),
dingtalkmapping.DingTalkSendMsgRequest{
Content: fmt.Sprintf("订单产品查询失败的订单数: %d\n%s", len(productInfoNotFoundOrders), strings.Join(ordersNumberAndID, "\n")),
},
)
if err != nil {
log.Errorf("钉钉报警发送失败: %s", err.Error())
}
}

// 过滤出所有非交易日的交易日历,这部分产品订单不进行合单
holidayTodayCalendar := lo.Filter(fundCalendars, func(item *mktmapping.FundCalendar, index int) bool {
return item.IsHoliday(carbon.Now().ToDateStruct(), mktwealthconstant.HolidayType_None)
})

if len(holidayTodayCalendar) > 0 {
l.Warn("今日有非交易日产品订单,不进行合单")

// 构建非交易日告警消息
holidayProductInfo := lo.Map(holidayTodayCalendar, func(item *mktmapping.FundCalendar, index int) string {
return fmt.Sprintf("产品代码: %s", item.ProductCode)
})

// 钉钉报警这些产品当日非交易日,不退出
err := t.dingTalkRestyClient.DingTalkRobotSendMsg(ctx,
viper.GetString("dependence.dingTalkHoliday.accessToken"),
viper.GetString("dependence.dingTalkHoliday.secret"),
dingtalkmapping.DingTalkSendMsgRequest{
Content: fmt.Sprintf("当日非交易日的产品数量: %d\n%s", len(holidayTodayCalendar), strings.Join(holidayProductInfo, "\n")),
},
)
if err != nil {
log.Errorf("钉钉非交易日报警发送失败: %s", err.Error())
}
}

在这段代码中:

  1. 过滤出 productInfoNotFoundOrders 并发送告警。
  2. 过滤出 holidayTodayCalendar 并发送告警,包含每个非交易日产品的代码。

每个订单和产品的信息都将打印在单独的一行,以确保告警信息的清晰和可读性。

——来自AI问答宝 https://ai.wendabao.net

for k, v := range processOrdersResult.MapNomiOdr {
    err := t.processAndSaveNomineeOrders(ctx, k, v, processResult.MapNomiOSubOrders, fundType)
    if err != nil {
        l.Warn("Some collection orders failed to be written to the database today", zap.Error(err))
        // 告警发生错误的集合订单产品名称和集合订单下面所有的子订单ID
        
        continue
    }
}

明白了,结合你之前的告警写法,我们可以在处理并保存集合订单时发生错误时,将发生错误的集合订单产品名称和所有子订单的ID通过钉钉告警。以下是完整的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
for k, v := range processOrdersResult.MapNomiOdr {
err := t.processAndSaveNomineeOrders(ctx, k, v, processResult.MapNomiOSubOrders, fundType)
if err != nil {
l.Warn("Some collection orders failed to be written to the database today", zap.Error(err))

// 构建告警消息,包含集合订单产品名称和所有子订单ID
subOrderIDs := lo.Map(v, func(order trademodel.TWealthOrder, index int) string {
return order.OrderID
})

productName := k // 集合订单产品名称

// 钉钉告警
dingTalkMsg := fmt.Sprintf("集合订单产品名称: %s\n子订单ID列表:\n%s", productName, strings.Join(subOrderIDs, "\n"))

err = t.dingTalkRestyClient.DingTalkRobotSendMsg(ctx,
viper.GetString("dependence.dingTalkOrderError.accessToken"),
viper.GetString("dependence.dingTalkOrderError.secret"),
dingtalkmapping.DingTalkSendMsgRequest{
Content: dingTalkMsg,
},
)
if err != nil {
log.Errorf("钉钉报警发送失败: %s", err.Error())
}

continue
}
}

在这段代码中:

  1. 遍历 processOrdersResult.MapNomiOdr 处理并保存集合订单。
  2. 如果发生错误,记录警告日志。
  3. 构建告警消息,包含集合订单产品名称和所有子订单的ID。
  4. 通过钉钉发送告警消息。
  5. 如果钉钉告警发送失败,记录错误日志。

这样,每个发生错误的集合订单及其子订单信息都可以通过钉钉告警清晰地显示出来。

——来自AI问答宝 https://ai.wendabao.net

我告诉你一下,集合订单是processOrdersResult.MapNomiOdr,子订单列表是 processResult.MapNomiOSubOrders
// processOrdersResult 结构体用于存储 processOrders 函数的处理结果
type processOrdersResult struct {
MapNomiOdr map[string]*nomineeOrderInfo // 存储每个合单类型的汇总信息
MapNomiOSubOrders map[string][]int // 存储每个合单类型对应的原始订单ID列表
}

明白了,基于你提供的信息,我们需要从 processOrdersResult.MapNomiOdr 中获取集合订单的产品名称,并从 processResult.MapNomiOSubOrders 中获取对应的子订单ID列表。以下是完整的告警代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
for k, v := range processOrdersResult.MapNomiOdr {
err := t.processAndSaveNomineeOrders(ctx, k, v, processOrdersResult.MapNomiOSubOrders, fundType)
if err != nil {
l.Warn("Some collection orders failed to be written to the database today", zap.Error(err))

// 获取集合订单的产品名称
productName := k

// 获取子订单ID列表
subOrderIDs, exists := processOrdersResult.MapNomiOSubOrders[k]
if !exists {
subOrderIDs = []int{} // 如果不存在,初始化为空列表
}

// 构建告警消息,包含集合订单产品名称和所有子订单ID
subOrderIDStrings := lo.Map(subOrderIDs, func(orderID int, index int) string {
return fmt.Sprintf("%d", orderID)
})

dingTalkMsg := fmt.Sprintf("集合订单产品名称: %s\n子订单ID列表:\n%s", productName, strings.Join(subOrderIDStrings, "\n"))

// 钉钉告警
err = t.dingTalkRestyClient.DingTalkRobotSendMsg(ctx,
viper.GetString("dependence.dingTalkOrderError.accessToken"),
viper.GetString("dependence.dingTalkOrderError.secret"),
dingtalkmapping.DingTalkSendMsgRequest{
Content: dingTalkMsg,
},
)
if err != nil {
log.Errorf("钉钉报警发送失败: %s", err.Error())
}

continue
}
}

在这段代码中:

  1. 遍历 processOrdersResult.MapNomiOdr 处理并保存集合订单。
  2. 如果发生错误,记录警告日志。
  3. 获取集合订单的产品名称。
  4. 获取子订单ID列表,并将它们转换为字符串形式。
  5. 构建告警消息,包含集合订单产品名称和所有子订单ID。
  6. 通过钉钉发送告警消息。
  7. 如果钉钉告警发送失败,记录错误日志。

这样,告警消息中会详细列出每个发生错误的集合订单的产品名称和对应的子订单ID。

——来自AI问答宝 https://ai.wendabao.net

我想按照这种形式打印,
[uat] 合单监控: 监控到以下集合订单合单失败记录: [028221580 366120148 366217081 366220983 366220985 366221573 366221577 366521795 368209881 368220153 666990156 666990162 99688500801] 共计13条

明白了,你希望打印的告警消息格式应为:

1
[uat] 合单监控: 监控到以下集合订单合单失败记录: [子订单ID1 子订单ID2 ...] 共计N条

以下是更新后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
for k, v := range processOrdersResult.MapNomiOdr {
err := t.processAndSaveNomineeOrders(ctx, k, v, processOrdersResult.MapNomiOSubOrders, fundType)
if err != nil {
l.Warn("Some collection orders failed to be written to the database today", zap.Error(err))

// 获取集合订单的产品名称
productName := k

// 获取子订单ID列表
subOrderIDs, exists := processOrdersResult.MapNomiOSubOrders[k]
if !exists {
subOrderIDs = []int{} // 如果不存在,初始化为空列表
}

// 将子订单ID列表转换为字符串
subOrderIDStrings := lo.Map(subOrderIDs, func(orderID int, index int) string {
return fmt.Sprintf("%d", orderID)
})

// 构建告警消息,包含集合订单产品名称和所有子订单ID
dingTalkMsg := fmt.Sprintf(
"[uat] 合单监控: 监控到以下集合订单合单失败记录: [%s] 共计%d条",
strings.Join(subOrderIDStrings, " "),
len(subOrderIDStrings),
)

// 钉钉告警
err = t.dingTalkRestyClient.DingTalkRobotSendMsg(ctx,
viper.GetString("dependence.dingTalkOrderError.accessToken"),
viper.GetString("dependence.dingTalkOrderError.secret"),
dingtalkmapping.DingTalkSendMsgRequest{
Content: dingTalkMsg,
},
)
if err != nil {
log.Errorf("钉钉报警发送失败: %s", err.Error())
}

continue
}
}

在这段代码中:

  1. 遍历 processOrdersResult.MapNomiOdr 处理并保存集合订单。
  2. 如果发生错误,记录警告日志。
  3. 获取集合订单的产品名称。
  4. 获取子订单ID列表,并将它们转换为字符串形式。
  5. 构建告警消息,包含 [uat] 合单监控: 监控到以下集合订单合单失败记录: [子订单ID1 子订单ID2 ...] 共计N条 格式。
  6. 通过钉钉发送告警消息。
  7. 如果钉钉告警发送失败,记录错误日志。

这样,告警消息将详细列出每个发生错误的集合订单的产品名称和对应的子订单ID,格式符合你的要求。

——来自AI问答宝 https://ai.wendabao.net