update
This commit is contained in:
parent
d9ccbf13f7
commit
c30ce8f0dd
@ -13,7 +13,7 @@ import (
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/api/grpc/interceptor"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/api/grpc/v1/documents"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/api/http"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/api/http/v1"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/api/http/controller"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/base"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/base/conf"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/base/logger"
|
||||
@ -27,8 +27,11 @@ import (
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/router"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/auth"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/collection"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/document"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/jwks"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/stream"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/workspace"
|
||||
)
|
||||
|
||||
// Injectors from wire.go:
|
||||
@ -36,24 +39,29 @@ import (
|
||||
func CreateApp() (*base.Application, error) {
|
||||
config := conf.NewConfig()
|
||||
loggerLogger := logger.NewZapLogger(config)
|
||||
db := orm.NewGORM(config, loggerLogger)
|
||||
query := dao.NewQuery(db)
|
||||
service := workspace.NewService(config, query)
|
||||
jwksJWKS := jwks.NewJWKS(config, loggerLogger)
|
||||
service := auth.NewService(config, jwksJWKS, loggerLogger)
|
||||
userController := v1.NewUserController(service)
|
||||
handlers := http.NewHandler(userController)
|
||||
middleware := http.NewMiddleware(config, loggerLogger, service)
|
||||
authService := auth.NewService(config, jwksJWKS, loggerLogger)
|
||||
workspaceController := controller.NewWorkspaceController(service, authService)
|
||||
collectionService := collection.NewService(config, query)
|
||||
collectionController := controller.NewCollectionController(collectionService, service, authService)
|
||||
documentService := document.NewService(config, query)
|
||||
documentController := controller.NewDocumentController(documentService, collectionService, service, authService)
|
||||
handlers := http.NewHandler(workspaceController, collectionController, documentController)
|
||||
middleware := http.NewMiddleware(config, loggerLogger, authService)
|
||||
routerApi := router.NewApiRoute(handlers, middleware)
|
||||
swaggerRouter := router.NewSwaggerRoute()
|
||||
httpServer := server.NewHTTPServer(config, routerApi, swaggerRouter, middleware, loggerLogger)
|
||||
db := orm.NewGORM(config, loggerLogger)
|
||||
query := dao.NewQuery(db)
|
||||
handler := documents.NewHandler(query)
|
||||
interceptorAuth := interceptor.NewAuth(service, loggerLogger, config)
|
||||
interceptorAuth := interceptor.NewAuth(authService, loggerLogger, config)
|
||||
interceptorLogger := interceptor.NewLogger(loggerLogger)
|
||||
grpcInterceptor := grpc.NewInterceptor(interceptorAuth, interceptorLogger)
|
||||
grpcHandlers := grpc.NewHandler(handler, grpcInterceptor)
|
||||
apiApi := api.NewApi(grpcHandlers, handlers)
|
||||
streamService := stream.NewService(config)
|
||||
servicesService := services.NewService(loggerLogger, jwksJWKS, service, streamService)
|
||||
servicesService := services.NewService(loggerLogger, jwksJWKS, authService, streamService, service, collectionService, documentService)
|
||||
redisRedis := redis.NewRedis(config)
|
||||
batchBatch := batch.NewBatch(loggerLogger)
|
||||
s3S3 := s3.NewS3(config)
|
||||
|
1456
docs/docs.go
1456
docs/docs.go
File diff suppressed because it is too large
Load Diff
1456
docs/swagger.json
1456
docs/swagger.json
File diff suppressed because it is too large
Load Diff
@ -1,38 +1,218 @@
|
||||
definitions:
|
||||
response.Body:
|
||||
dto.AddWorkspaceMemberRequest:
|
||||
properties:
|
||||
data: {}
|
||||
error:
|
||||
user_id:
|
||||
$ref: '#/definitions/user.ID'
|
||||
required:
|
||||
- user_id
|
||||
type: object
|
||||
dto.CreateBlockRequest:
|
||||
properties:
|
||||
after_block_id:
|
||||
type: integer
|
||||
content:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- content
|
||||
- type
|
||||
type: object
|
||||
dto.CreateCollectionRequest:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
workspace_id:
|
||||
type: integer
|
||||
required:
|
||||
- workspace_id
|
||||
type: object
|
||||
dto.CreateDocumentRequest:
|
||||
properties:
|
||||
collection_id:
|
||||
type: integer
|
||||
name:
|
||||
type: string
|
||||
parent_id:
|
||||
type: integer
|
||||
workspace_id:
|
||||
type: integer
|
||||
required:
|
||||
- collection_id
|
||||
- workspace_id
|
||||
type: object
|
||||
dto.CreateWorkspaceRequest:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
type: object
|
||||
dto.DocumentDTO:
|
||||
properties:
|
||||
collection_id:
|
||||
type: integer
|
||||
created_at:
|
||||
type: string
|
||||
has_children:
|
||||
type: boolean
|
||||
id:
|
||||
type: integer
|
||||
name:
|
||||
type: string
|
||||
parent_id:
|
||||
type: integer
|
||||
updated_at:
|
||||
type: string
|
||||
workspace_id:
|
||||
type: integer
|
||||
type: object
|
||||
dto.MoveBlockRequest:
|
||||
properties:
|
||||
after_block_id:
|
||||
type: integer
|
||||
type: object
|
||||
dto.Response:
|
||||
type: object
|
||||
dto.UpdateBlockRequest:
|
||||
properties:
|
||||
content:
|
||||
type: string
|
||||
required:
|
||||
- content
|
||||
type: object
|
||||
dto.UpdateDocumentRequest:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
type: object
|
||||
dto.ValidateError:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
success:
|
||||
type: boolean
|
||||
type: object
|
||||
schema.CurrentUserResponse:
|
||||
entity.Document:
|
||||
properties:
|
||||
ip:
|
||||
collection:
|
||||
$ref: '#/definitions/leafdev_top_Leaf_leaf-library-3_internal_entity.Collection'
|
||||
collection_id:
|
||||
type: integer
|
||||
created_at:
|
||||
type: string
|
||||
userEmail:
|
||||
deleted_at:
|
||||
$ref: '#/definitions/gorm.DeletedAt'
|
||||
id:
|
||||
type: integer
|
||||
name:
|
||||
type: string
|
||||
userId:
|
||||
parent:
|
||||
$ref: '#/definitions/entity.Document'
|
||||
parent_id:
|
||||
type: integer
|
||||
updated_at:
|
||||
type: string
|
||||
userName:
|
||||
workspace:
|
||||
$ref: '#/definitions/entity.Workspace'
|
||||
workspace_id:
|
||||
type: integer
|
||||
type: object
|
||||
entity.DocumentBlock:
|
||||
properties:
|
||||
content:
|
||||
type: string
|
||||
created_at:
|
||||
type: string
|
||||
document:
|
||||
$ref: '#/definitions/entity.Document'
|
||||
document_id:
|
||||
type: integer
|
||||
hash:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
order:
|
||||
type: number
|
||||
type:
|
||||
type: string
|
||||
updated_at:
|
||||
type: string
|
||||
type: object
|
||||
entity.Workspace:
|
||||
properties:
|
||||
created_at:
|
||||
type: string
|
||||
deleted_at:
|
||||
$ref: '#/definitions/gorm.DeletedAt'
|
||||
id:
|
||||
type: integer
|
||||
name:
|
||||
type: string
|
||||
updated_at:
|
||||
type: string
|
||||
user_id:
|
||||
$ref: '#/definitions/user.ID'
|
||||
type: object
|
||||
entity.WorkspaceMember:
|
||||
properties:
|
||||
created_at:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
updated_at:
|
||||
type: string
|
||||
user_id:
|
||||
$ref: '#/definitions/user.ID'
|
||||
workspace:
|
||||
$ref: '#/definitions/entity.Workspace'
|
||||
workspace_id:
|
||||
type: integer
|
||||
type: object
|
||||
gorm.DeletedAt:
|
||||
properties:
|
||||
time:
|
||||
type: string
|
||||
valid:
|
||||
description: Valid is true if Time is not NULL
|
||||
type: boolean
|
||||
type: object
|
||||
leafdev_top_Leaf_leaf-library-3_internal_entity.Collection:
|
||||
properties:
|
||||
created_at:
|
||||
type: string
|
||||
deleted_at:
|
||||
$ref: '#/definitions/gorm.DeletedAt'
|
||||
id:
|
||||
type: integer
|
||||
name:
|
||||
type: string
|
||||
updated_at:
|
||||
type: string
|
||||
workspace:
|
||||
$ref: '#/definitions/entity.Workspace'
|
||||
workspace_id:
|
||||
type: integer
|
||||
type: object
|
||||
user.ID:
|
||||
enum:
|
||||
- anonymous
|
||||
type: string
|
||||
x-enum-varnames:
|
||||
- AnonymousUser
|
||||
info:
|
||||
contact: {}
|
||||
title: API Docs
|
||||
version: "1.0"
|
||||
paths:
|
||||
/api/v1/ping:
|
||||
get:
|
||||
/collections:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
deprecated: true
|
||||
description: 测试接口,将会返回当前用户的信息
|
||||
description: 在工作空间下创建新的集合
|
||||
parameters:
|
||||
- description: 创建集合请求
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.CreateCollectionRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
@ -40,20 +220,705 @@ paths:
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/response.Body'
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/schema.CurrentUserResponse'
|
||||
$ref: '#/definitions/leafdev_top_Leaf_leaf-library-3_internal_entity.Collection'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad Request
|
||||
description: 参数验证失败
|
||||
schema:
|
||||
$ref: '#/definitions/response.Body'
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Greet
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/dto.ValidateError'
|
||||
type: array
|
||||
type: object
|
||||
"404":
|
||||
description: 工作空间不存在
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
summary: 创建集合
|
||||
tags:
|
||||
- ping
|
||||
- Collection
|
||||
/collections/{collection_id}/documents:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 获取指定集合下的文档树,如果指定了父文档ID则获取该文档下的子树
|
||||
parameters:
|
||||
- description: 集合ID
|
||||
in: path
|
||||
name: collection_id
|
||||
required: true
|
||||
type: integer
|
||||
- description: 父文档ID,不传则获取根文档
|
||||
in: query
|
||||
name: parent_id
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/dto.DocumentDTO'
|
||||
type: array
|
||||
type: object
|
||||
"403":
|
||||
description: 无权访问
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
"404":
|
||||
description: 集合不存在或父文档不存在
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
summary: 列出文档树
|
||||
tags:
|
||||
- Document
|
||||
/collections/{id}:
|
||||
delete:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 删除指定的集合
|
||||
parameters:
|
||||
- description: 集合ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
"404":
|
||||
description: 集合不存在
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
summary: 删除集合
|
||||
tags:
|
||||
- Collection
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 根据集合ID获取集合详情
|
||||
parameters:
|
||||
- description: 集合ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/leafdev_top_Leaf_leaf-library-3_internal_entity.Collection'
|
||||
type: object
|
||||
"404":
|
||||
description: 集合不存在
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
summary: 获取集合
|
||||
tags:
|
||||
- Collection
|
||||
/documents:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 创建一个新的文档,可以是根文档或子文档
|
||||
parameters:
|
||||
- description: 创建文档请求
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.CreateDocumentRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/dto.DocumentDTO'
|
||||
type: object
|
||||
"400":
|
||||
description: 参数验证失败
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/dto.ValidateError'
|
||||
type: array
|
||||
type: object
|
||||
"403":
|
||||
description: 无权访问
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
"404":
|
||||
description: 集合不存在或父文档不存在
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
summary: 创建文档
|
||||
tags:
|
||||
- Document
|
||||
/documents/{document_id}/blocks:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 获取指文档下的所有文档块
|
||||
parameters:
|
||||
- description: 文档ID
|
||||
in: path
|
||||
name: document_id
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/entity.DocumentBlock'
|
||||
type: array
|
||||
type: object
|
||||
"404":
|
||||
description: 文档不存在
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
summary: 列出文档块列表
|
||||
tags:
|
||||
- Document
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 在文档中创建新的文档块
|
||||
parameters:
|
||||
- description: 文档ID
|
||||
in: path
|
||||
name: document_id
|
||||
required: true
|
||||
type: integer
|
||||
- description: 创建文档块请求
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.CreateBlockRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/entity.DocumentBlock'
|
||||
type: object
|
||||
"400":
|
||||
description: 参数验证失败
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/dto.ValidateError'
|
||||
type: array
|
||||
type: object
|
||||
"404":
|
||||
description: 文档不存在
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
summary: 创建文档块
|
||||
tags:
|
||||
- Document
|
||||
/documents/{document_id}/blocks/{block_id}:
|
||||
delete:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 删除指定的文档块
|
||||
parameters:
|
||||
- description: 文档ID
|
||||
in: path
|
||||
name: document_id
|
||||
required: true
|
||||
type: integer
|
||||
- description: 文档块ID
|
||||
in: path
|
||||
name: block_id
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
"404":
|
||||
description: 文档块不存在
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
summary: 删除文档块
|
||||
tags:
|
||||
- Document
|
||||
put:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 更新文档块的内容
|
||||
parameters:
|
||||
- description: 文档ID
|
||||
in: path
|
||||
name: document_id
|
||||
required: true
|
||||
type: integer
|
||||
- description: 文档块ID
|
||||
in: path
|
||||
name: block_id
|
||||
required: true
|
||||
type: integer
|
||||
- description: 更新文档块请求
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.UpdateBlockRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/entity.DocumentBlock'
|
||||
type: object
|
||||
"400":
|
||||
description: 参数验证失败
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/dto.ValidateError'
|
||||
type: array
|
||||
type: object
|
||||
"404":
|
||||
description: 文档块不存在
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
summary: 更新文档块
|
||||
tags:
|
||||
- Document
|
||||
/documents/{document_id}/blocks/{block_id}/move:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 移动文档块的位置
|
||||
parameters:
|
||||
- description: 文档ID
|
||||
in: path
|
||||
name: document_id
|
||||
required: true
|
||||
type: integer
|
||||
- description: 文档块ID
|
||||
in: path
|
||||
name: block_id
|
||||
required: true
|
||||
type: integer
|
||||
- description: 移动文档块请求
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.MoveBlockRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
"400":
|
||||
description: 参数验证失败
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/dto.ValidateError'
|
||||
type: array
|
||||
type: object
|
||||
"404":
|
||||
description: 文档块不存在
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
summary: 移动文档块
|
||||
tags:
|
||||
- Document
|
||||
/documents/{id}:
|
||||
delete:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 删除指定的文档
|
||||
parameters:
|
||||
- description: 文档ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
"404":
|
||||
description: 文档不存在
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
summary: 删除文档
|
||||
tags:
|
||||
- Document
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 根据文档ID获取文档详情
|
||||
parameters:
|
||||
- description: 文档ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/dto.DocumentDTO'
|
||||
type: object
|
||||
"403":
|
||||
description: 无权访问
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
"404":
|
||||
description: 文档不存在
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
summary: 获取文档
|
||||
tags:
|
||||
- Document
|
||||
put:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 更新文档的名称等信息
|
||||
parameters:
|
||||
- description: 文档ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
- description: 更新文档请求
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.UpdateDocumentRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/entity.Document'
|
||||
type: object
|
||||
"400":
|
||||
description: 参数验证失败
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/dto.ValidateError'
|
||||
type: array
|
||||
type: object
|
||||
"404":
|
||||
description: 文档不存在
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
summary: 更新文档
|
||||
tags:
|
||||
- Document
|
||||
/workspaces:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 获取当前用户的所有工作空间
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/entity.Workspace'
|
||||
type: array
|
||||
type: object
|
||||
summary: 列出工作空间列表
|
||||
tags:
|
||||
- Workspace
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 创建一个新的工作空间
|
||||
parameters:
|
||||
- description: 创建工作空间请求
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.CreateWorkspaceRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/entity.Workspace'
|
||||
type: object
|
||||
"400":
|
||||
description: 参数验证失败
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/dto.ValidateError'
|
||||
type: array
|
||||
type: object
|
||||
summary: 创建工作空间
|
||||
tags:
|
||||
- Workspace
|
||||
/workspaces/{id}:
|
||||
delete:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 删除指定的工作空间
|
||||
parameters:
|
||||
- description: 工作空间ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
"404":
|
||||
description: 工作空间不存在
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
summary: 删除工作空间
|
||||
tags:
|
||||
- Workspace
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 根据工作空间ID获取工作空间详情
|
||||
parameters:
|
||||
- description: 工作空间ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/entity.Workspace'
|
||||
type: object
|
||||
"404":
|
||||
description: 工作空间不存在
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
summary: 获取工作空间
|
||||
tags:
|
||||
- Workspace
|
||||
/workspaces/{id}/members:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 向工作空间添加新成员
|
||||
parameters:
|
||||
- description: 工作空间ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
- description: 添加成员请求
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.AddWorkspaceMemberRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/entity.WorkspaceMember'
|
||||
type: object
|
||||
"400":
|
||||
description: 参数验证失败
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/dto.ValidateError'
|
||||
type: array
|
||||
type: object
|
||||
"404":
|
||||
description: 工作空间不存在
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
"409":
|
||||
description: 成员已存在
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
summary: 添加工作空间成员
|
||||
tags:
|
||||
- Workspace
|
||||
/workspaces/{id}/members/{user_id}:
|
||||
delete:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 从工作空间移除成员
|
||||
parameters:
|
||||
- description: 工作空间ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
- description: 用户ID
|
||||
in: path
|
||||
name: user_id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
"404":
|
||||
description: 工作空间不存在或成员不存在
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
summary: 移除工作空间成员
|
||||
tags:
|
||||
- Workspace
|
||||
/workspaces/{workspace_id}/collections:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 获取指定工作空间下的所有集合
|
||||
parameters:
|
||||
- description: 工作空间ID
|
||||
in: path
|
||||
name: workspace_id
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/dto.Response'
|
||||
- properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/leafdev_top_Leaf_leaf-library-3_internal_entity.Collection'
|
||||
type: array
|
||||
type: object
|
||||
"404":
|
||||
description: 工作空间不存在
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Response'
|
||||
summary: 列出集合列表
|
||||
tags:
|
||||
- Collection
|
||||
securityDefinitions:
|
||||
ApiKeyAuth:
|
||||
in: header
|
||||
|
1
go.sum
1
go.sum
@ -128,6 +128,7 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
|
||||
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
|
168
internal/api/http/controller/collection.go
Normal file
168
internal/api/http/controller/collection.go
Normal file
@ -0,0 +1,168 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
_ "leafdev.top/Leaf/leaf-library-3/internal/entity"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/pkg/validator"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/auth"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/collection"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/workspace"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/types/dto"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/types/errs"
|
||||
)
|
||||
|
||||
type CollectionController struct {
|
||||
collectionService *collection.Service
|
||||
workspaceService *workspace.Service
|
||||
authService *auth.Service
|
||||
}
|
||||
|
||||
func NewCollectionController(
|
||||
collectionService *collection.Service,
|
||||
workspaceService *workspace.Service,
|
||||
authService *auth.Service,
|
||||
) *CollectionController {
|
||||
return &CollectionController{
|
||||
collectionService: collectionService,
|
||||
workspaceService: workspaceService,
|
||||
authService: authService,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateCollection 创建集合
|
||||
// @Summary 创建集合
|
||||
// @Description 在工作空间下创建新的集合
|
||||
// @Tags Collection
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body dto.CreateCollectionRequest true "创建集合请求"
|
||||
// @Success 200 {object} dto.Response{data=entity.Collection}
|
||||
// @Failure 400 {object} dto.Response{data=[]dto.ValidateError} "参数验证失败"
|
||||
// @Failure 404 {object} dto.Response "工作空间不存在"
|
||||
// @Router /collections [post]
|
||||
func (c *CollectionController) CreateCollection(ctx *fiber.Ctx) error {
|
||||
var req dto.CreateCollectionRequest
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if validationErrors, ok, err := validator.Struct(req); !ok {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dto.Ctx(ctx).Message((*validationErrors)[0].Message).Status(fiber.StatusBadRequest).Send()
|
||||
}
|
||||
|
||||
// 检查工作空间是否存在
|
||||
exists, err := c.workspaceService.Exists(ctx.Context(), req.WorkspaceID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return dto.Ctx(ctx).Error(errs.ErrWorkspaceNotExists).Status(fiber.StatusNotFound).Send()
|
||||
}
|
||||
|
||||
collection, err := c.collectionService.Create(ctx.Context(), req.WorkspaceID, req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dto.Ctx(ctx).Success(collection).Send()
|
||||
}
|
||||
|
||||
// GetCollection 获取集合
|
||||
// @Summary 获取集合
|
||||
// @Description 根据集合ID获取集合详情
|
||||
// @Tags Collection
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "集合ID"
|
||||
// @Success 200 {object} dto.Response{data=entity.Collection}
|
||||
// @Failure 404 {object} dto.Response "集合不存在"
|
||||
// @Router /collections/{id} [get]
|
||||
func (c *CollectionController) GetCollection(ctx *fiber.Ctx) error {
|
||||
var req dto.GetCollectionRequest
|
||||
if err := ctx.ParamsParser(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
exists, err := c.collectionService.Exists(ctx.Context(), req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return dto.Ctx(ctx).Error(errs.ErrCollectionNotExists).Status(fiber.StatusNotFound).Send()
|
||||
}
|
||||
|
||||
collection, err := c.collectionService.Get(ctx.Context(), req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dto.Ctx(ctx).Success(collection).Send()
|
||||
}
|
||||
|
||||
// ListCollections 列出工作空间下的所有集合
|
||||
// @Summary 列出集合列表
|
||||
// @Description 获取指定工作空间下的所有集合
|
||||
// @Tags Collection
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param workspace_id path int true "工作空间ID"
|
||||
// @Success 200 {object} dto.Response{data=[]entity.Collection}
|
||||
// @Failure 404 {object} dto.Response "工作空间不存在"
|
||||
// @Router /workspaces/{workspace_id}/collections [get]
|
||||
func (c *CollectionController) ListCollections(ctx *fiber.Ctx) error {
|
||||
var req dto.ListCollectionsRequest
|
||||
if err := ctx.ParamsParser(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 检查工作空间是否存在
|
||||
exists, err := c.workspaceService.Exists(ctx.Context(), req.WorkspaceID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return dto.Ctx(ctx).Error(errs.ErrWorkspaceNotExists).Status(fiber.StatusNotFound).Send()
|
||||
}
|
||||
|
||||
collections, err := c.collectionService.List(ctx.Context(), req.WorkspaceID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dto.Ctx(ctx).Success(collections).Send()
|
||||
}
|
||||
|
||||
// DeleteCollection 删除集合
|
||||
// @Summary 删除集合
|
||||
// @Description 删除指定的集合
|
||||
// @Tags Collection
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "集合ID"
|
||||
// @Success 200 {object} dto.Response
|
||||
// @Failure 404 {object} dto.Response "集合不存在"
|
||||
// @Router /collections/{id} [delete]
|
||||
func (c *CollectionController) DeleteCollection(ctx *fiber.Ctx) error {
|
||||
var req dto.DeleteCollectionRequest
|
||||
if err := ctx.ParamsParser(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
exists, err := c.collectionService.Exists(ctx.Context(), req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return dto.Ctx(ctx).Error(errs.ErrCollectionNotExists).Status(fiber.StatusNotFound).Send()
|
||||
}
|
||||
|
||||
err = c.collectionService.Delete(ctx.Context(), req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dto.Ctx(ctx).Success(nil).Send()
|
||||
}
|
476
internal/api/http/controller/document.go
Normal file
476
internal/api/http/controller/document.go
Normal file
@ -0,0 +1,476 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/pkg/validator"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/auth"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/collection"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/document"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/workspace"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/types/dto"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/types/errs"
|
||||
)
|
||||
|
||||
type DocumentController struct {
|
||||
documentService *document.Service
|
||||
collectionService *collection.Service
|
||||
workspaceService *workspace.Service
|
||||
authService *auth.Service
|
||||
}
|
||||
|
||||
func NewDocumentController(
|
||||
documentService *document.Service,
|
||||
collectionService *collection.Service,
|
||||
workspaceService *workspace.Service,
|
||||
authService *auth.Service,
|
||||
) *DocumentController {
|
||||
return &DocumentController{
|
||||
documentService: documentService,
|
||||
collectionService: collectionService,
|
||||
workspaceService: workspaceService,
|
||||
authService: authService,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateDocument 创建文档
|
||||
// @Summary 创建文档
|
||||
// @Description 创建一个新的文档,可以是根文档或子文档
|
||||
// @Tags Document
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body dto.CreateDocumentRequest true "创建文档请求"
|
||||
// @Success 200 {object} dto.Response{data=dto.DocumentDTO}
|
||||
// @Failure 400 {object} dto.Response{data=[]dto.ValidateError} "参数验证失败"
|
||||
// @Failure 403 {object} dto.Response "无权访问"
|
||||
// @Failure 404 {object} dto.Response "集合不存在或父文档不存在"
|
||||
// @Router /documents [post]
|
||||
func (c *DocumentController) CreateDocument(ctx *fiber.Ctx) error {
|
||||
var req dto.CreateDocumentRequest
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if validationErrors, ok, err := validator.Struct(req); !ok {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dto.Ctx(ctx).Data(validationErrors).Status(fiber.StatusBadRequest).Send()
|
||||
}
|
||||
|
||||
// 检查集合是否存在
|
||||
collection, err := c.collectionService.Get(ctx.Context(), req.CollectionID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if collection == nil {
|
||||
return dto.Ctx(ctx).Error(errs.ErrCollectionNotExists).Status(fiber.StatusNotFound).Send()
|
||||
}
|
||||
|
||||
// 检查用户是否有权限访问该集合
|
||||
user := c.authService.GetUser(ctx)
|
||||
workspace, err := c.workspaceService.Get(ctx.Context(), collection.WorkspaceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if workspace == nil {
|
||||
return dto.Ctx(ctx).Error(errs.ErrWorkspaceNotExists).Status(fiber.StatusNotFound).Send()
|
||||
}
|
||||
|
||||
isMember, err := c.workspaceService.IsMember(ctx.Context(), user.ID, workspace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !isMember {
|
||||
return dto.Ctx(ctx).Error(errs.ErrNoPermission).Status(fiber.StatusForbidden).Send()
|
||||
}
|
||||
|
||||
// 如果是子文档,检查父文档是否存在且属于同一个集合
|
||||
if req.ParentID != nil {
|
||||
parentDoc, err := c.documentService.Get(ctx.Context(), *req.ParentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if parentDoc == nil {
|
||||
return dto.Ctx(ctx).Error(errs.ErrDocumentNotExists).Status(fiber.StatusNotFound).Send()
|
||||
}
|
||||
if parentDoc.CollectionId != req.CollectionID {
|
||||
return dto.Ctx(ctx).Error(errs.ErrInvalidParentDocument).Status(fiber.StatusBadRequest).Send()
|
||||
}
|
||||
}
|
||||
|
||||
doc, err := c.documentService.Create(ctx.Context(), req.Name, req.WorkspaceID, req.CollectionID, req.ParentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
docDTO, err := c.documentService.ToDTO(ctx.Context(), doc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dto.Ctx(ctx).Success(docDTO).Send()
|
||||
}
|
||||
|
||||
// GetDocument 获取文档
|
||||
// @Summary 获取文档
|
||||
// @Description 根据文档ID获取文档详情
|
||||
// @Tags Document
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "文档ID"
|
||||
// @Success 200 {object} dto.Response{data=dto.DocumentDTO}
|
||||
// @Failure 403 {object} dto.Response "无权访问"
|
||||
// @Failure 404 {object} dto.Response "文档不存在"
|
||||
// @Router /documents/{id} [get]
|
||||
func (c *DocumentController) GetDocument(ctx *fiber.Ctx) error {
|
||||
var params dto.DocumentIDParam
|
||||
if err := ctx.ParamsParser(¶ms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
doc, err := c.documentService.Get(ctx.Context(), params.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if doc == nil {
|
||||
return dto.Ctx(ctx).Error(errs.ErrDocumentNotExists).Status(fiber.StatusNotFound).Send()
|
||||
}
|
||||
|
||||
// 检查用户是否有权限访问该文档
|
||||
user := c.authService.GetUser(ctx)
|
||||
collection, err := c.collectionService.Get(ctx.Context(), doc.CollectionId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
workspace, err := c.workspaceService.Get(ctx.Context(), collection.WorkspaceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isMember, err := c.workspaceService.IsMember(ctx.Context(), user.ID, workspace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !isMember {
|
||||
return dto.Ctx(ctx).Error(errs.ErrNoPermission).Status(fiber.StatusForbidden).Send()
|
||||
}
|
||||
|
||||
docDTO, err := c.documentService.ToDTO(ctx.Context(), doc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dto.Ctx(ctx).Success(docDTO).Send()
|
||||
}
|
||||
|
||||
// ListDocuments 列出集合或父文档下的所有文档
|
||||
// @Summary 列出文档树
|
||||
// @Description 获取指定集合下的文档树,如果指定了父文档ID则获取该文档下的子树
|
||||
// @Tags Document
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param collection_id path int true "集合ID"
|
||||
// @Param parent_id query int false "父文档ID,不传则获取根文档"
|
||||
// @Success 200 {object} dto.Response{data=[]dto.DocumentDTO}
|
||||
// @Failure 403 {object} dto.Response "无权访问"
|
||||
// @Failure 404 {object} dto.Response "集合不存在或父文档不存在"
|
||||
// @Router /collections/{collection_id}/documents [get]
|
||||
func (c *DocumentController) ListDocuments(ctx *fiber.Ctx) error {
|
||||
var pathParams dto.ListDocumentsPathParam
|
||||
if err := ctx.ParamsParser(&pathParams); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var queryParams dto.ListDocumentsQueryParam
|
||||
if err := ctx.QueryParser(&queryParams); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 检查集合是否存在
|
||||
collection, err := c.collectionService.Get(ctx.Context(), pathParams.CollectionID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if collection == nil {
|
||||
return dto.Ctx(ctx).Error(errs.ErrCollectionNotExists).Status(fiber.StatusNotFound).Send()
|
||||
}
|
||||
|
||||
// 检查用户是否有权限访问该集合
|
||||
user := c.authService.GetUser(ctx)
|
||||
workspace, err := c.workspaceService.Get(ctx.Context(), collection.WorkspaceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isMember, err := c.workspaceService.IsMember(ctx.Context(), user.ID, workspace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !isMember {
|
||||
return dto.Ctx(ctx).Error(errs.ErrNoPermission).Status(fiber.StatusForbidden).Send()
|
||||
}
|
||||
|
||||
// 如果指定了父文档,检查父文档是否存在且属于同一个集合
|
||||
if queryParams.ParentID != nil {
|
||||
parentDoc, err := c.documentService.Get(ctx.Context(), *queryParams.ParentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if parentDoc == nil {
|
||||
return dto.Ctx(ctx).Error(errs.ErrDocumentNotExists).Status(fiber.StatusNotFound).Send()
|
||||
}
|
||||
if parentDoc.CollectionId != pathParams.CollectionID {
|
||||
return dto.Ctx(ctx).Error(errs.ErrInvalidParentDocument).Status(fiber.StatusBadRequest).Send()
|
||||
}
|
||||
}
|
||||
|
||||
// 获取文档列表
|
||||
docs, err := c.documentService.List(ctx.Context(), pathParams.CollectionID, queryParams.ParentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 转换为 DTO
|
||||
dtos := make([]*dto.DocumentDTO, len(docs))
|
||||
for i, doc := range docs {
|
||||
docDTO, err := c.documentService.ToDTO(ctx.Context(), doc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dtos[i] = docDTO
|
||||
}
|
||||
|
||||
return dto.Ctx(ctx).Success(dtos).Send()
|
||||
}
|
||||
|
||||
// UpdateDocument 更新文档
|
||||
// @Summary 更新文档
|
||||
// @Description 更新文档的名称等信息
|
||||
// @Tags Document
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "文档ID"
|
||||
// @Param request body dto.UpdateDocumentRequest true "更新文档请求"
|
||||
// @Success 200 {object} dto.Response{data=entity.Document}
|
||||
// @Failure 400 {object} dto.Response{data=[]dto.ValidateError} "参数验证失败"
|
||||
// @Failure 404 {object} dto.Response "文档不存在"
|
||||
// @Router /documents/{id} [put]
|
||||
func (c *DocumentController) UpdateDocument(ctx *fiber.Ctx) error {
|
||||
var params dto.DocumentIDParam
|
||||
if err := ctx.ParamsParser(¶ms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var req dto.UpdateDocumentRequest
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if validationErrors, ok, err := validator.Struct(req); !ok {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dto.Ctx(ctx).Data(validationErrors).Status(fiber.StatusBadRequest).Send()
|
||||
}
|
||||
|
||||
doc, err := c.documentService.Update(ctx.Context(), params.ID, req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
docDTO, err := c.documentService.ToDTO(ctx.Context(), doc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dto.Ctx(ctx).Success(docDTO).Send()
|
||||
}
|
||||
|
||||
// DeleteDocument 删除文档
|
||||
// @Summary 删除文档
|
||||
// @Description 删除指定的文档
|
||||
// @Tags Document
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "文档ID"
|
||||
// @Success 200 {object} dto.Response
|
||||
// @Failure 404 {object} dto.Response "文档不存在"
|
||||
// @Router /documents/{id} [delete]
|
||||
func (c *DocumentController) DeleteDocument(ctx *fiber.Ctx) error {
|
||||
var params dto.DocumentIDParam
|
||||
if err := ctx.ParamsParser(¶ms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err := c.documentService.Delete(ctx.Context(), params.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dto.Ctx(ctx).Success(nil).Send()
|
||||
}
|
||||
|
||||
// CreateBlock 创建文档块
|
||||
// @Summary 创建文档块
|
||||
// @Description 在文档中创建新的文档块
|
||||
// @Tags Document
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param document_id path int true "文档ID"
|
||||
// @Param request body dto.CreateBlockRequest true "创建文档块请求"
|
||||
// @Success 200 {object} dto.Response{data=entity.DocumentBlock}
|
||||
// @Failure 400 {object} dto.Response{data=[]dto.ValidateError} "参数验证失败"
|
||||
// @Failure 404 {object} dto.Response "文档不存在"
|
||||
// @Router /documents/{document_id}/blocks [post]
|
||||
func (c *DocumentController) CreateBlock(ctx *fiber.Ctx) error {
|
||||
var params dto.ListBlocksRequest
|
||||
if err := ctx.ParamsParser(¶ms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var req dto.CreateBlockRequest
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if validationErrors, ok, err := validator.Struct(req); !ok {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dto.Ctx(ctx).Data(validationErrors).Status(fiber.StatusBadRequest).Send()
|
||||
}
|
||||
|
||||
block, err := c.documentService.CreateBlock(ctx.Context(), params.DocumentID, req.Type, req.Content, req.AfterBlockID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dto.Ctx(ctx).Success(block).Send()
|
||||
}
|
||||
|
||||
// UpdateBlock 更新文档块
|
||||
// @Summary 更新文档块
|
||||
// @Description 更新文档块的内容
|
||||
// @Tags Document
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param document_id path int true "文档ID"
|
||||
// @Param block_id path int true "文档块ID"
|
||||
// @Param request body dto.UpdateBlockRequest true "更新文档块请求"
|
||||
// @Success 200 {object} dto.Response{data=entity.DocumentBlock}
|
||||
// @Failure 400 {object} dto.Response{data=[]dto.ValidateError} "参数验证失败"
|
||||
// @Failure 404 {object} dto.Response "文档块不存在"
|
||||
// @Router /documents/{document_id}/blocks/{block_id} [put]
|
||||
func (c *DocumentController) UpdateBlock(ctx *fiber.Ctx) error {
|
||||
var params dto.BlockIDParam
|
||||
if err := ctx.ParamsParser(¶ms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var req dto.UpdateBlockRequest
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if validationErrors, ok, err := validator.Struct(req); !ok {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dto.Ctx(ctx).Data(validationErrors).Status(fiber.StatusBadRequest).Send()
|
||||
}
|
||||
|
||||
block, err := c.documentService.UpdateBlock(ctx.Context(), params.ID, req.Content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dto.Ctx(ctx).Success(block).Send()
|
||||
}
|
||||
|
||||
// DeleteBlock 删除文档块
|
||||
// @Summary 删除文档块
|
||||
// @Description 删除指定的文档块
|
||||
// @Tags Document
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param document_id path int true "文档ID"
|
||||
// @Param block_id path int true "文档块ID"
|
||||
// @Success 200 {object} dto.Response
|
||||
// @Failure 404 {object} dto.Response "文档块不存在"
|
||||
// @Router /documents/{document_id}/blocks/{block_id} [delete]
|
||||
func (c *DocumentController) DeleteBlock(ctx *fiber.Ctx) error {
|
||||
var params dto.BlockIDParam
|
||||
if err := ctx.ParamsParser(¶ms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err := c.documentService.DeleteBlock(ctx.Context(), params.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dto.Ctx(ctx).Success(nil).Send()
|
||||
}
|
||||
|
||||
// ListBlocks 列出文档下的所有块
|
||||
// @Summary 列出文档块列表
|
||||
// @Description 获取指文档下的所有文档块
|
||||
// @Tags Document
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param document_id path int true "文档ID"
|
||||
// @Success 200 {object} dto.Response{data=[]entity.DocumentBlock}
|
||||
// @Failure 404 {object} dto.Response "文档不存在"
|
||||
// @Router /documents/{document_id}/blocks [get]
|
||||
func (c *DocumentController) ListBlocks(ctx *fiber.Ctx) error {
|
||||
var params dto.ListBlocksRequest
|
||||
if err := ctx.ParamsParser(¶ms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blocks, err := c.documentService.ListBlocks(ctx.Context(), params.DocumentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dto.Ctx(ctx).Success(blocks).Send()
|
||||
}
|
||||
|
||||
// MoveBlock 移动文档块
|
||||
// @Summary 移动文档块
|
||||
// @Description 移动文档块的位置
|
||||
// @Tags Document
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param document_id path int true "文档ID"
|
||||
// @Param block_id path int true "文档块ID"
|
||||
// @Param request body dto.MoveBlockRequest true "移动文档块请求"
|
||||
// @Success 200 {object} dto.Response
|
||||
// @Failure 400 {object} dto.Response{data=[]dto.ValidateError} "参数验证失败"
|
||||
// @Failure 404 {object} dto.Response "文档块不存在"
|
||||
// @Router /documents/{document_id}/blocks/{block_id}/move [post]
|
||||
func (c *DocumentController) MoveBlock(ctx *fiber.Ctx) error {
|
||||
var params dto.BlockIDParam
|
||||
if err := ctx.ParamsParser(¶ms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var req dto.MoveBlockRequest
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if validationErrors, ok, err := validator.Struct(req); !ok {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dto.Ctx(ctx).Data(validationErrors).Status(fiber.StatusBadRequest).Send()
|
||||
}
|
||||
|
||||
err := c.documentService.MoveBlock(ctx.Context(), params.ID, req.AfterBlockID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dto.Ctx(ctx).Success(nil).Send()
|
||||
}
|
240
internal/api/http/controller/workspace.go
Normal file
240
internal/api/http/controller/workspace.go
Normal file
@ -0,0 +1,240 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
_ "leafdev.top/Leaf/leaf-library-3/internal/entity"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/pkg/validator"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/auth"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/workspace"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/types/dto"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/types/errs"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/types/user"
|
||||
)
|
||||
|
||||
type WorkspaceController struct {
|
||||
workspaceService *workspace.Service
|
||||
authService *auth.Service
|
||||
}
|
||||
|
||||
func NewWorkspaceController(workspaceService *workspace.Service, authService *auth.Service) *WorkspaceController {
|
||||
return &WorkspaceController{
|
||||
workspaceService: workspaceService,
|
||||
authService: authService,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateWorkspace 创建工作空间
|
||||
// @Summary 创建工作空间
|
||||
// @Description 创建一个新的工作空间
|
||||
// @Tags Workspace
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body dto.CreateWorkspaceRequest true "创建工作空间请求"
|
||||
// @Success 200 {object} dto.Response{data=entity.Workspace}
|
||||
// @Failure 400 {object} dto.Response{data=[]dto.ValidateError} "参数验证失败"
|
||||
// @Router /workspaces [post]
|
||||
func (c *WorkspaceController) CreateWorkspace(ctx *fiber.Ctx) error {
|
||||
var req dto.CreateWorkspaceRequest
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if validationErrors, ok, err := validator.Struct(req); !ok {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dto.Ctx(ctx).Data(validationErrors).Status(fiber.StatusBadRequest).Send()
|
||||
}
|
||||
|
||||
// 从上下文获取用户ID
|
||||
user := c.authService.GetUser(ctx)
|
||||
userId := user.ID
|
||||
|
||||
workspace, err := c.workspaceService.Create(ctx.Context(), userId, req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dto.Ctx(ctx).Success(workspace).Send()
|
||||
}
|
||||
|
||||
// GetWorkspace 获取工作空间
|
||||
// @Summary 获取工作空间
|
||||
// @Description 根据工作空间ID获取工作空间详情
|
||||
// @Tags Workspace
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "工作空间ID"
|
||||
// @Success 200 {object} dto.Response{data=entity.Workspace}
|
||||
// @Failure 404 {object} dto.Response "工作空间不存在"
|
||||
// @Router /workspaces/{id} [get]
|
||||
func (c *WorkspaceController) GetWorkspace(ctx *fiber.Ctx) error {
|
||||
var params dto.WorkspaceIDParam
|
||||
if err := ctx.ParamsParser(¶ms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
exists, err := c.workspaceService.Exists(ctx.Context(), params.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return dto.Ctx(ctx).Error(errs.ErrWorkspaceNotExists).Status(fiber.StatusNotFound).Send()
|
||||
}
|
||||
|
||||
workspace, err := c.workspaceService.Get(ctx.Context(), params.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dto.Ctx(ctx).Success(workspace).Send()
|
||||
}
|
||||
|
||||
// ListWorkspaces 列出用户的所有工作空间
|
||||
// @Summary 列出工作空间列表
|
||||
// @Description 获取当前用户的所有工作空间
|
||||
// @Tags Workspace
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} dto.Response{data=[]entity.Workspace}
|
||||
// @Router /workspaces [get]
|
||||
func (c *WorkspaceController) ListWorkspaces(ctx *fiber.Ctx) error {
|
||||
// 从上下文获取用户ID
|
||||
user := c.authService.GetUser(ctx)
|
||||
userId := user.ID
|
||||
|
||||
workspaces, err := c.workspaceService.List(ctx.Context(), userId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dto.Ctx(ctx).Success(workspaces).Send()
|
||||
}
|
||||
|
||||
// DeleteWorkspace 删除工作空间
|
||||
// @Summary 删除工作空间
|
||||
// @Description 删除指定的工作空间
|
||||
// @Tags Workspace
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "工作空间ID"
|
||||
// @Success 200 {object} dto.Response
|
||||
// @Failure 404 {object} dto.Response "工作空间不存在"
|
||||
// @Router /workspaces/{id} [delete]
|
||||
func (c *WorkspaceController) DeleteWorkspace(ctx *fiber.Ctx) error {
|
||||
var params dto.WorkspaceIDParam
|
||||
if err := ctx.ParamsParser(¶ms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
exists, err := c.workspaceService.Exists(ctx.Context(), params.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return dto.Ctx(ctx).Error(errs.ErrWorkspaceNotExists).Status(fiber.StatusNotFound).Send()
|
||||
}
|
||||
|
||||
err = c.workspaceService.DeleteWorkspace(ctx.Context(), params.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dto.Ctx(ctx).Success(nil).Send()
|
||||
}
|
||||
|
||||
// AddMember 添加工作空间成员
|
||||
// @Summary 添加工作空间成员
|
||||
// @Description 向工作空间添加新成员
|
||||
// @Tags Workspace
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "工作空间ID"
|
||||
// @Param request body dto.AddWorkspaceMemberRequest true "添加成员请求"
|
||||
// @Success 200 {object} dto.Response{data=entity.WorkspaceMember}
|
||||
// @Failure 400 {object} dto.Response{data=[]dto.ValidateError} "参数验证失败"
|
||||
// @Failure 404 {object} dto.Response "工作空间不存在"
|
||||
// @Failure 409 {object} dto.Response "成员已存在"
|
||||
// @Router /workspaces/{id}/members [post]
|
||||
func (c *WorkspaceController) AddMember(ctx *fiber.Ctx) error {
|
||||
var params dto.WorkspaceIDParam
|
||||
if err := ctx.ParamsParser(¶ms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
exists, err := c.workspaceService.Exists(ctx.Context(), params.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return dto.Ctx(ctx).Error(errs.ErrWorkspaceNotExists).Status(fiber.StatusNotFound).Send()
|
||||
}
|
||||
|
||||
var req dto.AddWorkspaceMemberRequest
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if validationErrors, ok, err := validator.Struct(req); !ok {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dto.Ctx(ctx).Data(validationErrors).Status(fiber.StatusBadRequest).Send()
|
||||
}
|
||||
|
||||
workspace, err := c.workspaceService.Get(ctx.Context(), params.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
member, err := c.workspaceService.AddMember(ctx.Context(), req.UserID, workspace)
|
||||
if err != nil {
|
||||
if err == errs.ErrMemberAlreadyExists {
|
||||
return dto.Ctx(ctx).Error(err).Status(fiber.StatusConflict).Send()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return dto.Ctx(ctx).Success(member).Send()
|
||||
}
|
||||
|
||||
// RemoveMember 移除工作空间成员
|
||||
// @Summary 移除工作空间成员
|
||||
// @Description 从工作空间移除成员
|
||||
// @Tags Workspace
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "工作空间ID"
|
||||
// @Param user_id path string true "用户ID"
|
||||
// @Success 200 {object} dto.Response
|
||||
// @Failure 404 {object} dto.Response "工作空间不存在或成员不存在"
|
||||
// @Router /workspaces/{id}/members/{user_id} [delete]
|
||||
func (c *WorkspaceController) RemoveMember(ctx *fiber.Ctx) error {
|
||||
var params dto.WorkspaceMemberParam
|
||||
if err := ctx.ParamsParser(¶ms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
exists, err := c.workspaceService.Exists(ctx.Context(), params.WorkspaceID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return dto.Ctx(ctx).Error(errs.ErrWorkspaceNotExists).Status(fiber.StatusNotFound).Send()
|
||||
}
|
||||
|
||||
workspace, err := c.workspaceService.Get(ctx.Context(), params.WorkspaceID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.workspaceService.RemoveMember(ctx.Context(), user.ID(params.UserID), workspace)
|
||||
if err != nil {
|
||||
if err == errs.ErrMemberNotExists {
|
||||
return dto.Ctx(ctx).Error(err).Status(fiber.StatusNotFound).Send()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return dto.Ctx(ctx).Success(nil).Send()
|
||||
}
|
@ -3,8 +3,8 @@ package http
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/google/wire"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/api/http/controller"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/api/http/middleware"
|
||||
v1 "leafdev.top/Leaf/leaf-library-3/internal/api/http/v1"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/base/conf"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/base/logger"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/auth"
|
||||
@ -22,14 +22,21 @@ type Middleware struct {
|
||||
}
|
||||
|
||||
type Handlers struct {
|
||||
User *v1.UserController
|
||||
// User *v1.UserController
|
||||
Workspace *controller.WorkspaceController
|
||||
Collection *controller.CollectionController
|
||||
Document *controller.DocumentController
|
||||
}
|
||||
|
||||
func NewHandler(
|
||||
user *v1.UserController,
|
||||
workspace *controller.WorkspaceController,
|
||||
collection *controller.CollectionController,
|
||||
document *controller.DocumentController,
|
||||
) *Handlers {
|
||||
return &Handlers{
|
||||
User: user,
|
||||
Workspace: workspace,
|
||||
Collection: collection,
|
||||
Document: document,
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,7 +58,10 @@ var ProviderSet = wire.NewSet(
|
||||
NewMiddleware,
|
||||
|
||||
// Init Controller
|
||||
v1.NewUserController,
|
||||
// controller.NewUserController,
|
||||
controller.NewWorkspaceController,
|
||||
controller.NewCollectionController,
|
||||
controller.NewDocumentController,
|
||||
|
||||
// Init Handler
|
||||
NewHandler,
|
||||
|
@ -1,56 +0,0 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/pkg/validator"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/auth"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/types/dto"
|
||||
)
|
||||
|
||||
type UserController struct {
|
||||
authService *auth.Service
|
||||
}
|
||||
|
||||
func NewUserController(authService *auth.Service) *UserController {
|
||||
return &UserController{authService}
|
||||
}
|
||||
|
||||
// Test godoc
|
||||
// @Summary Greet
|
||||
// @Description 测试接口,将会返回当前用户的信息
|
||||
// @Tags ping
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security ApiKeyAuth
|
||||
// @deprecated true
|
||||
// @Success 200 {object} response.Body{data=schema.CurrentUserResponse}
|
||||
// @Failure 400 {object} response.Body
|
||||
// @Router /api/v1/ping [get]
|
||||
func (u *UserController) Test(c *fiber.Ctx) error {
|
||||
user := u.authService.GetUser(c)
|
||||
|
||||
// bind
|
||||
var testRequest = &dto.TestRequest{}
|
||||
err := c.QueryParser(testRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 验证
|
||||
validationErrors, err := validator.Struct(testRequest)
|
||||
if err != nil {
|
||||
return dto.Ctx(c).Error(err).Data(validationErrors).Send()
|
||||
}
|
||||
|
||||
var currentUserResponse = &dto.CurrentUserResponse{
|
||||
IP: c.IP(),
|
||||
Valid: user.Valid,
|
||||
UserEmail: user.Token.Email,
|
||||
UserId: user.Token.Sub,
|
||||
UserName: user.Token.Name,
|
||||
}
|
||||
|
||||
return dto.Ctx(c).Status(http.StatusOK).Data(currentUserResponse).Send()
|
||||
}
|
@ -39,6 +39,7 @@ func errorConverter(logger *logger.Logger, ctx *fiber.Ctx, err error) error {
|
||||
|
||||
case errors.Is(err, gorm.ErrRecordNotFound):
|
||||
errorMsg = errs.ErrNotFound
|
||||
status = http.StatusNotFound
|
||||
|
||||
default:
|
||||
logger.Sugar.Errorf("fiber error: %s", err)
|
||||
|
@ -27,7 +27,7 @@ func newBlockChunk(db *gorm.DB, opts ...gen.DOOption) blockChunk {
|
||||
|
||||
tableName := _blockChunk.blockChunkDo.TableName()
|
||||
_blockChunk.ALL = field.NewAsterisk(tableName)
|
||||
_blockChunk.Id = field.NewUint(tableName, "id")
|
||||
_blockChunk.ID = field.NewUint(tableName, "id")
|
||||
_blockChunk.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
_blockChunk.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||
_blockChunk.DocumentBlockId = field.NewUint(tableName, "document_block_id")
|
||||
@ -87,7 +87,7 @@ type blockChunk struct {
|
||||
blockChunkDo
|
||||
|
||||
ALL field.Asterisk
|
||||
Id field.Uint
|
||||
ID field.Uint
|
||||
CreatedAt field.Time
|
||||
UpdatedAt field.Time
|
||||
DocumentBlockId field.Uint
|
||||
@ -109,7 +109,7 @@ func (b blockChunk) As(alias string) *blockChunk {
|
||||
|
||||
func (b *blockChunk) updateTableName(table string) *blockChunk {
|
||||
b.ALL = field.NewAsterisk(table)
|
||||
b.Id = field.NewUint(table, "id")
|
||||
b.ID = field.NewUint(table, "id")
|
||||
b.CreatedAt = field.NewTime(table, "created_at")
|
||||
b.UpdatedAt = field.NewTime(table, "updated_at")
|
||||
b.DocumentBlockId = field.NewUint(table, "document_block_id")
|
||||
@ -131,7 +131,7 @@ func (b *blockChunk) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||
|
||||
func (b *blockChunk) fillFieldMap() {
|
||||
b.fieldMap = make(map[string]field.Expr, 6)
|
||||
b.fieldMap["id"] = b.Id
|
||||
b.fieldMap["id"] = b.ID
|
||||
b.fieldMap["created_at"] = b.CreatedAt
|
||||
b.fieldMap["updated_at"] = b.UpdatedAt
|
||||
b.fieldMap["document_block_id"] = b.DocumentBlockId
|
||||
|
@ -27,7 +27,7 @@ func newCollection(db *gorm.DB, opts ...gen.DOOption) collection {
|
||||
|
||||
tableName := _collection.collectionDo.TableName()
|
||||
_collection.ALL = field.NewAsterisk(tableName)
|
||||
_collection.Id = field.NewUint(tableName, "id")
|
||||
_collection.ID = field.NewUint(tableName, "id")
|
||||
_collection.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
_collection.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||
_collection.Name = field.NewString(tableName, "name")
|
||||
@ -48,7 +48,7 @@ type collection struct {
|
||||
collectionDo
|
||||
|
||||
ALL field.Asterisk
|
||||
Id field.Uint
|
||||
ID field.Uint
|
||||
CreatedAt field.Time
|
||||
UpdatedAt field.Time
|
||||
Name field.String
|
||||
@ -71,7 +71,7 @@ func (c collection) As(alias string) *collection {
|
||||
|
||||
func (c *collection) updateTableName(table string) *collection {
|
||||
c.ALL = field.NewAsterisk(table)
|
||||
c.Id = field.NewUint(table, "id")
|
||||
c.ID = field.NewUint(table, "id")
|
||||
c.CreatedAt = field.NewTime(table, "created_at")
|
||||
c.UpdatedAt = field.NewTime(table, "updated_at")
|
||||
c.Name = field.NewString(table, "name")
|
||||
@ -94,7 +94,7 @@ func (c *collection) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||
|
||||
func (c *collection) fillFieldMap() {
|
||||
c.fieldMap = make(map[string]field.Expr, 7)
|
||||
c.fieldMap["id"] = c.Id
|
||||
c.fieldMap["id"] = c.ID
|
||||
c.fieldMap["created_at"] = c.CreatedAt
|
||||
c.fieldMap["updated_at"] = c.UpdatedAt
|
||||
c.fieldMap["name"] = c.Name
|
||||
|
@ -27,13 +27,14 @@ func newDocumentBlock(db *gorm.DB, opts ...gen.DOOption) documentBlock {
|
||||
|
||||
tableName := _documentBlock.documentBlockDo.TableName()
|
||||
_documentBlock.ALL = field.NewAsterisk(tableName)
|
||||
_documentBlock.Id = field.NewUint(tableName, "id")
|
||||
_documentBlock.ID = field.NewUint(tableName, "id")
|
||||
_documentBlock.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
_documentBlock.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||
_documentBlock.DocumentId = field.NewUint(tableName, "document_id")
|
||||
_documentBlock.Type = field.NewString(tableName, "type")
|
||||
_documentBlock.Content = field.NewString(tableName, "content")
|
||||
_documentBlock.Hash = field.NewString(tableName, "hash")
|
||||
_documentBlock.Order_ = field.NewFloat64(tableName, "order")
|
||||
_documentBlock.Document = documentBlockBelongsToDocument{
|
||||
db: db.Session(&gorm.Session{}),
|
||||
|
||||
@ -72,13 +73,14 @@ type documentBlock struct {
|
||||
documentBlockDo
|
||||
|
||||
ALL field.Asterisk
|
||||
Id field.Uint
|
||||
ID field.Uint
|
||||
CreatedAt field.Time
|
||||
UpdatedAt field.Time
|
||||
DocumentId field.Uint
|
||||
Type field.String
|
||||
Content field.String
|
||||
Hash field.String
|
||||
Order_ field.Float64
|
||||
Document documentBlockBelongsToDocument
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
@ -96,13 +98,14 @@ func (d documentBlock) As(alias string) *documentBlock {
|
||||
|
||||
func (d *documentBlock) updateTableName(table string) *documentBlock {
|
||||
d.ALL = field.NewAsterisk(table)
|
||||
d.Id = field.NewUint(table, "id")
|
||||
d.ID = field.NewUint(table, "id")
|
||||
d.CreatedAt = field.NewTime(table, "created_at")
|
||||
d.UpdatedAt = field.NewTime(table, "updated_at")
|
||||
d.DocumentId = field.NewUint(table, "document_id")
|
||||
d.Type = field.NewString(table, "type")
|
||||
d.Content = field.NewString(table, "content")
|
||||
d.Hash = field.NewString(table, "hash")
|
||||
d.Order_ = field.NewFloat64(table, "order")
|
||||
|
||||
d.fillFieldMap()
|
||||
|
||||
@ -119,14 +122,15 @@ func (d *documentBlock) GetFieldByName(fieldName string) (field.OrderExpr, bool)
|
||||
}
|
||||
|
||||
func (d *documentBlock) fillFieldMap() {
|
||||
d.fieldMap = make(map[string]field.Expr, 8)
|
||||
d.fieldMap["id"] = d.Id
|
||||
d.fieldMap = make(map[string]field.Expr, 9)
|
||||
d.fieldMap["id"] = d.ID
|
||||
d.fieldMap["created_at"] = d.CreatedAt
|
||||
d.fieldMap["updated_at"] = d.UpdatedAt
|
||||
d.fieldMap["document_id"] = d.DocumentId
|
||||
d.fieldMap["type"] = d.Type
|
||||
d.fieldMap["content"] = d.Content
|
||||
d.fieldMap["hash"] = d.Hash
|
||||
d.fieldMap["order"] = d.Order_
|
||||
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ func newDocument(db *gorm.DB, opts ...gen.DOOption) document {
|
||||
|
||||
tableName := _document.documentDo.TableName()
|
||||
_document.ALL = field.NewAsterisk(tableName)
|
||||
_document.Id = field.NewUint(tableName, "id")
|
||||
_document.ID = field.NewUint(tableName, "id")
|
||||
_document.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
_document.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||
_document.Name = field.NewString(tableName, "name")
|
||||
@ -82,7 +82,7 @@ type document struct {
|
||||
documentDo
|
||||
|
||||
ALL field.Asterisk
|
||||
Id field.Uint
|
||||
ID field.Uint
|
||||
CreatedAt field.Time
|
||||
UpdatedAt field.Time
|
||||
Name field.String
|
||||
@ -111,7 +111,7 @@ func (d document) As(alias string) *document {
|
||||
|
||||
func (d *document) updateTableName(table string) *document {
|
||||
d.ALL = field.NewAsterisk(table)
|
||||
d.Id = field.NewUint(table, "id")
|
||||
d.ID = field.NewUint(table, "id")
|
||||
d.CreatedAt = field.NewTime(table, "created_at")
|
||||
d.UpdatedAt = field.NewTime(table, "updated_at")
|
||||
d.Name = field.NewString(table, "name")
|
||||
@ -136,7 +136,7 @@ func (d *document) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||
|
||||
func (d *document) fillFieldMap() {
|
||||
d.fieldMap = make(map[string]field.Expr, 11)
|
||||
d.fieldMap["id"] = d.Id
|
||||
d.fieldMap["id"] = d.ID
|
||||
d.fieldMap["created_at"] = d.CreatedAt
|
||||
d.fieldMap["updated_at"] = d.UpdatedAt
|
||||
d.fieldMap["name"] = d.Name
|
||||
|
@ -27,7 +27,7 @@ func newWorkspaceMember(db *gorm.DB, opts ...gen.DOOption) workspaceMember {
|
||||
|
||||
tableName := _workspaceMember.workspaceMemberDo.TableName()
|
||||
_workspaceMember.ALL = field.NewAsterisk(tableName)
|
||||
_workspaceMember.Id = field.NewUint(tableName, "id")
|
||||
_workspaceMember.ID = field.NewUint(tableName, "id")
|
||||
_workspaceMember.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
_workspaceMember.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||
_workspaceMember.WorkspaceId = field.NewUint(tableName, "workspace_id")
|
||||
@ -47,7 +47,7 @@ type workspaceMember struct {
|
||||
workspaceMemberDo
|
||||
|
||||
ALL field.Asterisk
|
||||
Id field.Uint
|
||||
ID field.Uint
|
||||
CreatedAt field.Time
|
||||
UpdatedAt field.Time
|
||||
WorkspaceId field.Uint
|
||||
@ -69,7 +69,7 @@ func (w workspaceMember) As(alias string) *workspaceMember {
|
||||
|
||||
func (w *workspaceMember) updateTableName(table string) *workspaceMember {
|
||||
w.ALL = field.NewAsterisk(table)
|
||||
w.Id = field.NewUint(table, "id")
|
||||
w.ID = field.NewUint(table, "id")
|
||||
w.CreatedAt = field.NewTime(table, "created_at")
|
||||
w.UpdatedAt = field.NewTime(table, "updated_at")
|
||||
w.WorkspaceId = field.NewUint(table, "workspace_id")
|
||||
@ -91,7 +91,7 @@ func (w *workspaceMember) GetFieldByName(fieldName string) (field.OrderExpr, boo
|
||||
|
||||
func (w *workspaceMember) fillFieldMap() {
|
||||
w.fieldMap = make(map[string]field.Expr, 6)
|
||||
w.fieldMap["id"] = w.Id
|
||||
w.fieldMap["id"] = w.ID
|
||||
w.fieldMap["created_at"] = w.CreatedAt
|
||||
w.fieldMap["updated_at"] = w.UpdatedAt
|
||||
w.fieldMap["workspace_id"] = w.WorkspaceId
|
||||
|
@ -27,7 +27,7 @@ func newWorkspace(db *gorm.DB, opts ...gen.DOOption) workspace {
|
||||
|
||||
tableName := _workspace.workspaceDo.TableName()
|
||||
_workspace.ALL = field.NewAsterisk(tableName)
|
||||
_workspace.Id = field.NewUint(tableName, "id")
|
||||
_workspace.ID = field.NewUint(tableName, "id")
|
||||
_workspace.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
_workspace.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||
_workspace.Name = field.NewString(tableName, "name")
|
||||
@ -43,7 +43,7 @@ type workspace struct {
|
||||
workspaceDo
|
||||
|
||||
ALL field.Asterisk
|
||||
Id field.Uint
|
||||
ID field.Uint
|
||||
CreatedAt field.Time
|
||||
UpdatedAt field.Time
|
||||
Name field.String
|
||||
@ -65,7 +65,7 @@ func (w workspace) As(alias string) *workspace {
|
||||
|
||||
func (w *workspace) updateTableName(table string) *workspace {
|
||||
w.ALL = field.NewAsterisk(table)
|
||||
w.Id = field.NewUint(table, "id")
|
||||
w.ID = field.NewUint(table, "id")
|
||||
w.CreatedAt = field.NewTime(table, "created_at")
|
||||
w.UpdatedAt = field.NewTime(table, "updated_at")
|
||||
w.Name = field.NewString(table, "name")
|
||||
@ -88,7 +88,7 @@ func (w *workspace) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||
|
||||
func (w *workspace) fillFieldMap() {
|
||||
w.fieldMap = make(map[string]field.Expr, 6)
|
||||
w.fieldMap["id"] = w.Id
|
||||
w.fieldMap["id"] = w.ID
|
||||
w.fieldMap["created_at"] = w.CreatedAt
|
||||
w.fieldMap["updated_at"] = w.UpdatedAt
|
||||
w.fieldMap["name"] = w.Name
|
||||
|
@ -1,6 +1,6 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE documents
|
||||
ADD COLUMN parent_id BIGINT REFERENCES documents (id);
|
||||
ADD COLUMN parent_id BIGINT REFERENCES documents (id) NULL;
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE documents
|
||||
|
@ -0,0 +1,10 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE document_blocks
|
||||
ADD COLUMN "order" DOUBLE PRECISION NOT NULL DEFAULT 1000.0;
|
||||
|
||||
CREATE INDEX idx_document_blocks_order ON document_blocks ("order");
|
||||
|
||||
-- +goose Down
|
||||
DROP INDEX IF EXISTS idx_document_blocks_order;
|
||||
ALTER TABLE document_blocks
|
||||
DROP COLUMN "order";
|
@ -16,8 +16,8 @@ type Document struct {
|
||||
CollectionId dto.EntityId `json:"collection_id"`
|
||||
Collection *Collection `json:"collection"`
|
||||
|
||||
ParentId dto.EntityId `json:"parent_id"`
|
||||
Parent *Document `json:"parent"`
|
||||
ParentId *dto.EntityId `json:"parent_id"`
|
||||
Parent *Document `json:"parent"`
|
||||
|
||||
DeletedAt gorm.DeletedAt `json:"deleted_at"`
|
||||
}
|
||||
@ -32,9 +32,10 @@ type DocumentBlock struct {
|
||||
DocumentId dto.EntityId `json:"document_id"`
|
||||
Document *Document `json:"document"`
|
||||
|
||||
Type string `json:"type"`
|
||||
Content string `json:"content"`
|
||||
Hash string `json:"hash"`
|
||||
Type string `json:"type"`
|
||||
Content string `json:"content"`
|
||||
Hash string `json:"hash"`
|
||||
Order float64 `json:"order" gorm:"index"`
|
||||
}
|
||||
|
||||
func (*DocumentBlock) TableName() string {
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
|
||||
// Model 是所有 entity 的基类,后期要将所有的 Base 改成这种形式
|
||||
type Model struct {
|
||||
Id dto.EntityId `gorm:"primarykey" json:"id"`
|
||||
ID dto.EntityId `gorm:"primarykey" json:"id"`
|
||||
CreatedAt time.Time `gorm:"autoUpdateTime:milli" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime:milli" json:"updated_at"`
|
||||
//DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
type Workspace struct {
|
||||
Model
|
||||
Name string `json:"name"`
|
||||
UserId user.Id `json:"user_id"`
|
||||
UserId user.ID `json:"user_id"`
|
||||
DeletedAt gorm.DeletedAt `json:"deleted_at"`
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ func (*Workspace) TableName() string {
|
||||
type WorkspaceMember struct {
|
||||
Model
|
||||
WorkspaceId dto.EntityId `json:"workspace_id"`
|
||||
UserId user.Id `json:"user_id"`
|
||||
UserId user.ID `json:"user_id"`
|
||||
Workspace *Workspace `json:"workspace"`
|
||||
}
|
||||
|
||||
|
@ -27,14 +27,14 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
func Struct(data interface{}) (validationErrors *[]dto.ValidateError, err error) {
|
||||
func Struct(data interface{}) (validationErrors *[]dto.ValidateError, ok bool, err error) {
|
||||
v := validate.Struct(data)
|
||||
|
||||
var e error
|
||||
var ves []dto.ValidateError
|
||||
|
||||
if v.Validate() {
|
||||
return &ves, e // 返回指针
|
||||
return &ves, true, nil // 返回指针
|
||||
} else {
|
||||
e = ErrValidationFailed
|
||||
|
||||
@ -60,5 +60,5 @@ func Struct(data interface{}) (validationErrors *[]dto.ValidateError, err error)
|
||||
// //}
|
||||
//}
|
||||
|
||||
return &ves, e
|
||||
return &ves, false, e
|
||||
}
|
||||
|
@ -5,25 +5,15 @@ import (
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/api/http"
|
||||
)
|
||||
|
||||
// 两种方法都可以
|
||||
//type Api struct {
|
||||
// User *v1.UserController
|
||||
//}
|
||||
|
||||
type Api struct {
|
||||
HttpHandler *http.Handlers
|
||||
Middleware *http.Middleware
|
||||
}
|
||||
|
||||
func NewApiRoute(
|
||||
//User *v1.UserController,
|
||||
HttpHandler *http.Handlers,
|
||||
Middleware *http.Middleware,
|
||||
) *Api {
|
||||
//return &Api{
|
||||
// User,
|
||||
//}
|
||||
|
||||
return &Api{
|
||||
HttpHandler,
|
||||
Middleware,
|
||||
@ -31,20 +21,61 @@ func NewApiRoute(
|
||||
}
|
||||
|
||||
func (a *Api) V1(r fiber.Router) {
|
||||
auth := r.Group("/api/v1")
|
||||
auth := r.Group("")
|
||||
{
|
||||
// 要求认证
|
||||
auth.Use(a.Middleware.Auth.Handler())
|
||||
|
||||
// RoutePermission 为权限验证
|
||||
//auth.Get("/ping", a.Middleware.RBAC.RoutePermission(), a.HttpHandler.User.Test)
|
||||
// 工作空间路由
|
||||
workspaces := auth.Group("/workspaces")
|
||||
{
|
||||
workspaces.Post("/", a.HttpHandler.Workspace.CreateWorkspace)
|
||||
workspaces.Get("/", a.HttpHandler.Workspace.ListWorkspaces)
|
||||
workspaces.Get("/:id", a.HttpHandler.Workspace.GetWorkspace)
|
||||
workspaces.Delete("/:id", a.HttpHandler.Workspace.DeleteWorkspace)
|
||||
|
||||
auth.Get("/ping", a.HttpHandler.User.Test)
|
||||
}
|
||||
|
||||
guest := r.Group("/api/v1")
|
||||
{
|
||||
guest.Get("/guest_ping", a.HttpHandler.User.Test)
|
||||
// 工作空间成员管理
|
||||
workspaces.Post("/:id/members", a.HttpHandler.Workspace.AddMember)
|
||||
workspaces.Delete("/:id/members/:user_id", a.HttpHandler.Workspace.RemoveMember)
|
||||
|
||||
// 工作空间下的集合
|
||||
workspaces.Get("/:workspace_id/collections", a.HttpHandler.Collection.ListCollections)
|
||||
}
|
||||
|
||||
// 集合路由
|
||||
collections := auth.Group("/collections")
|
||||
{
|
||||
collections.Post("/", a.HttpHandler.Collection.CreateCollection)
|
||||
collections.Get("/:id", a.HttpHandler.Collection.GetCollection)
|
||||
|
||||
// 集合下的文档
|
||||
collections.Get("/:collection_id/documents", a.HttpHandler.Document.ListDocuments)
|
||||
}
|
||||
|
||||
// 文档路由
|
||||
documents := auth.Group("/documents")
|
||||
{
|
||||
documents.Post("/", a.HttpHandler.Document.CreateDocument)
|
||||
documents.Get("/:id", a.HttpHandler.Document.GetDocument)
|
||||
documents.Put("/:id", a.HttpHandler.Document.UpdateDocument)
|
||||
documents.Delete("/:id", a.HttpHandler.Document.DeleteDocument)
|
||||
|
||||
// 文档块
|
||||
documents.Post("/:document_id/blocks", a.HttpHandler.Document.CreateBlock)
|
||||
documents.Get("/:document_id/blocks", a.HttpHandler.Document.ListBlocks)
|
||||
}
|
||||
|
||||
// 文档块路由
|
||||
blocks := auth.Group("/blocks")
|
||||
{
|
||||
blocks.Put("/:block_id", a.HttpHandler.Document.UpdateBlock)
|
||||
blocks.Delete("/:block_id", a.HttpHandler.Document.DeleteBlock)
|
||||
blocks.Post("/:block_id/move", a.HttpHandler.Document.MoveBlock)
|
||||
}
|
||||
}
|
||||
|
||||
// guest := r.Group("/api/v1")
|
||||
// {
|
||||
// guest.Get("/guest_ping", a.HttpHandler.User.Test)
|
||||
// }
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ func (a *Service) GetUser(ctx *fiber.Ctx) *user.User {
|
||||
userCtx := ctx.Locals(constants.AuthMiddlewareKey)
|
||||
|
||||
u, ok := userCtx.(*user.User)
|
||||
u.Id = u.Token.Sub
|
||||
u.ID = u.Token.Sub
|
||||
|
||||
if !ok {
|
||||
panic("User context is not valid")
|
||||
@ -40,7 +40,7 @@ func (a *Service) GetCtx(ctx context.Context) *user.User {
|
||||
userCtx := ctx.Value(constants.AuthMiddlewareKey)
|
||||
|
||||
u, ok := userCtx.(*user.User)
|
||||
u.Id = u.Token.Sub
|
||||
u.ID = u.Token.Sub
|
||||
|
||||
if !ok {
|
||||
panic("User context is not valid")
|
||||
@ -53,7 +53,7 @@ func (a *Service) GetUserSafe(ctx *fiber.Ctx) (*user.User, bool) {
|
||||
userCtx := ctx.Locals(constants.AuthMiddlewareKey)
|
||||
|
||||
u, ok := userCtx.(*user.User)
|
||||
u.Id = u.Token.Sub
|
||||
u.ID = u.Token.Sub
|
||||
|
||||
return u, ok
|
||||
}
|
||||
@ -62,7 +62,7 @@ func (a *Service) GetCtxSafe(ctx context.Context) (*user.User, bool) {
|
||||
userCtx := ctx.Value(constants.AuthMiddlewareKey)
|
||||
|
||||
u, ok := userCtx.(*user.User)
|
||||
u.Id = u.Token.Sub
|
||||
u.ID = u.Token.Sub
|
||||
|
||||
return u, ok
|
||||
}
|
||||
@ -90,7 +90,7 @@ func (a *Service) parseUserJWT(tokenType constants.JwtTokenTypes, jwtToken strin
|
||||
return nil, errs.NotValidToken
|
||||
}
|
||||
|
||||
sub = user.Id(subStr)
|
||||
sub = user.ID(subStr)
|
||||
|
||||
// 如果 token.Header 中没有 typ
|
||||
if token.Header["typ"] == "" {
|
||||
|
@ -19,11 +19,11 @@ func (s *Service) Get(ctx context.Context, collectionId dto.EntityId) (*entity.C
|
||||
return nil, errs.ErrCollectionNotExists
|
||||
}
|
||||
|
||||
return s.dao.WithContext(ctx).Collection.Where(s.dao.Collection.Id.Eq(collectionId.Uint())).First()
|
||||
return s.dao.WithContext(ctx).Collection.Where(s.dao.Collection.ID.Eq(collectionId.Uint())).First()
|
||||
}
|
||||
|
||||
func (s *Service) Exists(ctx context.Context, CollectionId dto.EntityId) (bool, error) {
|
||||
num, err := s.dao.WithContext(ctx).Collection.Where(s.dao.Collection.Id.Eq(CollectionId.Uint())).Count()
|
||||
num, err := s.dao.WithContext(ctx).Collection.Where(s.dao.Collection.ID.Eq(CollectionId.Uint())).Count()
|
||||
|
||||
return num > 0, err
|
||||
}
|
||||
@ -47,59 +47,32 @@ func (s *Service) Create(ctx context.Context, workspaceId dto.EntityId, name str
|
||||
return collection, nil
|
||||
}
|
||||
|
||||
//
|
||||
//func (s *Service) AddMember(ctx context.Context, userId schema.UserId, CollectionEntity *entity.Collection) (*entity.CollectionMember, error) {
|
||||
// memberExists, err := s.MemberExists(ctx, userId, CollectionEntity)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// if memberExists {
|
||||
// return nil, errs.ErrMemberAlreadyExists
|
||||
// }
|
||||
//
|
||||
// var CollectionMember = &entity.CollectionMember{
|
||||
// UserId: userId,
|
||||
// CollectionId: CollectionEntity.Id,
|
||||
// }
|
||||
//
|
||||
// err = s.dao.WithContext(ctx).CollectionMember.Create(CollectionMember)
|
||||
//
|
||||
// return CollectionMember, err
|
||||
//}
|
||||
//
|
||||
//func (s *Service) RemoveMember(ctx context.Context, userId schema.UserId, CollectionEntity *entity.Collection) error {
|
||||
// memberExists, err := s.MemberExists(ctx, userId, CollectionEntity)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// if !memberExists {
|
||||
// return errs.ErrMemberNotExists
|
||||
// }
|
||||
//
|
||||
// _, err = s.dao.WithContext(ctx).CollectionMember.
|
||||
// Where(
|
||||
// s.dao.CollectionMember.UserId.Eq(userId.String()),
|
||||
// s.dao.CollectionMember.CollectionId.Eq(
|
||||
// CollectionEntity.Id.Uint()),
|
||||
// ).
|
||||
// Delete()
|
||||
//
|
||||
// return err
|
||||
//}
|
||||
//
|
||||
//func (s *Service) MemberExists(ctx context.Context, userId schema.UserId, CollectionEntity *entity.Collection) (bool, error) {
|
||||
// if num, err := s.dao.WithContext(ctx).CollectionMember.Where(s.dao.CollectionMember.UserId.Eq(userId.String()), s.dao.CollectionMember.CollectionId.Eq(CollectionEntity.Id.Uint())).Count(); err != nil {
|
||||
// return false, err
|
||||
// } else {
|
||||
// return num > 0, nil
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (s *Service) DeleteCollection(ctx context.Context, CollectionId dto.EntityId) error {
|
||||
// // 删除始终是一个复杂的工程,所以先标记为软删除
|
||||
// _, err := s.dao.WithContext(ctx).Collection.Where(s.dao.Collection.Id.Eq(CollectionId.Uint())).Delete()
|
||||
//
|
||||
// return err
|
||||
//}
|
||||
func (s *Service) Update(ctx context.Context, collectionId dto.EntityId, name string) (*entity.Collection, error) {
|
||||
collection, err := s.Get(ctx, collectionId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
collection.Name = name
|
||||
err = s.dao.WithContext(ctx).Collection.Save(collection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return collection, nil
|
||||
}
|
||||
|
||||
func (s *Service) Delete(ctx context.Context, collectionId dto.EntityId) error {
|
||||
exists, err := s.Exists(ctx, collectionId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return errs.ErrCollectionNotExists
|
||||
}
|
||||
|
||||
// 使用软删除
|
||||
_, err = s.dao.WithContext(ctx).Collection.Where(s.dao.Collection.ID.Eq(collectionId.Uint())).Delete()
|
||||
return err
|
||||
}
|
||||
|
@ -1,105 +0,0 @@
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/entity"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/types/dto"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/types/errs"
|
||||
)
|
||||
|
||||
func (s *Service) Get(ctx context.Context, collectionId dto.EntityId) (*entity.Collection, error) {
|
||||
exists, err := s.Exists(ctx, collectionId)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return nil, errs.ErrCollectionNotExists
|
||||
}
|
||||
|
||||
return s.dao.WithContext(ctx).Collection.Where(s.dao.Collection.Id.Eq(collectionId.Uint())).First()
|
||||
}
|
||||
|
||||
func (s *Service) Exists(ctx context.Context, CollectionId dto.EntityId) (bool, error) {
|
||||
num, err := s.dao.WithContext(ctx).Collection.Where(s.dao.Collection.Id.Eq(CollectionId.Uint())).Count()
|
||||
|
||||
return num > 0, err
|
||||
}
|
||||
|
||||
func (s *Service) List(ctx context.Context, workspaceId dto.EntityId) ([]*entity.Collection, error) {
|
||||
return s.dao.WithContext(ctx).Collection.Where(s.dao.Collection.WorkspaceId.Eq(workspaceId.Uint())).Find()
|
||||
}
|
||||
|
||||
func (s *Service) Create(ctx context.Context, workspaceId dto.EntityId, name string) (*entity.Collection, error) {
|
||||
var collection = &entity.Collection{
|
||||
Name: name,
|
||||
WorkspaceId: workspaceId,
|
||||
}
|
||||
|
||||
err := s.dao.WithContext(ctx).Collection.Create(collection)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return collection, nil
|
||||
}
|
||||
|
||||
//
|
||||
//func (s *Service) AddMember(ctx context.Context, userId schema.UserId, CollectionEntity *entity.Collection) (*entity.CollectionMember, error) {
|
||||
// memberExists, err := s.MemberExists(ctx, userId, CollectionEntity)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// if memberExists {
|
||||
// return nil, errs.ErrMemberAlreadyExists
|
||||
// }
|
||||
//
|
||||
// var CollectionMember = &entity.CollectionMember{
|
||||
// UserId: userId,
|
||||
// CollectionId: CollectionEntity.Id,
|
||||
// }
|
||||
//
|
||||
// err = s.dao.WithContext(ctx).CollectionMember.Create(CollectionMember)
|
||||
//
|
||||
// return CollectionMember, err
|
||||
//}
|
||||
//
|
||||
//func (s *Service) RemoveMember(ctx context.Context, userId schema.UserId, CollectionEntity *entity.Collection) error {
|
||||
// memberExists, err := s.MemberExists(ctx, userId, CollectionEntity)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// if !memberExists {
|
||||
// return errs.ErrMemberNotExists
|
||||
// }
|
||||
//
|
||||
// _, err = s.dao.WithContext(ctx).CollectionMember.
|
||||
// Where(
|
||||
// s.dao.CollectionMember.UserId.Eq(userId.String()),
|
||||
// s.dao.CollectionMember.CollectionId.Eq(
|
||||
// CollectionEntity.Id.Uint()),
|
||||
// ).
|
||||
// Delete()
|
||||
//
|
||||
// return err
|
||||
//}
|
||||
//
|
||||
//func (s *Service) MemberExists(ctx context.Context, userId schema.UserId, CollectionEntity *entity.Collection) (bool, error) {
|
||||
// if num, err := s.dao.WithContext(ctx).CollectionMember.Where(s.dao.CollectionMember.UserId.Eq(userId.String()), s.dao.CollectionMember.CollectionId.Eq(CollectionEntity.Id.Uint())).Count(); err != nil {
|
||||
// return false, err
|
||||
// } else {
|
||||
// return num > 0, nil
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (s *Service) DeleteCollection(ctx context.Context, CollectionId dto.EntityId) error {
|
||||
// // 删除始终是一个复杂的工程,所以先标记为软删除
|
||||
// _, err := s.dao.WithContext(ctx).Collection.Where(s.dao.Collection.Id.Eq(CollectionId.Uint())).Delete()
|
||||
//
|
||||
// return err
|
||||
//}
|
256
internal/services/document/document.go
Normal file
256
internal/services/document/document.go
Normal file
@ -0,0 +1,256 @@
|
||||
package document
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/entity"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/types/dto"
|
||||
)
|
||||
|
||||
// Get 获取文档
|
||||
func (s *Service) Get(ctx context.Context, id dto.EntityId) (*entity.Document, error) {
|
||||
return s.dao.Document.Where(s.dao.Document.ID.Eq(uint(id))).First()
|
||||
}
|
||||
|
||||
// Create 创建文档
|
||||
func (s *Service) Create(ctx context.Context, name string, workspaceId, collectionId dto.EntityId, parentId *dto.EntityId) (*entity.Document, error) {
|
||||
doc := &entity.Document{
|
||||
Name: name,
|
||||
WorkspaceId: workspaceId,
|
||||
CollectionId: collectionId,
|
||||
ParentId: parentId,
|
||||
}
|
||||
|
||||
err := s.dao.Document.Create(doc)
|
||||
return doc, err
|
||||
}
|
||||
|
||||
// Update 更新文档
|
||||
func (s *Service) Update(ctx context.Context, id dto.EntityId, name string) (*entity.Document, error) {
|
||||
doc, err := s.Get(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
doc.Name = name
|
||||
err = s.dao.Document.Save(doc)
|
||||
return doc, err
|
||||
}
|
||||
|
||||
// Delete 删除文档
|
||||
func (s *Service) Delete(ctx context.Context, id dto.EntityId) error {
|
||||
_, err := s.dao.Document.Where(s.dao.Document.ID.Eq(uint(id))).Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
// ListDocuments 列出文档树
|
||||
func (s *Service) ListDocuments(ctx context.Context, collectionId dto.EntityId, parentId *dto.EntityId) ([]*entity.Document, error) {
|
||||
q := s.dao.WithContext(ctx).Document.
|
||||
Where(s.dao.Document.CollectionId.Eq(collectionId.Uint())).
|
||||
Order(s.dao.Document.CreatedAt)
|
||||
|
||||
if parentId == nil {
|
||||
// 获取根文档
|
||||
q = q.Where(s.dao.Document.ParentId.IsNull())
|
||||
} else {
|
||||
// 获取指定父文档下的子文档
|
||||
q = q.Where(s.dao.Document.ParentId.Eq(parentId.Uint()))
|
||||
}
|
||||
|
||||
return q.Find()
|
||||
}
|
||||
|
||||
// generateBlockHash 生成块的哈希值
|
||||
func (s *Service) generateBlockHash(documentId dto.EntityId, blockType string, content string, timestamp time.Time) string {
|
||||
// 组合所有相关信息
|
||||
data := fmt.Sprintf("%d:%s:%s:%d", documentId, blockType, content, timestamp.UnixNano())
|
||||
|
||||
// 计算 SHA-256 摘要
|
||||
hash := sha256.Sum256([]byte(data))
|
||||
|
||||
// 转换为十六进制字符串
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
// CreateBlock 创建文档块
|
||||
func (s *Service) CreateBlock(ctx context.Context, documentId dto.EntityId, blockType string, content string, afterBlockId *dto.EntityId) (*entity.DocumentBlock, error) {
|
||||
// 生成块的哈希值
|
||||
now := time.Now()
|
||||
hash := s.generateBlockHash(documentId, blockType, content, now)
|
||||
|
||||
var order float64
|
||||
if afterBlockId == nil {
|
||||
order = 1000.0
|
||||
} else {
|
||||
// 获取前一个块
|
||||
prevBlock, err := s.dao.WithContext(ctx).DocumentBlock.Where(s.dao.DocumentBlock.ID.Eq(afterBlockId.Uint())).First()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取前一个块后面的第一个块
|
||||
nextBlock, err := s.dao.WithContext(ctx).DocumentBlock.
|
||||
Where(s.dao.DocumentBlock.DocumentId.Eq(uint(documentId))).
|
||||
Where(s.dao.DocumentBlock.Order_.Gt(prevBlock.Order)).
|
||||
Order(s.dao.DocumentBlock.Order_).
|
||||
First()
|
||||
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
// 如果没有后续块,则在前一个块的序号上加 1000
|
||||
order = prevBlock.Order + 1000.0
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
// 如果有后续块,则取前一个块和后一个块序号的中间值
|
||||
order = (prevBlock.Order + nextBlock.Order) / 2
|
||||
}
|
||||
}
|
||||
|
||||
block := &entity.DocumentBlock{
|
||||
DocumentId: documentId,
|
||||
Type: blockType,
|
||||
Content: content,
|
||||
Order: order,
|
||||
Hash: hash,
|
||||
}
|
||||
|
||||
err := s.dao.WithContext(ctx).DocumentBlock.Create(block)
|
||||
return block, err
|
||||
}
|
||||
|
||||
// UpdateBlock 更新文档块
|
||||
func (s *Service) UpdateBlock(ctx context.Context, blockId dto.EntityId, content string) (*entity.DocumentBlock, error) {
|
||||
block, err := s.dao.WithContext(ctx).DocumentBlock.Where(s.dao.DocumentBlock.ID.Eq(blockId.Uint())).First()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 更新内容和哈希
|
||||
now := time.Now()
|
||||
block.Content = content
|
||||
block.Hash = s.generateBlockHash(block.DocumentId, block.Type, content, now)
|
||||
err = s.dao.WithContext(ctx).DocumentBlock.Save(block)
|
||||
return block, err
|
||||
}
|
||||
|
||||
// DeleteBlock 删除文档块
|
||||
func (s *Service) DeleteBlock(ctx context.Context, blockId dto.EntityId) error {
|
||||
_, err := s.dao.WithContext(ctx).DocumentBlock.Where(s.dao.DocumentBlock.ID.Eq(blockId.Uint())).Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
// ListBlocks 列出文档的所有块
|
||||
func (s *Service) ListBlocks(ctx context.Context, documentId dto.EntityId) ([]*entity.DocumentBlock, error) {
|
||||
return s.dao.WithContext(ctx).DocumentBlock.
|
||||
Where(s.dao.DocumentBlock.DocumentId.Eq(uint(documentId))).
|
||||
Order(s.dao.DocumentBlock.Order_).
|
||||
Find()
|
||||
}
|
||||
|
||||
// MoveBlock 移动文档块
|
||||
func (s *Service) MoveBlock(ctx context.Context, blockId dto.EntityId, afterBlockId *dto.EntityId) error {
|
||||
block, err := s.dao.WithContext(ctx).DocumentBlock.Where(s.dao.DocumentBlock.ID.Eq(blockId.Uint())).First()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var newOrder float64
|
||||
if afterBlockId == nil {
|
||||
newOrder = 1000.0
|
||||
} else {
|
||||
// 获取目标位置的前一个块
|
||||
prevBlock, err := s.dao.WithContext(ctx).DocumentBlock.Where(s.dao.DocumentBlock.ID.Eq(afterBlockId.Uint())).First()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 获取目标位置的后一个块
|
||||
nextBlock, err := s.dao.WithContext(ctx).DocumentBlock.
|
||||
Where(s.dao.DocumentBlock.DocumentId.Eq(uint(block.DocumentId))).
|
||||
Where(s.dao.DocumentBlock.Order_.Gt(prevBlock.Order)).
|
||||
Order(s.dao.DocumentBlock.Order_).
|
||||
First()
|
||||
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
newOrder = prevBlock.Order + 1000.0
|
||||
} else if err != nil {
|
||||
return err
|
||||
} else {
|
||||
newOrder = (prevBlock.Order + nextBlock.Order) / 2
|
||||
}
|
||||
}
|
||||
|
||||
block.Order = newOrder
|
||||
// 移动后重新生成 Hash
|
||||
now := time.Now()
|
||||
block.Hash = s.generateBlockHash(block.DocumentId, block.Type, block.Content, now)
|
||||
return s.dao.WithContext(ctx).DocumentBlock.Save(block)
|
||||
}
|
||||
|
||||
// ReorderBlocks 重新排序文档块
|
||||
func (s *Service) ReorderBlocks(ctx context.Context, documentId dto.EntityId) error {
|
||||
blocks, err := s.ListBlocks(ctx, documentId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 重新设置每个块的顺序
|
||||
for i, block := range blocks {
|
||||
block.Order = float64((i + 1) * 1000)
|
||||
// 重新生成 Hash
|
||||
now := time.Now()
|
||||
block.Hash = s.generateBlockHash(block.DocumentId, block.Type, block.Content, now)
|
||||
if err := s.dao.WithContext(ctx).DocumentBlock.Save(block); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasChildren 检查文档是否有子文档
|
||||
func (s *Service) HasChildren(ctx context.Context, id dto.EntityId) (bool, error) {
|
||||
count, err := s.dao.Document.Where(s.dao.Document.ParentId.Eq(id.Uint())).Count()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
// ToDTO 将文档实体转换为 DTO
|
||||
func (s *Service) ToDTO(ctx context.Context, doc *entity.Document) (*dto.DocumentDTO, error) {
|
||||
hasChildren, err := s.HasChildren(ctx, doc.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dto.DocumentDTO{
|
||||
BaseDTO: dto.BaseDTO{
|
||||
ID: doc.ID,
|
||||
CreatedAt: doc.CreatedAt,
|
||||
UpdatedAt: doc.UpdatedAt,
|
||||
},
|
||||
Name: doc.Name,
|
||||
WorkspaceID: doc.WorkspaceId,
|
||||
CollectionID: doc.CollectionId,
|
||||
ParentID: doc.ParentId,
|
||||
HasChildren: hasChildren,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// List 列出文档
|
||||
func (s *Service) List(ctx context.Context, collectionId dto.EntityId, parentId *dto.EntityId) ([]*entity.Document, error) {
|
||||
q := s.dao.Document.Where(s.dao.Document.CollectionId.Eq(uint(collectionId)))
|
||||
if parentId != nil {
|
||||
q = q.Where(s.dao.Document.ParentId.Eq(uint(*parentId)))
|
||||
} else {
|
||||
q = q.Where(s.dao.Document.ParentId.IsNull())
|
||||
}
|
||||
|
||||
return q.Find()
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package collection
|
||||
package document
|
||||
|
||||
import (
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/base/conf"
|
||||
|
@ -3,23 +3,32 @@ package services
|
||||
import (
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/base/logger"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/auth"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/collection"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/document"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/jwks"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/stream"
|
||||
"leafdev.top/Leaf/leaf-library-3/internal/services/workspace"
|
||||
|
||||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
logger *logger.Logger
|
||||
Jwks *jwks.JWKS
|
||||
Auth *auth.Service
|
||||
Stream *stream.Service
|
||||
logger *logger.Logger
|
||||
Jwks *jwks.JWKS
|
||||
Auth *auth.Service
|
||||
Stream *stream.Service
|
||||
Workspace *workspace.Service
|
||||
Collection *collection.Service
|
||||
Document *document.Service
|
||||
}
|
||||
|
||||
var Provide = wire.NewSet(
|
||||
jwks.NewJWKS,
|
||||
auth.NewService,
|
||||
stream.NewService,
|
||||
workspace.NewService,
|
||||
collection.NewService,
|
||||
document.NewService,
|
||||
NewService,
|
||||
)
|
||||
|
||||
@ -28,11 +37,17 @@ func NewService(
|
||||
jwks *jwks.JWKS,
|
||||
auth *auth.Service,
|
||||
stream *stream.Service,
|
||||
workspace *workspace.Service,
|
||||
collection *collection.Service,
|
||||
document *document.Service,
|
||||
) *Service {
|
||||
return &Service{
|
||||
logger,
|
||||
jwks,
|
||||
auth,
|
||||
stream,
|
||||
workspace,
|
||||
collection,
|
||||
document,
|
||||
}
|
||||
}
|
||||
|
@ -20,20 +20,20 @@ func (s *Service) Get(ctx context.Context, workspaceId dto.EntityId) (*entity.Wo
|
||||
return nil, errs.ErrWorkspaceNotExists
|
||||
}
|
||||
|
||||
return s.dao.WithContext(ctx).Workspace.Where(s.dao.Workspace.Id.Eq(workspaceId.Uint())).First()
|
||||
return s.dao.WithContext(ctx).Workspace.Where(s.dao.Workspace.ID.Eq(workspaceId.Uint())).First()
|
||||
}
|
||||
|
||||
func (s *Service) Exists(ctx context.Context, workspaceId dto.EntityId) (bool, error) {
|
||||
num, err := s.dao.WithContext(ctx).Workspace.Where(s.dao.Workspace.Id.Eq(workspaceId.Uint())).Count()
|
||||
num, err := s.dao.WithContext(ctx).Workspace.Where(s.dao.Workspace.ID.Eq(workspaceId.Uint())).Count()
|
||||
|
||||
return num > 0, err
|
||||
}
|
||||
|
||||
func (s *Service) List(ctx context.Context, userId user.Id) ([]*entity.Workspace, error) {
|
||||
func (s *Service) List(ctx context.Context, userId user.ID) ([]*entity.Workspace, error) {
|
||||
return s.dao.WithContext(ctx).Workspace.Where(s.dao.Workspace.UserId.Eq(userId.String())).Find()
|
||||
}
|
||||
|
||||
func (s *Service) Create(ctx context.Context, userId user.Id, name string) (*entity.Workspace, error) {
|
||||
func (s *Service) Create(ctx context.Context, userId user.ID, name string) (*entity.Workspace, error) {
|
||||
var workspace = &entity.Workspace{
|
||||
Name: name,
|
||||
UserId: userId,
|
||||
@ -54,7 +54,7 @@ func (s *Service) Create(ctx context.Context, userId user.Id, name string) (*ent
|
||||
return workspace, nil
|
||||
}
|
||||
|
||||
func (s *Service) AddMember(ctx context.Context, userId user.Id, workspaceEntity *entity.Workspace) (*entity.WorkspaceMember, error) {
|
||||
func (s *Service) AddMember(ctx context.Context, userId user.ID, workspaceEntity *entity.Workspace) (*entity.WorkspaceMember, error) {
|
||||
memberExists, err := s.MemberExists(ctx, userId, workspaceEntity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -66,7 +66,7 @@ func (s *Service) AddMember(ctx context.Context, userId user.Id, workspaceEntity
|
||||
|
||||
var workspaceMember = &entity.WorkspaceMember{
|
||||
UserId: userId,
|
||||
WorkspaceId: workspaceEntity.Id,
|
||||
WorkspaceId: workspaceEntity.ID,
|
||||
}
|
||||
|
||||
err = s.dao.WithContext(ctx).WorkspaceMember.Create(workspaceMember)
|
||||
@ -74,7 +74,7 @@ func (s *Service) AddMember(ctx context.Context, userId user.Id, workspaceEntity
|
||||
return workspaceMember, err
|
||||
}
|
||||
|
||||
func (s *Service) RemoveMember(ctx context.Context, userId user.Id, workspaceEntity *entity.Workspace) error {
|
||||
func (s *Service) RemoveMember(ctx context.Context, userId user.ID, workspaceEntity *entity.Workspace) error {
|
||||
memberExists, err := s.MemberExists(ctx, userId, workspaceEntity)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -88,15 +88,15 @@ func (s *Service) RemoveMember(ctx context.Context, userId user.Id, workspaceEnt
|
||||
Where(
|
||||
s.dao.WorkspaceMember.UserId.Eq(userId.String()),
|
||||
s.dao.WorkspaceMember.WorkspaceId.Eq(
|
||||
workspaceEntity.Id.Uint()),
|
||||
workspaceEntity.ID.Uint()),
|
||||
).
|
||||
Delete()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Service) MemberExists(ctx context.Context, userId user.Id, workspaceEntity *entity.Workspace) (bool, error) {
|
||||
if num, err := s.dao.WithContext(ctx).WorkspaceMember.Where(s.dao.WorkspaceMember.UserId.Eq(userId.String()), s.dao.WorkspaceMember.WorkspaceId.Eq(workspaceEntity.Id.Uint())).Count(); err != nil {
|
||||
func (s *Service) MemberExists(ctx context.Context, userId user.ID, workspaceEntity *entity.Workspace) (bool, error) {
|
||||
if num, err := s.dao.WithContext(ctx).WorkspaceMember.Where(s.dao.WorkspaceMember.UserId.Eq(userId.String()), s.dao.WorkspaceMember.WorkspaceId.Eq(workspaceEntity.ID.Uint())).Count(); err != nil {
|
||||
return false, err
|
||||
} else {
|
||||
return num > 0, nil
|
||||
@ -105,7 +105,12 @@ func (s *Service) MemberExists(ctx context.Context, userId user.Id, workspaceEnt
|
||||
|
||||
func (s *Service) DeleteWorkspace(ctx context.Context, workspaceId dto.EntityId) error {
|
||||
// 删除始终是一个复杂的工程,所以先标记为软删除
|
||||
_, err := s.dao.WithContext(ctx).Workspace.Where(s.dao.Workspace.Id.Eq(workspaceId.Uint())).Delete()
|
||||
_, err := s.dao.WithContext(ctx).Workspace.Where(s.dao.Workspace.ID.Eq(workspaceId.Uint())).Delete()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// IsMember 检查用户是否是工作空间的成员
|
||||
func (s *Service) IsMember(ctx context.Context, userId user.ID, workspace *entity.Workspace) (bool, error) {
|
||||
return s.MemberExists(ctx, userId, workspace)
|
||||
}
|
||||
|
10
internal/types/dto/base.go
Normal file
10
internal/types/dto/base.go
Normal file
@ -0,0 +1,10 @@
|
||||
package dto
|
||||
|
||||
import "time"
|
||||
|
||||
// BaseDTO 基础数据传输对象
|
||||
type BaseDTO struct {
|
||||
ID EntityId `json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
22
internal/types/dto/collection.go
Normal file
22
internal/types/dto/collection.go
Normal file
@ -0,0 +1,22 @@
|
||||
package dto
|
||||
|
||||
// CreateCollectionRequest 创建集合请求
|
||||
type CreateCollectionRequest struct {
|
||||
Name string `json:"name" validate:"required|minLen:1"`
|
||||
WorkspaceID EntityId `json:"workspace_id" validate:"required"`
|
||||
}
|
||||
|
||||
// GetCollectionRequest 获取集合请求
|
||||
type GetCollectionRequest struct {
|
||||
ID EntityId `params:"id"`
|
||||
}
|
||||
|
||||
// DeleteCollectionRequest 删除集合请求
|
||||
type DeleteCollectionRequest struct {
|
||||
ID EntityId `params:"id"`
|
||||
}
|
||||
|
||||
// ListCollectionsRequest 列出工作空间下的集合请求
|
||||
type ListCollectionsRequest struct {
|
||||
WorkspaceID EntityId `params:"workspace_id"`
|
||||
}
|
71
internal/types/dto/document.go
Normal file
71
internal/types/dto/document.go
Normal file
@ -0,0 +1,71 @@
|
||||
package dto
|
||||
|
||||
// CreateDocumentRequest 创建文档请求
|
||||
type CreateDocumentRequest struct {
|
||||
Name string `json:"name" validate:"required|minLen:1"`
|
||||
WorkspaceID EntityId `json:"workspace_id" validate:"required"`
|
||||
CollectionID EntityId `json:"collection_id" validate:"required"`
|
||||
ParentID *EntityId `json:"parent_id,omitempty"`
|
||||
}
|
||||
|
||||
// GetDocumentRequest 获取文档请求
|
||||
type GetDocumentRequest struct {
|
||||
ID EntityId `params:"id"`
|
||||
}
|
||||
|
||||
// DeleteDocumentRequest 删除文档请求
|
||||
type DeleteDocumentRequest struct {
|
||||
ID EntityId `params:"id"`
|
||||
}
|
||||
|
||||
// UpdateDocumentRequest 更新文档请求
|
||||
type UpdateDocumentRequest struct {
|
||||
Name string `json:"name" validate:"required|minLen:1"`
|
||||
}
|
||||
|
||||
// ListDocumentsPathParam 列出文档的路径参数
|
||||
type ListDocumentsPathParam struct {
|
||||
CollectionID EntityId `params:"collection_id"`
|
||||
}
|
||||
|
||||
// ListDocumentsQueryParam 列出文档的查询参数
|
||||
type ListDocumentsQueryParam struct {
|
||||
ParentID *EntityId `query:"parent_id,omitempty"`
|
||||
}
|
||||
|
||||
// CreateBlockRequest 创建文档块请求
|
||||
type CreateBlockRequest struct {
|
||||
Type string `json:"type" validate:"required"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
AfterBlockID *EntityId `json:"after_block_id,omitempty"`
|
||||
}
|
||||
|
||||
// GetBlockRequest 获取文档块请求
|
||||
type GetBlockRequest struct {
|
||||
ID EntityId `params:"block_id"`
|
||||
}
|
||||
|
||||
// UpdateBlockRequest 更新文档块请求
|
||||
type UpdateBlockRequest struct {
|
||||
Content string `json:"content" validate:"required"`
|
||||
}
|
||||
|
||||
// MoveBlockRequest 移动文档块请求
|
||||
type MoveBlockRequest struct {
|
||||
AfterBlockID *EntityId `json:"after_block_id,omitempty"`
|
||||
}
|
||||
|
||||
// ListBlocksRequest 列出文档下的块请求
|
||||
type ListBlocksRequest struct {
|
||||
DocumentID EntityId `params:"document_id"`
|
||||
}
|
||||
|
||||
// DocumentDTO 文档数据传输对象
|
||||
type DocumentDTO struct {
|
||||
BaseDTO
|
||||
Name string `json:"name"`
|
||||
WorkspaceID EntityId `json:"workspace_id"`
|
||||
CollectionID EntityId `json:"collection_id"`
|
||||
ParentID *EntityId `json:"parent_id,omitempty"`
|
||||
HasChildren bool `json:"has_children"`
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"net/http"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type IError interface {
|
||||
@ -23,14 +24,14 @@ type Body struct {
|
||||
Wrap bool `json:"-"`
|
||||
}
|
||||
|
||||
type HttpResponse struct {
|
||||
type Response struct {
|
||||
body *Body
|
||||
httpStatus int
|
||||
ctx *fiber.Ctx
|
||||
}
|
||||
|
||||
func Ctx(c *fiber.Ctx) *HttpResponse {
|
||||
return &HttpResponse{
|
||||
func Ctx(c *fiber.Ctx) *Response {
|
||||
return &Response{
|
||||
body: &Body{
|
||||
Wrap: true,
|
||||
},
|
||||
@ -39,7 +40,7 @@ func Ctx(c *fiber.Ctx) *HttpResponse {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *HttpResponse) Message(message string) *HttpResponse {
|
||||
func (r *Response) Message(message string) *Response {
|
||||
r.body.Message = message
|
||||
|
||||
if r.httpStatus == 0 {
|
||||
@ -50,24 +51,24 @@ func (r *HttpResponse) Message(message string) *HttpResponse {
|
||||
}
|
||||
|
||||
// WithoutWrap 将不在 body 中包裹 data
|
||||
func (r *HttpResponse) WithoutWrap() *HttpResponse {
|
||||
func (r *Response) WithoutWrap() *Response {
|
||||
r.body.Wrap = false
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *HttpResponse) Wrap() *HttpResponse {
|
||||
func (r *Response) Wrap() *Response {
|
||||
r.body.Wrap = true
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *HttpResponse) Data(data any) *HttpResponse {
|
||||
func (r *Response) Data(data any) *Response {
|
||||
r.body.Data = data
|
||||
return r
|
||||
|
||||
}
|
||||
|
||||
func (r *HttpResponse) Error(err IError) *HttpResponse {
|
||||
func (r *Response) Error(err IError) *Response {
|
||||
if err != nil {
|
||||
r.body.Error = err.Error()
|
||||
|
||||
@ -86,13 +87,13 @@ func (r *HttpResponse) Error(err IError) *HttpResponse {
|
||||
|
||||
}
|
||||
|
||||
func (r *HttpResponse) Status(status int) *HttpResponse {
|
||||
func (r *Response) Status(status int) *Response {
|
||||
r.httpStatus = status
|
||||
return r
|
||||
|
||||
}
|
||||
|
||||
func (r *HttpResponse) Send() error {
|
||||
func (r *Response) Send() error {
|
||||
if r.httpStatus == 0 {
|
||||
r.httpStatus = http.StatusOK
|
||||
}
|
||||
@ -114,7 +115,18 @@ func (r *HttpResponse) Send() error {
|
||||
return r.ctx.Status(r.httpStatus).JSON(r.body.Data)
|
||||
}
|
||||
|
||||
//func (r *HttpResponse) ValidationError(validationErrors *[]ValidateError) *HttpResponse {
|
||||
func (r *Response) Success(data any) *Response {
|
||||
r.body.Data = data
|
||||
r.body.Success = true
|
||||
|
||||
if r.httpStatus == 0 {
|
||||
r.httpStatus = http.StatusOK
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
//func (r *Response) ValidationError(validationErrors *[]ValidateError) *Response {
|
||||
// if validationErrors == nil || len(*validationErrors) == 0 {
|
||||
// }
|
||||
//
|
||||
|
27
internal/types/dto/params.go
Normal file
27
internal/types/dto/params.go
Normal file
@ -0,0 +1,27 @@
|
||||
package dto
|
||||
|
||||
// WorkspaceIDParam 工作空间 ID 参数
|
||||
type WorkspaceIDParam struct {
|
||||
ID EntityId `params:"id"`
|
||||
}
|
||||
|
||||
// WorkspaceMemberParam 工作空间成员参数
|
||||
type WorkspaceMemberParam struct {
|
||||
WorkspaceID EntityId `params:"id"`
|
||||
UserID string `params:"user_id"`
|
||||
}
|
||||
|
||||
// CollectionIDParam 集合 ID 参数
|
||||
type CollectionIDParam struct {
|
||||
ID EntityId `params:"id"`
|
||||
}
|
||||
|
||||
// DocumentIDParam 文档 ID 参数
|
||||
type DocumentIDParam struct {
|
||||
ID EntityId `params:"id"`
|
||||
}
|
||||
|
||||
// BlockIDParam 文档块 ID 参数
|
||||
type BlockIDParam struct {
|
||||
ID EntityId `params:"block_id"`
|
||||
}
|
@ -8,6 +8,6 @@ type CurrentUserResponse struct {
|
||||
IP string `json:"ip"`
|
||||
Valid bool `json:"valid"`
|
||||
UserEmail string `json:"userEmail"`
|
||||
UserId user.Id `json:"userId"`
|
||||
UserId user.ID `json:"userId"`
|
||||
UserName string `json:"userName"`
|
||||
}
|
||||
|
29
internal/types/dto/workspace.go
Normal file
29
internal/types/dto/workspace.go
Normal file
@ -0,0 +1,29 @@
|
||||
package dto
|
||||
|
||||
import "leafdev.top/Leaf/leaf-library-3/internal/types/user"
|
||||
|
||||
// CreateWorkspaceRequest 创建工作空间请求
|
||||
type CreateWorkspaceRequest struct {
|
||||
Name string `json:"name" validate:"required|minLen:1"`
|
||||
}
|
||||
|
||||
// GetWorkspaceRequest 获取工作空间请求
|
||||
type GetWorkspaceRequest struct {
|
||||
ID EntityId `params:"id"`
|
||||
}
|
||||
|
||||
// DeleteWorkspaceRequest 删除工作空间请求
|
||||
type DeleteWorkspaceRequest struct {
|
||||
ID EntityId `params:"id"`
|
||||
}
|
||||
|
||||
// AddWorkspaceMemberRequest 添加工作空间成员请求
|
||||
type AddWorkspaceMemberRequest struct {
|
||||
UserID user.ID `json:"user_id" validate:"required"`
|
||||
}
|
||||
|
||||
// RemoveWorkspaceMemberRequest 移除工作空间成员请求
|
||||
type RemoveWorkspaceMemberRequest struct {
|
||||
WorkspaceID EntityId `params:"id"`
|
||||
UserID string `params:"user_id"`
|
||||
}
|
@ -3,6 +3,9 @@ package errs
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrPageNotFound = errors.New("page not found")
|
||||
ErrNotFound = errors.New("not found")
|
||||
ErrPageNotFound = errors.New("page not found")
|
||||
ErrNotFound = errors.New("not found")
|
||||
ErrNoPermission = errors.New("no permission")
|
||||
ErrDocumentNotExists = errors.New("document not exists")
|
||||
ErrInvalidParentDocument = errors.New("invalid parent document")
|
||||
)
|
||||
|
@ -6,14 +6,14 @@ import (
|
||||
)
|
||||
|
||||
// AnonymousUser 调试模式下的用户
|
||||
const AnonymousUser Id = "anonymous"
|
||||
const AnonymousUser ID = "anonymous"
|
||||
|
||||
type Token struct {
|
||||
Aud string `json:"aud"`
|
||||
Iss string `json:"iss"`
|
||||
Iat float64 `json:"iat"`
|
||||
Exp float64 `json:"exp"`
|
||||
Sub Id `json:"sub" mapstructure:"-"`
|
||||
Sub ID `json:"sub" mapstructure:"-"`
|
||||
Scopes []string `json:"scopes"`
|
||||
Roles []Role `json:"roles,omitempty"`
|
||||
Permissions []Permission `json:"permissions"`
|
||||
@ -30,7 +30,7 @@ type Token struct {
|
||||
|
||||
type User struct {
|
||||
Token Token
|
||||
Id Id
|
||||
ID ID
|
||||
Valid bool
|
||||
}
|
||||
|
||||
@ -42,9 +42,9 @@ func (r Role) String() string {
|
||||
|
||||
type Permission string
|
||||
|
||||
type Id string
|
||||
type ID string
|
||||
|
||||
func (u *User) GetId() Id {
|
||||
func (u *User) GetId() ID {
|
||||
return u.Token.Sub
|
||||
}
|
||||
|
||||
@ -108,7 +108,7 @@ func (u *User) HasPermissions(permissions ...Permission) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (u Id) String() string {
|
||||
func (u ID) String() string {
|
||||
return string(u)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user