Files
coze-studio/backend/domain/user/service/user_saas_impl.go
2025-10-10 22:26:08 +08:00

154 lines
5.1 KiB
Go

/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package service
import (
"context"
"encoding/json"
"github.com/coze-dev/coze-studio/backend/domain/user/entity"
"github.com/coze-dev/coze-studio/backend/pkg/errorx"
"github.com/coze-dev/coze-studio/backend/pkg/logs"
"github.com/coze-dev/coze-studio/backend/pkg/saasapi"
"github.com/coze-dev/coze-studio/backend/types/errno"
)
// CozeUserService provides user-related API operations
type CozeUserService struct {
client *saasapi.CozeAPIClient
}
// NewCozeUserService creates a new user service
func NewCozeUserService() *CozeUserService {
return &CozeUserService{
client: saasapi.NewCozeAPIClient(),
}
}
// GetUserInfo calls the /v1/users/me endpoint
func (s *CozeUserService) GetUserInfo(ctx context.Context) (*entity.SaasUserData, error) {
resp, err := s.client.Get(ctx, "/v1/users/me")
if err != nil {
logs.CtxErrorf(ctx, "failed to call GetUserInfo API: %v", err)
return nil, errorx.New(errno.ErrUserResourceNotFound, errorx.KV("reason", "API call failed"))
}
// Parse the data field
var userData entity.SaasUserData
if err := json.Unmarshal(resp.Data, &userData); err != nil {
logs.CtxErrorf(ctx, "failed to parse user data: %v", err)
return nil, errorx.New(errno.ErrUserResourceNotFound, errorx.KV("reason", "data parse failed"))
}
// Map to entity.SaasUserData
return &entity.SaasUserData{
UserID: userData.UserID,
UserName: userData.UserName,
NickName: userData.NickName,
AvatarURL: userData.AvatarURL,
}, nil
}
func (s *CozeUserService) GetEnterpriseBenefit(ctx context.Context, req *entity.GetEnterpriseBenefitRequest) (*entity.GetEnterpriseBenefitResponse, error) {
queryParams := make(map[string]interface{})
if req.BenefitType != nil {
queryParams["benefit_type"] = *req.BenefitType
}
if req.ResourceID != nil {
queryParams["resource_id"] = *req.ResourceID
}
resp, err := s.client.GetWithQuery(ctx, "/v1/commerce/benefit/benefits/get?benefit_type=call_tool_limit", queryParams)
if err != nil {
logs.CtxErrorf(ctx, "failed to call GetEnterpriseBenefit API: %v", err)
return nil, errorx.New(errno.ErrUserResourceNotFound, errorx.KV("reason", "API call failed"))
}
var benefitData entity.BenefitData
if err := json.Unmarshal(resp.Data, &benefitData); err != nil {
logs.CtxErrorf(ctx, "failed to parse benefit data: %v", err)
return nil, errorx.New(errno.ErrUserResourceNotFound, errorx.KV("reason", "data parse failed"))
}
// Validate parsed data
if benefitData.BasicInfo != nil && !benefitData.BasicInfo.UserLevel.IsValid() {
logs.CtxWarnf(ctx, "invalid user level: %s", benefitData.BasicInfo.UserLevel)
}
for _, benefitInfo := range benefitData.BenefitInfo {
if benefitInfo != nil && benefitInfo.Basic != nil && !benefitInfo.Basic.Status.IsValid() {
logs.CtxWarnf(ctx, "invalid benefit status: %s", benefitInfo.Basic.Status)
}
if benefitInfo != nil && benefitInfo.Basic != nil && benefitInfo.Basic.ItemInfo != nil && !benefitInfo.Basic.ItemInfo.Strategy.IsValid() {
logs.CtxWarnf(ctx, "invalid resource usage strategy: %s", benefitInfo.Basic.ItemInfo.Strategy)
}
}
benefit := &entity.GetEnterpriseBenefitResponse{
Code: int32(resp.Code),
Message: resp.Msg,
Data: &benefitData,
}
logs.CtxInfof(ctx, "successfully retrieved enterprise benefit data, user_level: %s, benefit_count: %d",
benefit.Data.BasicInfo.UserLevel, len(benefit.Data.BenefitInfo))
return benefit, nil
}
func (s *CozeUserService) GetUserBenefit(ctx context.Context) (*entity.UserBenefit, error) {
benefitType := entity.BenefitTypeCallToolLimit
req := &entity.GetEnterpriseBenefitRequest{
BenefitType: &benefitType,
}
benefit, err := s.GetEnterpriseBenefit(ctx, req)
if err != nil {
return nil, err
}
if benefit.Data == nil || len(benefit.Data.BenefitInfo) == 0 {
return nil, errorx.New(errno.ErrUserResourceNotFound, errorx.KV("reason", "benefit info not found"))
}
return &entity.UserBenefit{
UsedCount: int32(benefit.Data.BenefitInfo[0].Basic.ItemInfo.Used),
TotalCount: int32(benefit.Data.BenefitInfo[0].Basic.ItemInfo.Total),
IsUnlimited: func() bool {
return benefit.Data.BenefitInfo[0].Basic.ItemInfo.Strategy == entity.ResourceUsageStrategyUnlimit
}(),
}, nil
}
var cozeUserService *CozeUserService
func getCozeUserService() *CozeUserService {
if cozeUserService == nil {
cozeUserService = NewCozeUserService()
}
return cozeUserService
}
func (u *userImpl) GetSaasUserInfo(ctx context.Context) (*entity.SaasUserData, error) {
return getCozeUserService().GetUserInfo(ctx)
}
func (u *userImpl) GetUserBenefit(ctx context.Context) (*entity.UserBenefit, error) {
return getCozeUserService().GetUserBenefit(ctx)
}