mirror of https://gitee.com/godoos/godoos.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
277 lines
8.0 KiB
277 lines
8.0 KiB
package dingtalk
|
|
|
|
import (
|
|
"fmt"
|
|
"godocms/common"
|
|
"godocms/pkg/dingtalk/payload"
|
|
"log/slog"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// GetDriveSpaces 获取空间列表
|
|
func (ding *DingTalk) GetDriveSpaces(unionId string, spaceType payload.SpaceType, token string,
|
|
size int) (rsp payload.GetDriveSpaces, err error,
|
|
) {
|
|
query := url.Values{}
|
|
query.Set("spaceType", string(spaceType))
|
|
query.Set("unionId", unionId)
|
|
query.Set("nextToken", token)
|
|
query.Set("maxResults", strconv.Itoa(size))
|
|
|
|
return rsp, ding.Request(http.MethodGet, GetDriveSpacesKey, query, nil, &rsp)
|
|
}
|
|
|
|
// GetStorageSapceFiles 获取空间文件列表
|
|
func (ding *DingTalk) GetStorageSapceFiles(req *payload.GetStorageSpacesFilesReq) (rsp payload.FileDentriesResponse, err error) {
|
|
query := url.Values{}
|
|
query.Set("parentId", req.ParentId)
|
|
query.Set("nextToken", req.NextToken)
|
|
query.Set("maxResults", strconv.Itoa(req.Size))
|
|
query.Set("orderBy", req.OrderBy)
|
|
query.Set("order", string(req.Order))
|
|
query.Set("withThumbnail", strconv.FormatBool(req.WithThumbnail))
|
|
query.Set("unionId", req.UnionId)
|
|
|
|
return rsp, ding.Request(http.MethodGet, fmt.Sprintf(GetStorageSpacesFilesKey, req.SpaceId), query, nil, &rsp)
|
|
}
|
|
|
|
// GetStorageFileInfo 获取文件信息
|
|
func (ding *DingTalk) GetStorageFileInfo(spaceId, dentryId, unionId string) (rsp payload.GetStorageFileInfoResponse, err error) {
|
|
query := url.Values{}
|
|
query.Set("unionId", unionId)
|
|
|
|
return rsp, ding.Request(http.MethodPost, fmt.Sprintf(GetStorageSpacesFileInfoKey, spaceId, dentryId), query, nil, &rsp)
|
|
}
|
|
|
|
// GetStorageSpacesFileDownloadInfo 获取文件下载信息
|
|
func (ding *DingTalk) GetStorageSpacesFileDownloadInfo(spaceId, fileId, unionId string) (rsp payload.GetStorageSpacesFileDownloadInfo, err error) {
|
|
query := url.Values{}
|
|
query.Set("unionId", unionId)
|
|
return rsp, ding.Request(http.MethodPost, fmt.Sprintf(GetStorageSpacesFileDownloadInfoKey, spaceId, fileId),
|
|
query, nil, &rsp)
|
|
}
|
|
|
|
type FileNode struct {
|
|
ID string `json:"id"`
|
|
SpaceID string `json:"space_id"`
|
|
ParentID string `json:"parent_id"`
|
|
Type string `json:"type"`
|
|
Name string `json:"name"`
|
|
Size int64 `json:"size"`
|
|
Path string `json:"path"`
|
|
Status string `json:"status"` // NORMAL DELETED EXPIRED
|
|
Children []*FileNode `json:"children,omitempty"` // 子节点
|
|
}
|
|
|
|
// 获取用户钉盘文件列表
|
|
func (ding *DingTalk) GetUserDingSpace(unionid string) ([]*FileNode, error) {
|
|
var nextToken string
|
|
var fileTree []*FileNode
|
|
|
|
for {
|
|
rsp, err := ding.GetDriveSpaces(unionid, payload.Org, nextToken, 10)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, space := range rsp.Spaces {
|
|
fmt.Printf("space: %+v\n", space)
|
|
rootNode := &FileNode{
|
|
ID: "0", // 假设根目录的 ParentId 为 "0"
|
|
SpaceID: space.SpaceId,
|
|
ParentID: "0",
|
|
Type: payload.DentryFolder,
|
|
Name: space.Name, // 根目录的名称
|
|
}
|
|
|
|
if err := ding.buildFileTree(unionid, space.SpaceId, "0", rootNode); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
fileTree = append(fileTree, rootNode)
|
|
}
|
|
|
|
nextToken = rsp.Token
|
|
if nextToken == "" {
|
|
break
|
|
}
|
|
}
|
|
|
|
return fileTree, nil
|
|
}
|
|
|
|
func (ding *DingTalk) buildFileTree(unionid, spaceID, parentID string, parentNode *FileNode) error {
|
|
var nextToken string
|
|
for {
|
|
files, err := ding.GetStorageSapceFiles(payload.NewGetStorageSpacesFilesReq(spaceID, unionid, 10,
|
|
payload.WithStorageFilesParentId(parentID), payload.WithStorageFilesNextToken(nextToken)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, v := range files.Dentries {
|
|
// 创建新的文件或文件夹节点
|
|
node := &FileNode{
|
|
ID: v.ID,
|
|
SpaceID: v.SpaceId,
|
|
ParentID: v.ParentId,
|
|
Type: v.Type,
|
|
Name: v.Name,
|
|
}
|
|
|
|
// 如果是文件夹,递归构建文件树
|
|
if v.Type == payload.DentryFolder {
|
|
if err := ding.buildFileTree(unionid, spaceID, v.ID, node); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// 将当前节点添加到父节点的子节点列表
|
|
parentNode.Children = append(parentNode.Children, node)
|
|
}
|
|
|
|
nextToken = files.Token
|
|
if nextToken == "" {
|
|
break
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type SpaceChannel struct {
|
|
node *FileNode
|
|
unionid string
|
|
path []string
|
|
}
|
|
|
|
func (ding *DingTalk) JoinDingSpaceFileDwonloadTask(unionid string, files []*FileNode) error {
|
|
signal := make(chan *SpaceChannel)
|
|
fileLog := make(chan *WriteFileLog)
|
|
done := make(chan struct{}) // 用于通知文件处理完毕
|
|
fileCache := make([]*WriteFileLog, 0)
|
|
|
|
go ding.listenToChannel(signal, fileLog)
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(1)
|
|
|
|
// 处理文件下载
|
|
go func() {
|
|
defer wg.Done()
|
|
dfs(unionid, files, signal, []string{})
|
|
signal <- nil // 发送 EOF 信号
|
|
close(signal)
|
|
close(fileLog)
|
|
}()
|
|
|
|
// 处理下载日志文件
|
|
go func() {
|
|
defer close(done) // 完成时通知
|
|
for msg := range fileLog {
|
|
if msg == nil {
|
|
fmt.Println("-------------------------日志读取结束--------------------------")
|
|
return
|
|
}
|
|
slog.Info("Write file log", "file", msg.FileName, "path", msg.Path, "remote", msg.Remote, "time", msg.Time)
|
|
fileCache = append(fileCache, msg)
|
|
}
|
|
}()
|
|
|
|
// 等待所有工作完成
|
|
wg.Wait()
|
|
<-done // 等待 fileLog goroutine 完成
|
|
|
|
// 写入缓存
|
|
common.Cache.Delete(fmt.Sprintf("ding_space_%s", unionid))
|
|
common.Cache.Set(fmt.Sprintf("ding_space_%s", unionid), fileCache, 24*60)
|
|
fmt.Println("-------------------------日志文件写入完成--------------------------")
|
|
|
|
return nil
|
|
}
|
|
|
|
func dfs(unionid string, nodes []*FileNode, ch chan *SpaceChannel, path []string) {
|
|
for _, node := range nodes {
|
|
if node == nil {
|
|
continue
|
|
}
|
|
// 将当前节点的 ID 添加到路径中
|
|
newPath := append([]string(nil), path...) // 创建一个新切片来避免修改原路径
|
|
newPath = append(newPath, node.Name)
|
|
fmt.Println("parent path: ", newPath, "this node: ", node.ID, " name: ", node.Name)
|
|
// 发送当前节点到 channel
|
|
ch <- &SpaceChannel{node: node, unionid: unionid, path: newPath}
|
|
|
|
// 如果有子节点,递归遍历子节点
|
|
if len(node.Children) > 0 {
|
|
dfs(unionid, node.Children, ch, newPath)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 监听 channel,处理节点
|
|
func (ding *DingTalk) listenToChannel(ch chan *SpaceChannel, fileLog chan *WriteFileLog) {
|
|
for signal := range ch {
|
|
if signal == nil {
|
|
// 处理 EOF 信号
|
|
slog.Info("EOF signal received")
|
|
break
|
|
}
|
|
if signal.node.Type == payload.DentryFolder {
|
|
folderPath := filepath.Join(signal.path...)
|
|
// 新建文件夹
|
|
realFolderPath := filepath.Join("data", "upload", "dingspace", folderPath)
|
|
err := os.MkdirAll(realFolderPath, 0755)
|
|
if err != nil {
|
|
fmt.Println("Error creating directory:", err)
|
|
}
|
|
slog.Info("New folder", slog.Any("folderPath", realFolderPath))
|
|
}
|
|
if signal.node.Type == payload.DentryFile {
|
|
// 获取文件信息,下载文件
|
|
filePath := filepath.Join(signal.path...)
|
|
realFilePath := filepath.Join("data", "upload", "dingspace", filePath)
|
|
resp, err := ding.GetStorageSpacesFileDownloadInfo(signal.node.SpaceID, signal.node.ID, signal.unionid)
|
|
if err != nil {
|
|
slog.Error("GetStorageFileInfo:", slog.Any("err", err))
|
|
}
|
|
headers := resp.HeaderSignatureInfo.Headers
|
|
|
|
for _, url := range resp.HeaderSignatureInfo.ResourceUrls {
|
|
wfl := &WriteFileLog{
|
|
Action: "download file info",
|
|
FileName: signal.node.Name,
|
|
Header: headers,
|
|
Path: realFilePath,
|
|
Remote: url,
|
|
Time: time.Now().Local().Format("2006-01-02 15:04:05"),
|
|
Unionid: signal.unionid,
|
|
SpaceID: signal.node.SpaceID,
|
|
FileID: signal.node.ID,
|
|
}
|
|
fileLog <- wfl
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 打印文件树结构
|
|
func printFileTree(fileTree []*FileNode, level int) {
|
|
for _, node := range fileTree {
|
|
// 打印当前节点信息
|
|
fmt.Printf("%s[%s] (ID: %s, SpaceID: %s, Type: %s)\n", getIndentation(level), node.Name, node.ID, node.SpaceID, node.Type)
|
|
// 递归打印子节点
|
|
printFileTree(node.Children, level+1)
|
|
}
|
|
}
|
|
|
|
// 获取缩进字符串,方便打印树形结构
|
|
func getIndentation(level int) string {
|
|
return strings.Repeat(" ", level*2) // 每级缩进 2 个空格
|
|
}
|
|
|