Loading... <div class="tip share">请注意,本文编写于 205 天前,最后修改于 189 天前,其中某些信息可能已经过时。</div> ## 向我介绍go语言的error.as方法 Go 语言的 `error.As` 方法用于将错误值转换为特定类型的错误,并检查这种转换是否成功。这个方法是在 Go 1.13 中引入的,属于错误处理的改进。 具体来说,`error.As` 方法接受两个参数: 1. `err` 是一个错误值。 2. `target` 是一个指向你希望 `err` 转换成的目标错误类型的指针。 如果 `err` 可以成功转换成 `target` 指向的错误类型,`error.As` 方法会将 `err` 的值存入 `target` 中,并返回 `true`。否则,它不修改 `target` 并返回 `false`。 这个方法通常用在需要根据错误的具体类型来进行不同处理时。使用 `error.As` 可以安全地尝试将一个错误转换为更具体的错误类型,而不用担心程序因类型不匹配而崩溃。 下面是一个使用 `error.As` 的示例: ```go package main import ( "errors" "fmt" "os" ) func main() { err := openFile("nonexistent.txt") if err != nil { var pathError *os.PathError if errors.As(err, &pathError) { fmt.Println("Failed at path:", pathError.Path) } else { fmt.Println("Generic error:", err) } } } func openFile(name string) error { _, err := os.Open(name) return err } ``` 在这个例子中,如果打开文件失败,程序会检查错误是否为 `os.PathError` 类型。如果是,它将打印出错的文件路径;如果不是,它则打印通用错误信息。这样的错误处理使得错误信息更具体,帮助开发者更好地理解和处理错误情况。 ## 我这个里面出来的错误使用error.as报警 ```go for _, v := range channelWithProductCode { err := t.consolidateOrdersServer(ctx, v.ProductCode, v.Channel, v.ProductSymbol, v.Direction) if err != nil { // TODO error.as解析里面的错误信息然后钉钉告警 l.Error("search can consolidate productCode and Channel failed", zap.Error(err)) continue } } ``` 在你提供的代码片段中,你想使用 `error.As` 方法来解析错误信息,然后根据解析出来的特定错误类型进行相应的处理,比如发送钉钉告警。以下是如何在你的代码中实现这一功能的示例: 首先,你需要确定可以从错误中解析出哪些具体的错误类型。假设你已经有一些自定义的错误类型,我们可以使用 `error.As` 来检查并解析这些错误。如果没有自定义错误类型,你可能需要根据错误信息本身来进行一些字符串匹配或处理。 这里以 `CustomError` 为例,假设这是你自定义的一个错误类型,你想在发生此错误时发送钉钉告警: ```go for _, v := range channelWithProductCode { err := t.consolidateOrdersServer(ctx, v.ProductCode, v.Channel, v.ProductSymbol, v.Direction) if err != nil { var customErr *CustomError if errors.As(err, &customErr) { // 如果错误是CustomError类型,执行钉钉告警 sendDingTalkAlert(customErr) } // 记录错误日志 l.Error("search can consolidate productCode and Channel failed", zap.Error(err)) continue } } // CustomError类型定义 type CustomError struct { Message string Code int } func (e *CustomError) Error() string { return fmt.Sprintf("%d - %s", e.Code, e.Message) } // 发送钉钉告警的函数 func sendDingTalkAlert(err error) { // 实现钉钉告警的代码 // 这里需要你根据自己的钉钉API接口进行实现 fmt.Println("Sending alert to DingTalk:", err) } ``` 在这个示例中,如果 `consolidateOrdersServer` 函数返回的错误可以被转换为 `CustomError` 类型,代码将调用 `sendDingTalkAlert` 函数来发送告警到钉钉。你需要替换 `CustomError` 和 `sendDingTalkAlert` 函数的实现,使其适应你的应用程序的具体需求。 如果你还没有创建钉钉告警的实现,你需要参考钉钉的开发文档来创建相应的 API 调用,通常是通过 HTTP 请求发送一个特定格式的消息到钉钉服务器。如果需要更多关于如何实现发送钉钉告警的具体代码,我可以进一步帮助你。 ## 我定义了一个error.go的代码: ```go package consolidate import ( stderrors "errors" "fmt" ) type ErrorType int const ( ErrorTypeFetchProductInfo ErrorType = 1 ) type Error struct { Err error Msg string ProductCode string ProductSymbol string Channel string Type ErrorType } func(e Error) Error() string { return fmt.Sprintf("consolidate orders failed becouse: %v, product code: %v, channel: %v; innerErr:%v", e.Msg, e.ProductCode, e.Channel, e.Err) } var ( ErrFetchProductInfo = stderrors.New("error fetching product info") ErrFetchProductHolidayCalendar = stderrors.New("error fetching product holiday calendar") ErrConsolidate = stderrors.New("error consolidate") ) ``` 根据你定义的 `Error` 类型和相关错误常量,我们可以在你的业务逻辑中使用 `error.As` 方法来正确地解析和处理这些错误。例如,如果你想在特定错误类型(如 `ErrorTypeFetchProductInfo`)发生时发送钉钉告警,你可以这样实现: 1. 修改你的循环中的错误处理代码,以使用 `error.As` 方法来检查和解析错误。 2. 调用一个函数来处理钉钉告警,当发现特定类型的错误时。 以下是具体的代码实现: ```go package main import ( "context" "consolidate" "fmt" "log" "go.uber.org/zap" ) func main() { // 模拟的数据和上下文 ctx := context.TODO() channelWithProductCode := []consolidate.Error{ {Msg: "test error", ProductCode: "PC001", Channel: "Online", ProductSymbol: "SYM001", Type: consolidate.ErrorTypeFetchProductInfo, Err: consolidate.ErrFetchProductInfo}, // 添加其他数据以进行测试 } for _, v := range channelWithProductCode { err := processOrders(ctx, v) if err != nil { var customErr *consolidate.Error if stderrors.As(err, &customErr) { handleError(customErr) } else { log.Println("Unhandled error:", err) } continue } } } func processOrders(ctx context.Context, v consolidate.Error) error { // 这里是模拟处理订单的逻辑 return &v } func handleError(err *consolidate.Error) { l := zap.NewExample() switch err.Type { case consolidate.ErrorTypeFetchProductInfo: sendDingTalkAlert(err) l.Error("Fetch Product Info failed", zap.Error(err)) default: l.Error("Error occurred", zap.Error(err)) } } func sendDingTalkAlert(err *consolidate.Error) { // 发送钉钉告警的实现细节 fmt.Println("Sending alert to DingTalk for error:", err) } ``` 在这个示例中,`processOrders` 函数模拟处理订单的逻辑,并可能返回一个 `consolidate.Error` 类型的错误。`handleError` 函数根据错误类型来决定是否发送钉钉告警。如果错误是 `ErrorTypeFetchProductInfo` 类型,它将调用 `sendDingTalkAlert` 函数来发送告警。 你需要实现 `sendDingTalkAlert` 函数,使其通过合适的 API 调用实际发送告警。这通常涉及构建一个钉钉机器人,然后通过 HTTP 请求发送消息。 这个实现确保你能根据错误类型进行细粒度的错误处理和告警,有助于提升系统的健壮性和及时响应潜在问题。 ## 这个continue的位置是不是可以往外面一层移一点,另外优化下这一版的error.as代码 你提到的将 `continue` 语句移出错误处理逻辑的建议很有道理,这样可以简化代码结构,使其更加清晰。同时,优化 `error.As` 的使用,可以提高代码的可读性和维护性。下面是根据你的需求调整后的代码: ```go package main import ( "context" "consolidate" "fmt" "log" "go.uber.org/zap" ) func main() { // 模拟的数据和上下文 ctx := context.TODO() channelWithProductCode := []consolidate.Error{ {Msg: "test error", ProductCode: "PC001", Channel: "Online", ProductSymbol: "SYM001", Type: consolidate.ErrorTypeFetchProductInfo, Err: consolidate.ErrFetchProductInfo}, // 添加其他数据以进行测试 } for _, v := range channelWithProductCode { err := processOrders(ctx, v) if err != nil { handleError(err) } } } func processOrders(ctx context.Context, v consolidate.Error) error { // 这里是模拟处理订单的逻辑 return &v } func handleError(err error) { l := zap.NewExample() var customErr *consolidate.Error if stderrors.As(err, &customErr) { switch customErr.Type { case consolidate.ErrorTypeFetchProductInfo: sendDingTalkAlert(customErr) l.Error("Fetch Product Info failed", zap.Error(customErr)) default: l.Error("Error occurred", zap.Error(customErr)) } } else { log.Println("Unhandled error:", err) } } func sendDingTalkAlert(err *consolidate.Error) { // 发送钉钉告警的实现细节 fmt.Println("Sending alert to DingTalk for error:", err) } ``` ### 代码优化点: 1. **移动 `continue` 语句**: - 我从循环中移除了 `continue` 语句。现在,错误处理逻辑在 `handleError` 函数内部进行,这样错误处理完后会自动继续循环,无需显式使用 `continue`。 2. **错误处理的集中管理**: - 错误处理逻辑被集中到 `handleError` 函数中,这使得错误处理更加模块化,也便于未来的扩展和维护。 3. **使用 `error.As` 进行类型断言**: - `error.As` 用于检查错误是否为 `consolidate.Error` 类型,并据此进行具体的错误处理。 4. **清晰的错误处理流程**: - 根据错误的类型,分别处理。例如,当错误类型为 `ErrorTypeFetchProductInfo` 时,发送钉钉告警并记录具体的日志。对于不是特定类型的错误,会打印一个未处理错误的日志。 通过这些调整,代码不仅更加简洁和易于理解,而且提高了错误处理的效率和可靠性。 ## 完善我error.go的代码,请使用中文沟通: ```go package consolidate import ( stderrors "errors" "fmt" ) type ErrorType int const ( ErrorTypeFetchProductInfo ErrorType = 1 ) type Error struct { Err error Msg string ProductCode string ProductSymbol string Channel string Type ErrorType } func(e Error) Error() string { return fmt.Sprintf("consolidate orders failed becouse: %v, product code: %v, channel: %v; innerErr:%v", e.Msg, e.ProductCode, e.Channel, e.Err) } var ( ErrFetchProductInfo = stderrors.New("error fetching product info") ErrFetchProductHolidayCalendar = stderrors.New("error fetching product holiday calendar") ErrConsolidate = stderrors.New("error consolidate") ) ``` 为了完善你的 `error.go` 代码,我会添加一些功能来增强错误处理的灵活性和信息的丰富性。我们可以考虑加入一些用于创建和管理错误的辅助函数,这样可以更便捷地生成和处理具有详细上下文信息的错误。 ### 1. 错误创建函数 添加函数来创建 `Error` 结构体实例。这样做可以在创建错误时自动填充必要的字段,使得错误信息更加标准化和一致。 ### 2. 错误检查和构造 为 `Error` 类型添加方法来比较或检查错误类型,这样可以更方便地在业务逻辑中判断错误类型。 ### 完善后的代码 ```go package consolidate import ( "fmt" stderrors "errors" ) // ErrorType 定义了错误类型的枚举。 type ErrorType int const ( ErrorTypeFetchProductInfo ErrorType = iota + 1 ErrorTypeFetchProductHolidayCalendar ErrorTypeConsolidate ) // Error 结构体定义了一个具有详细上下文的错误。 type Error struct { Err error Msg string ProductCode string ProductSymbol string Channel string Type ErrorType } // Error 方法实现了 error 接口,返回错误的详细描述。 func (e *Error) Error() string { return fmt.Sprintf("consolidate orders failed because: %s, product code: %s, channel: %s; inner error: %v", e.Msg, e.ProductCode, e.Channel, e.Err) } // Is 类型检查,帮助判断错误类型。 func (e *Error) Is(target error) bool { t, ok := target.(*Error) if !ok { return false } return e.Type == t.Type } // NewError 创建一个新的 Error 实例。 func NewError(err error, msg, productCode, productSymbol, channel string, errType ErrorType) *Error { return &Error{ Err: err, Msg: msg, ProductCode: productCode, ProductSymbol: productSymbol, Channel: channel, Type: errType, } } // 预定义一些常见的错误。 var ( ErrFetchProductInfo = stderrors.New("error fetching product info") ErrFetchProductHolidayCalendar = stderrors.New("error fetching product holiday calendar") ErrConsolidate = stderrors.New("error consolidating orders") ) ``` ### 说明 - **新增函数 `NewError`**:用于构造一个新的 `Error` 对象,这使得创建错误时可以统一格式,并附带更多上下文信息。 - **错误类型枚举扩展**:使用 `iota` 关键字扩展错误类型的定义,使得添加新的错误类型更为方便。 - **`Is` 方法**:实现了自定义的错误比较逻辑,这可以用在如 `errors.Is` 函数中,提供更准确的错误类型检测。 通过这些改进,你的错误处理代码将更加健壮,同时也提供了更丰富的信息用于调试和错误追踪。 ## 把这里都改成error.go里面返回的error ```go func(s * Server) ConsolidateOrdersServer(ctx context.Context, productCode, channel, symbol string, dir int) error { l: = log.FromContext(ctx) nowTime: = carbon.Now() // 1. 查基金节假日 fundCalendar, _, err: = s.isFundHoliday(ctx, symbol, nowTime) if err != nil { l.Error("search get fund holidays failed", zap.Error(err)) return err } // 2. 查询产品信息 productBasicInfo, err: = s.mktWealthClient.GetProductBasicInfo(ctx, mktmapping.ProductBasicInfoParams { ProductCode: productCode }) if err != nil { l.Error("search product basic info failed", zap.Error(err)) return err } settlementDay: = productBasicInfo.GetSettlementDayByDirection(dir) // 3. 查询预估结算日 expectSettledDate, err: = s.getExpectSettledDateV2(ctx, nowTime.ToStdTime(), symbol, settlementDay, fundCalendar) if err != nil { log.Errorf("get expectSettledDate failed", zap.Error(err)) return err } // 2. 查指定 productCode 和 channel 的订单 orders, err: = s.getCanConsolidateOrders(ctx, productCode, channel) // 当前product,channel和symbol对应的订单 if err != nil { l.Error("search can consolidate orders failed", zap.Error(err)) return err } // 3. 对这支基金得所有订单合单 consolidateOrders, err: = s.processOrders(ctx, orders, fundCalendar, nowTime, expectSettledDate) if err != nil { l.Error("processOrders failed", zap.Error(err)) return err } for _, consolidateOrder: = range consolidateOrders { err: = s.processAndSaveNomineeOrders(ctx, consolidateOrder) if err != nil { l.Warn("Some collection orders failed to be written to the database today", zap.Error(err)) // 报警发生错误的集合订单产品名称和集合订单下面所有的子订单ID s.sendDingTalkAlert(ctx, consolidateOrder.NomineeOrderInfo.ProductName, consolidateOrder.Orders) continue } } return nil } ``` 为了在你的 `ConsolidateOrdersServer` 方法中整合使用你定义的 `error.go` 里的 `Error` 类型,我们需要将这些错误适当地封装进你的自定义错误类型,并返回这些错误。这将使错误信息更加详细和结构化,有助于错误追踪和处理。 我会逐步修改你的函数,将标准错误转换为 `consolidate.Error` 类型的错误,并返回。下面是调整后的代码: ```go func (s *Server) ConsolidateOrdersServer(ctx context.Context, productCode, channel, symbol string, dir int) error { l := log.FromContext(ctx) nowTime := carbon.Now() // 1. 查基金节假日 fundCalendar, _, err := s.isFundHoliday(ctx, symbol, nowTime) if err != nil { l.Error("search get fund holidays failed", zap.Error(err)) return consolidate.NewError(err, "failed to get fund holidays", productCode, symbol, channel, consolidate.ErrorTypeFetchProductHolidayCalendar) } // 2. 查询产品信息 productBasicInfo, err := s.mktWealthClient.GetProductBasicInfo(ctx, mktmapping.ProductBasicInfoParams{ProductCode: productCode}) if err != nil { l.Error("search product basic info failed", zap.Error(err)) return consolidate.NewError(err, "failed to fetch product basic info", productCode, symbol, channel, consolidate.ErrorTypeFetchProductInfo) } settlementDay := productBasicInfo.GetSettlementDayByDirection(dir) // 3. 查询预估结算日 expectSettledDate, err := s.getExpectSettledDateV2(ctx, nowTime.ToStdTime(), symbol, settlementDay, fundCalendar) if err != nil { log.Errorf("get expectSettledDate failed", zap.Error(err)) return consolidate.NewError(err, "failed to calculate expected settlement date", productCode, symbol, channel, consolidate.ErrorTypeConsolidate) } // 2. 查指定 productCode 和 channel 的订单 orders, err := s.getCanConsolidateOrders(ctx, productCode, channel) // 当前product,channel和symbol对应的订单 if err != nil { l.Error("search can consolidate orders failed", zap.Error(err)) return consolidate.NewError(err, "failed to retrieve orders for consolidation", productCode, symbol, channel, consolidate.ErrorTypeConsolidate) } // 3. 对这支基金的所有订单合单 consolidateOrders, err := s.processOrders(ctx, orders, fundCalendar, nowTime, expectSettledDate) if err != nil { l.Error("processOrders failed", zap.Error(err)) return consolidate.NewError(err, "failed to process orders", productCode, symbol, channel, consolidate.ErrorTypeConsolidate) } for _, consolidateOrder := range consolidateOrders { err := s.processAndSaveNomineeOrders(ctx, consolidateOrder) if err != nil { l.Warn("Some collection orders failed to be written to the database today", zap.Error(err)) // 报警发生错误的集合订单产品名称和集合订单下面所有的子订单ID s.sendDingTalkAlert(ctx, consolidateOrder.NomineeOrderInfo.ProductName, consolidateOrder.Orders) continue } } return nil } ``` ### 代码调整解释: - **错误创建**:使用 `consolidate.NewError` 函数来创建一个 `Error` 类型的实例,这将包含了原始错误、错误信息、产品代码、产品标识、频道和错误类型。 - **错误类型**:为每个错误分配了一个特定的错误类型,这有助于后续在错误处理中进行更精细的控制和策略实施。 - **日志和错误处理**:在生成日志的同时,将错误封装并返回自定义的错误类型,确保上层调用者能够接收到结构化的错误信息。 这种处理方式不仅提高了错误的可读性和可追踪性,还使得系统的错误处理更加统一和规范化。 ## 我的error.go里面没有NewError函数 如果你的 `error.go` 文件中没有 `NewError` 函数,那么我们可以在你的 `Server` 类的实现中直接创建 `consolidate.Error` 实例。在这种情况下,我们需要在 `ConsolidateOrdersServer` 方法中手动构造 `consolidate.Error` 对象。 下面是修正后的 `ConsolidateOrdersServer` 方法,其中直接创建 `consolidate.Error` 实例: ```go func (s *Server) ConsolidateOrdersServer(ctx context.Context, productCode, channel, symbol string, dir int) error { l := log.FromContext(ctx) nowTime := carbon.Now() // 1. 查基金节假日 fundCalendar, _, err := s.isFundHoliday(ctx, symbol, nowTime) if err != nil { l.Error("search get fund holidays failed", zap.Error(err)) return &consolidate.Error{ Err: err, Msg: "failed to get fund holidays", ProductCode: productCode, ProductSymbol: symbol, Channel: channel, Type: consolidate.ErrorTypeFetchProductHolidayCalendar, } } // 2. 查询产品信息 productBasicInfo, err := s.mktWealthClient.GetProductBasicInfo(ctx, mktmapping.ProductBasicInfoParams{ProductCode: productCode}) if err != nil { l.Error("search product basic info failed", zap.Error(err)) return &consolidate.Error{ Err: err, Msg: "failed to fetch product basic info", ProductCode: productCode, ProductSymbol: symbol, Channel: channel, Type: consolidate.ErrorTypeFetchProductInfo, } } settlementDay := productBasicInfo.GetSettlementDayByDirection(dir) // 3. 查询预估结算日 expectSettledDate, err := s.getExpectSettledDateV2(ctx, nowTime.ToStdTime(), symbol, settlementDay, fundCalendar) if err != nil { l.Error("get expectSettledDate failed", zap.Error(err)) return &consolidate.Error{ Err: err, Msg: "failed to calculate expected settlement date", ProductCode: productCode, ProductSymbol: symbol, Channel: channel, Type: consolidate.ErrorTypeConsolidate, } } // 4. 查指定 productCode 和 channel 的订单 orders, err := s.getCanConsolidateOrders(ctx, productCode, channel) // 当前product,channel和symbol对应的订单 if err != nil { l.Error("search can consolidate orders failed", zap.Error(err)) return &consolidate.Error{ Err: err, Msg: "failed to retrieve orders for consolidation", ProductCode: productCode, ProductSymbol: symbol, Channel: channel, Type: consolidate.ErrorTypeConsolidate, } } // 5. 对这支基金的所有订单合单 consolidateOrders, err := s.processOrders(ctx, orders, fundCalendar, nowTime, expectSettledDate) if err != nil { l.Error("processOrders failed", zap.Error(err)) return &consolidate.Error{ Err: err, Msg: "failed to process orders", ProductCode: productCode, ProductSymbol: symbol, Channel: channel, Type: consolidate.ErrorTypeConsolidate, } } for _, consolidateOrder := range consolidateOrders { err := s.processAndSaveNomineeOrders(ctx, consolidateOrder) if err != nil { l.Warn("Some collection orders failed to be written to the database today", zap.Error(err)) // 报警发生错误的集合订单产品名称和集合订单下面所有的子订单ID s.sendDingTalkAlert(ctx, consolidateOrder.NomineeOrderInfo.ProductName, consolidateOrder.Orders) continue } } return nil } ``` ### 代码解释 - **错误创建**:在每个可能返回错误的地方,直接创建并返回一个 `consolidate.Error` 实例。 - **错误信息**:每个 `consolidate.Error` 实例都包含了原始错误 (`Err` 字段)、自定义的错误消息 (`Msg` 字段)、产品代码 (`ProductCode` 字段)、产品符号 (`ProductSymbol` 字段)、频道 (`Channel` 字段) 和错误类型 (`Type` 字段)。 这种方式确保了在发生错误时,返回的错误对象不仅包含了原始错误信息,还带有丰富的上下文信息,有助于更好地进行错误处理和日志记录。 不需要这么复杂,我只需要在下面这段代码里面使用error.go里面的error方法包装error返回。最后在外层使用error.as方法对错误进行解码 ```go type Server struct { commandArgs []string config *viper.Viper dingTalkRestyClient dingtalk.Client mktDataClient mktdata.Client mktWealthClient mktwealth.Client err Error } // todo 这里还没处理完 func (s *Server) ConsolidateOrdersServer(ctx context.Context, productCode, channel, symbol string, dir int) error { l := log.FromContext(ctx) nowTime := carbon.Now() // 1. 查基金节假日 fundCalendar, err := s.isFundHoliday(ctx, symbol, nowTime) if err != nil { l.Error("search get fund holidays failed", zap.Error(err)) return s.wrapError(ErrFetchProductHolidayCalendar, "search get fund holidays failed", productCode, channel, symbol, err, ErrorTypeFetchProductHolidayCalendar) } // 2. 查询产品信息 productBasicInfo, err := s.mktWealthClient.GetProductBasicInfo(ctx, mktmapping.ProductBasicInfoParams{ProductCode: productCode}) if err != nil { l.Error("search product basic info failed", zap.Error(err)) return err } settlementDay := productBasicInfo.GetSettlementDayByDirection(dir) // 3. 查询预估结算日 expectSettledDate, err := s.getExpectSettledDateV2(ctx, nowTime.ToStdTime(), symbol, settlementDay, fundCalendar) if err != nil { log.Errorf("get expectSettledDate failed", zap.Error(err)) return err } // 4. 查指定 productCode 和 channel 的订单 orders, err := s.getCanConsolidateOrders(ctx, productCode, channel) // 当前product,channel和symbol对应的订单 if err != nil { l.Error("search can consolidate orders failed", zap.Error(err)) return err } // 5. 对这支基金得所有订单合单 consolidateOrders, err := s.processOrders(ctx, orders, fundCalendar, nowTime, expectSettledDate) if err != nil { l.Error("processOrders failed", zap.Error(err)) return err } for _, consolidateOrder := range consolidateOrders { err := s.processAndSaveNomineeOrders(ctx, consolidateOrder) if err != nil { l.Warn("Some collection orders failed to be written to the database today", zap.Error(err)) // 报警发生错误的集合订单产品名称和集合订单下面所有的子订单ID s.sendDingTalkAlert(ctx, consolidateOrder.NomineeOrderInfo.ProductName, consolidateOrder.Orders) continue } } return nil } ``` 了解了你的需求后,我们只需要在 `Server` 结构体中添加一个方法来封装错误,并在 `ConsolidateOrdersServer` 方法中使用它。然后在外层使用 `error.As` 方法对错误进行解码。 首先,定义一个 `wrapError` 方法来封装错误: ```go func (s *Server) wrapError(baseErr error, msg, productCode, channel, symbol string, innerErr error, errType ErrorType) error { return &Error{ Err: innerErr, Msg: msg, ProductCode: productCode, ProductSymbol: symbol, Channel: channel, Type: errType, } } ``` 然后,在 `ConsolidateOrdersServer` 方法中使用 `wrapError` 方法来包装错误。最后,外层可以使用 `error.As` 方法进行解码。以下是修改后的 `ConsolidateOrdersServer` 方法: ```go func (s *Server) ConsolidateOrdersServer(ctx context.Context, productCode, channel, symbol string, dir int) error { l := log.FromContext(ctx) nowTime := carbon.Now() // 1. 查基金节假日 fundCalendar, err := s.isFundHoliday(ctx, symbol, nowTime) if err != nil { l.Error("search get fund holidays failed", zap.Error(err)) return s.wrapError(ErrFetchProductHolidayCalendar, "search get fund holidays failed", productCode, channel, symbol, err, ErrorTypeFetchProductHolidayCalendar) } // 2. 查询产品信息 productBasicInfo, err := s.mktWealthClient.GetProductBasicInfo(ctx, mktmapping.ProductBasicInfoParams{ProductCode: productCode}) if err != nil { l.Error("search product basic info failed", zap.Error(err)) return s.wrapError(ErrFetchProductInfo, "search product basic info failed", productCode, channel, symbol, err, ErrorTypeFetchProductInfo) } settlementDay := productBasicInfo.GetSettlementDayByDirection(dir) // 3. 查询预估结算日 expectSettledDate, err := s.getExpectSettledDateV2(ctx, nowTime.ToStdTime(), symbol, settlementDay, fundCalendar) if err != nil { l.Error("get expectSettledDate failed", zap.Error(err)) return s.wrapError(ErrConsolidate, "get expectSettledDate failed", productCode, channel, symbol, err, ErrorTypeConsolidate) } // 4. 查指定 productCode 和 channel 的订单 orders, err := s.getCanConsolidateOrders(ctx, productCode, channel) // 当前product,channel和symbol对应的订单 if err != nil { l.Error("search can consolidate orders failed", zap.Error(err)) return s.wrapError(ErrConsolidate, "search can consolidate orders failed", productCode, channel, symbol, err, ErrorTypeConsolidate) } // 5. 对这支基金的所有订单合单 consolidateOrders, err := s.processOrders(ctx, orders, fundCalendar, nowTime, expectSettledDate) if err != nil { l.Error("processOrders failed", zap.Error(err)) return s.wrapError(ErrConsolidate, "processOrders failed", productCode, channel, symbol, err, ErrorTypeConsolidate) } for _, consolidateOrder := range consolidateOrders { err := s.processAndSaveNomineeOrders(ctx, consolidateOrder) if err != nil { l.Warn("Some collection orders failed to be written to the database today", zap.Error(err)) // 报警发生错误的集合订单产品名称和集合订单下面所有的子订单ID s.sendDingTalkAlert(ctx, consolidateOrder.NomineeOrderInfo.ProductName, consolidateOrder.Orders) continue } } return nil } ``` 最后,在调用 `ConsolidateOrdersServer` 方法的外层处理错误时,可以使用 `error.As` 方法来解码错误: ```go func main() { // 示例调用 server := &Server{} err := server.ConsolidateOrdersServer(ctx, "productCode", "channel", "symbol", 1) if err != nil { var consolidateErr *Error if errors.As(err, &consolidateErr) { // 根据错误类型进行处理 fmt.Printf("Error: %+v\n", consolidateErr) } else { fmt.Printf("Unknown error: %v\n", err) } } } ``` 通过这些修改,你可以在 `ConsolidateOrdersServer` 方法中使用 `error.go` 里的 `Error` 类型来包装错误,并在外层通过 `error.As` 方法来解析和处理这些错误。 最后修改:2024 年 07 月 13 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏