# 文件管理需求&方案
## 需求列表
### 文件夹
文件夹封层,每个对象存储当前路径 dir 和 父节点 id ,以及当前文件夹的表示。
nodeType 为节点类型,主要是为了查询根节点。
增加环境参数存储,区分不同环境下的文件夹
##### 文件夹的属性
- 名称
- 标识
- 描述
- ParaentId
- Dir: 当前路径
- nodeType: root | child
- 环境参数
- 创建人
- 更新时间
- 创建时间
##### 文件夹的API
- 文件夹列表(全量)
- 创建文件夹
- 更新文件夹
- 查询文件夹 > 通过id搜索
- 删除文件夹
### 文件
上传至oss之后,存储url、文件名、备注、从属文件夹。文件url不对外公开
##### 文件的属性
- 文件名
- 描述
- key
- 文件夹id
- 创建人
- url
- 更新时间
- 创建时间
##### 文件的API
- 文件列表分页
- 创建文件
- 更新文件
- 删除文件
- 通过id获取文件详情
### 文件访问控制
通过解析路径,查询数据库中存储url,并返回对应的资源
## 方案设计

### 数据结构设计
#### 文件夹
folder
```javascript
{
name: { type: String },
displayName: { type: String },
parentId: { type: Schema.Types.ObjectId, ref: 'folder'},
dir: { type: String }, // 添加index,所属文件夹,如 '/'
nodeType: { type: String }, // ROOT | CHILD
env: { type: String }, // 环境参数
createdBy: { type: Schema.Types.ObjectId, ref: 'User'},
updatedBy: { type: Schema.Types.ObjectId, ref: 'User'}
}, {
timestamps: true
}
index
+ paraentId - name - env // 唯一索引
+ dir // 索引,方便查询 getByDir
```
#### 文件
file
```javascript
{
name: { type: String },
description: { type: String },
folder: { type: Schema.Types.ObjectId, ref: 'Folder'},
url: { type: String }, // oss 存储地址
createdBy: { type: Schema.Types.ObjectId, ref: 'User'},
updatedBy: { type: Schema.Types.ObjectId, ref: 'User'},
}, {
timestamps: true
}
index
+ folder - name
```
### API设计
文件夹返回对象
FolderOutbound
```javascript
{
id: string,
name: string,
displayName: string,
parentId: string,
dir: string, // 所属文件夹
nodeType: 'ROOT' | 'CHILD',
env: Env, // 所属环境
createdBy: User,
updatedBy: User,
createdAt: number,
updatedAt: number
}
```
文件返回对象
FileOutbound
```
{
id: string,
name: string,
description: string,
folder: string,
url: string, // 文件虚拟地址,通过 static 服务转发至 oss
createdAt: string,
updatedAt: string,
createdBy: User,
updatedBy: User
}
```
##### 文件夹列表(全量)
| Method | Url | |
| ------ | ------- | ---- |
| get | /folder | |
Response:
```javascript
{
code: 0,
msg: '',
data: FolderOutbound[]
}
```
##### 创建文件夹
| Method | Url | |
| ------ | ------- | ---- |
| post | /folder | |
Request query
```javascript
{
env: 'DEV' // 环境
}
```
Request body
```javascript
{
name: string,
displayName: string,
parentId: string, // optional 缺省时,创建根目录下的文件夹
}
```
##### 更新文件夹
重命名,拖动文件夹。
需要注意,如果拖动了文件夹,所属的文件夹dir属性需要更新
| Method | Url | |
| ------ | ----------- | ---- |
| put | /folder/:id | |
Request param
```javascript
{
id: ObjectId // 文件夹id
}
```
Request body
```javascript
{
name: string,
displayName: string,
parentId: string, // optional 缺省时,创建根目录下的文件夹
}
```
Response
code :403 - name-existed
code: 0
```javascript
{
code: 0,
data: FolderOutbound
}
```
##### 查询文件夹 > 通过id搜索
| Method | Url | |
| ------ | ----------- | ---- |
| Get | /folder/:id | |
Request param
```javascript
{
id: ObjectId // 文件夹id
}
```
Response
code: 0
```javascript
{
code: 0,
data: FolderOutbound
}
```
##### 删除文件夹
| Method | Url | |
| ------ | ----------- | ---- |
| delete | /folder/:id | |
Request param
```javascript
{
id: ObjectId // 文件夹id
}
```
Response
code: 0
```
{
code: 0
}
```
-----------
##### 文件列表全量
| Method | Url | |
| ------ | ----- | ---- |
| get | /file | |
Request quest
```javascript
{
folderId: ObjectId // 文件夹id
}
```
Response
code: 0
```
{
code: 0
data: FileOutbound[]
}
```
##### 创建文件
| Method | Url | |
| ------ | ----- | ---- |
| Post | /file | |
Request quest
```javascript
{
folderId: ObjectId // 文件夹id
name: string, // 文件名
description: string, // 文件描述
url: string // 文件地址
}
```
Response
code: 0
```javascript
{
code: 0
data: FileOutbound[]
}
```
##### 更新文件
可以更新文件夹
| Method | Url | |
| ------ | --------- | ---- |
| put | /file/:id | |
Request param
```javascript
{
id: fileId
}
```
Request quest
```javascript
{
folderId: ObjectId // 文件夹id
name: string, // 文件名
description: string, // 文件描述
url: string // 文件地址
}
```
Response
code: 0
```javascript
{
code: 0
data: FileOutbound[]
}
```
##### 删除文件
| Method | Url | |
| ------ | --------- | ---- |
| delete | /file/:id | |
Request param
```Javascript
{
id: fileId
}
```
Response
code: 0
```javascript
{
code: 0
}
```
##### 通过id获取文件详情
| Method | Url | |
| ------ | --------- | ---- |
| get | /file/:id | |
Request param
```javascript
{
id: fileId // 文件id
}
```
Response
code: 0
```javascript
{
code: 0
data: FileOutbound[]
}
```
------------------------------
##### 上传文件
###### 图片上传
上传文件至CDN
| Method | Url | |
| ------ | -------- | ---- |
| Post | /jdImage | |
Request quest
```javascript
{
files: Joi.binary().description('FormData文件')
}
```
Response
code: 0
```javascript
{
code: 0
data: string[] //cdn 地址
}
```
###### 文件上传
上传文件至oss
| Method | Url | |
| ------ | ---- | ---- |
| Post | /oss | |
Request quest
```javascript
{
files: Joi.binary().description('FormData文件')
}
```
Response
code: 0
```javascript
{
code: 0
data: string[] //oss 地址
}
```
### 文件访问控制
route
/static/app/xxx/folderName/fileName?env=DEV
### 遇到的问题
新建一个服务,使用neos-backend调用该服务,权限验证没有拆出来
####