github.com/weplanx/server@v0.2.6-0.20240318110640-f7e75155779a/api/lark/service.go (about)

     1  package lark
     2  
     3  import (
     4  	"context"
     5  	"crypto/aes"
     6  	"crypto/cipher"
     7  	"crypto/sha256"
     8  	"encoding/base64"
     9  	"fmt"
    10  	"github.com/bytedance/sonic"
    11  	"github.com/cloudwego/hertz/pkg/common/errors"
    12  	"github.com/imroc/req/v3"
    13  	"github.com/weplanx/go/locker"
    14  	"github.com/weplanx/go/passport"
    15  	"github.com/weplanx/go/sessions"
    16  	"github.com/weplanx/server/api/index"
    17  	"github.com/weplanx/server/common"
    18  	"github.com/weplanx/server/model"
    19  	"go.mongodb.org/mongo-driver/bson"
    20  	"go.mongodb.org/mongo-driver/bson/primitive"
    21  	"go.mongodb.org/mongo-driver/mongo"
    22  	"strings"
    23  	"time"
    24  )
    25  
    26  type Service struct {
    27  	*common.Inject
    28  	Sessions *sessions.Service
    29  	Locker   *locker.Locker
    30  	Passport *passport.Passport
    31  	IndexX   *index.Service
    32  }
    33  
    34  var client = req.C().
    35  	SetBaseURL("https://open.feishu.cn/open-apis").
    36  	SetJsonMarshal(sonic.Marshal).
    37  	SetJsonUnmarshal(sonic.Unmarshal).
    38  	SetTimeout(time.Second * 5)
    39  
    40  func (x *Service) Decrypt(encrypt string, key string) (string, error) {
    41  	buf, err := base64.StdEncoding.DecodeString(encrypt)
    42  	if err != nil {
    43  		return "", fmt.Errorf("base64StdEncode Error[%v]", err)
    44  	}
    45  	if len(buf) < aes.BlockSize {
    46  		return "", errors.NewPublic("cipher  too short")
    47  	}
    48  	keyBs := sha256.Sum256([]byte(key))
    49  	block, err := aes.NewCipher(keyBs[:sha256.Size])
    50  	if err != nil {
    51  		return "", fmt.Errorf("AESNewCipher Error[%v]", err)
    52  	}
    53  	iv := buf[:aes.BlockSize]
    54  	buf = buf[aes.BlockSize:]
    55  	if len(buf)%aes.BlockSize != 0 {
    56  		return "", errors.NewPublic("ciphertext is not a multiple of the block size")
    57  	}
    58  	mode := cipher.NewCBCDecrypter(block, iv)
    59  	mode.CryptBlocks(buf, buf)
    60  	n := strings.Index(string(buf), "{")
    61  	if n == -1 {
    62  		n = 0
    63  	}
    64  	m := strings.LastIndex(string(buf), "}")
    65  	if m == -1 {
    66  		m = len(buf) - 1
    67  	}
    68  	return string(buf[n : m+1]), nil
    69  }
    70  
    71  func (x *Service) GetTenantAccessToken(ctx context.Context) (token string, err error) {
    72  	key := fmt.Sprintf(`%s:%s`, "lark", "tenant_access_token")
    73  	var exists int64
    74  	if exists, err = x.RDb.Exists(ctx, key).Result(); err != nil {
    75  		return
    76  	}
    77  	if exists == 0 {
    78  		var result struct {
    79  			Code              uint64 `json:"code"`
    80  			Msg               string `json:"msg"`
    81  			TenantAccessToken string `json:"tenant_access_token"`
    82  			Expire            int64  `json:"expire"`
    83  		}
    84  		if _, err = client.R().
    85  			SetContext(ctx).
    86  			SetBody(M{
    87  				"app_id":     x.V.LarkAppId,
    88  				"app_secret": x.V.LarkAppSecret,
    89  			}).
    90  			SetSuccessResult(&result).
    91  			Post("/auth/v3/tenant_access_token/internal"); err != nil {
    92  			return
    93  		}
    94  		if err = x.RDb.Set(ctx, key,
    95  			result.TenantAccessToken,
    96  			time.Second*time.Duration(result.Expire)).Err(); err != nil {
    97  			return
    98  		}
    99  	}
   100  	return x.RDb.Get(ctx, key).Result()
   101  }
   102  
   103  func (x *Service) GetUserAccessToken(ctx context.Context, code string) (_ model.UserLark, err error) {
   104  	var token string
   105  	if token, err = x.GetTenantAccessToken(ctx); err != nil {
   106  		return
   107  	}
   108  	var result struct {
   109  		Code uint64         `json:"code"`
   110  		Msg  string         `json:"msg"`
   111  		Data model.UserLark `json:"data"`
   112  	}
   113  	if _, err = client.R().
   114  		SetContext(ctx).
   115  		SetBearerAuthToken(token).
   116  		SetBody(map[string]interface{}{
   117  			"grant_type": "authorization_code",
   118  			"code":       code,
   119  		}).
   120  		SetSuccessResult(&result).
   121  		Post("/authen/v1/access_token"); err != nil {
   122  		return
   123  	}
   124  	if result.Code != 0 {
   125  		err = errors.NewPublic(result.Msg)
   126  		return
   127  	}
   128  	return result.Data, nil
   129  }
   130  
   131  func (x *Service) Link(ctx context.Context, userId string, data model.UserLark) (_ *mongo.UpdateResult, err error) {
   132  	id, _ := primitive.ObjectIDFromHex(userId)
   133  	return x.Db.Collection("users").UpdateOne(ctx,
   134  		bson.M{"_id": id},
   135  		bson.M{"$set": bson.M{"lark": data}},
   136  	)
   137  }
   138  
   139  type LoginResult struct {
   140  	User        model.User
   141  	AccessToken string
   142  }
   143  
   144  func (x *Service) Login(ctx context.Context, openId string) (r *LoginResult, err error) {
   145  	r = new(LoginResult)
   146  	if r.User, err = x.IndexX.Logining(ctx, bson.M{"lark.open_id": openId, "status": true}); err != nil {
   147  		return
   148  	}
   149  
   150  	if err = x.Db.Collection("users").
   151  		FindOne(ctx, bson.M{"lark.open_id": openId, "status": true}).Decode(&r.User); err != nil {
   152  		if err == mongo.ErrNoDocuments {
   153  			err = common.ErrLoginNotExists
   154  			return
   155  		}
   156  		return
   157  	}
   158  	userId := r.User.ID.Hex()
   159  	if r.AccessToken, err = x.IndexX.CreateAccessToken(ctx, userId); err != nil {
   160  		return
   161  	}
   162  	return
   163  }
   164  
   165  func (x *Service) CreateTask(ctx context.Context) (result M, err error) {
   166  	var token string
   167  	if token, err = x.GetTenantAccessToken(ctx); err != nil {
   168  		return
   169  	}
   170  	body := `{}`
   171  	if _, err = client.R().
   172  		SetContext(ctx).
   173  		SetBearerAuthToken(token).
   174  		SetBody(body).
   175  		SetSuccessResult(&result).
   176  		Post("/task/v1/tasks"); err != nil {
   177  		return
   178  	}
   179  	return
   180  }
   181  
   182  func (x *Service) GetTasks(ctx context.Context) (result M, err error) {
   183  	var token string
   184  	if token, err = x.GetTenantAccessToken(ctx); err != nil {
   185  		return
   186  	}
   187  	if _, err = client.R().
   188  		SetContext(ctx).
   189  		SetBearerAuthToken(token).
   190  		SetSuccessResult(&result).
   191  		Get("/task/v1/tasks"); err != nil {
   192  		return
   193  	}
   194  	return
   195  }