package usecase import ( "context" "github.com/zeromicro/go-zero/core/logx" ) /* charge 是一次計點呼叫的預留與結算,沿用既有四個 meter,不新增第五個(RG-04)。 用法:`defer charge.Settle(ctx, &err)` 綁在具名 error 回傳上,這樣任何失敗路徑都退點, 不會出現「AI 失敗了但點數扣掉」。 */ type charge struct { svc *Service uid int64 meter string mode string label string source string settled bool } func (s *Service) bill(ctx context.Context, uid int64, meter, label, source string) (*charge, error) { if s == nil || s.Usage == nil { return &charge{settled: true}, nil } mode, err := s.Usage.PrepareCall(ctx, uid, meter) if err != nil { return nil, err } return &charge{svc: s, uid: uid, meter: meter, mode: mode, label: label, source: source}, nil } func (c *charge) Settle(ctx context.Context, errp *error) { if errp != nil && *errp != nil { c.Release(ctx) return } c.Commit(ctx) } // Commit 寫入用量事件。使用者已經拿到結果,所以這裡失敗只記錄不轉成錯誤。 func (c *charge) Commit(ctx context.Context) { if c == nil || c.settled { return } c.settled = true if _, err := c.svc.Usage.RecordCall(ctx, c.uid, c.meter, c.mode, c.label, c.source); err != nil { logx.Errorf("usage record uid=%d meter=%s source=%s: %v", c.uid, c.meter, c.source, err) } } func (c *charge) Release(ctx context.Context) { if c == nil || c.settled { return } c.settled = true if err := c.svc.Usage.ReleaseCall(ctx, c.uid, c.meter, c.mode); err != nil { logx.Errorf("usage release uid=%d meter=%s source=%s: %v", c.uid, c.meter, c.source, err) } }