OSS 模块类型定义

2025年1月28日
8 分钟阅读
作者:Penny Lens Team

OSS 模块类型定义

📋 概述

本文档提供了完整的 OSS(对象存储服务)模块 TypeScript 类型定义,用于管理文件和文件夹的元信息。这些类型定义涵盖了从基础数据模型到请求响应接口的完整类型系统。

🗂️ 核心数据模型

OssFolder - 文件夹模型

export interface OssFolder {
  _id: string;
  /** 用户ID */
  userId: string;
  /** 文件夹名称 */
  name: string;
  /** 父文件夹ID,根目录为空 */
  parentId?: string;
  /** 文件夹路径(相对路径,如 /images/avatars) */
  path: string;
  /** 备注 */
  remark?: string;
  /** 删除标记 */
  delFlag: boolean;
  /** 创建时间 */
  createdAt: number;
  /** 更新时间 */
  updatedAt: number;
}

特点

  • 支持层级结构(通过 parentIdpath
  • 软删除机制(delFlag
  • 用户隔离(userId
  • 时间戳记录

OssFile - 文件模型

export interface OssFile {
  _id: string;
  /** 用户ID */
  userId: string;
  /** 文件名 */
  fileName: string;
  /** 文件类型(MIME类型) */
  fileType: string;
  /** 文件大小(字节) */
  fileSize: number;
  /** 文件夹ID */
  folderId?: string;
  /** 文件路径(OSS相对路径,如 /images/avatar.jpg) */
  filePath: string;
  /** 文件哈希值(用于去重) */
  fileHash?: string;
  /** 备注 */
  remark?: string;
  /** 删除标记 */
  delFlag: boolean;
  /** 创建时间 */
  createdAt: number;
  /** 更新时间 */
  updatedAt: number;
}

特点

  • 完整的文件元信息
  • 去重支持(fileHash
  • 文件夹关联(folderId
  • 软删除机制

OssFileWithUrl - 带URL的文件模型

export interface OssFileWithUrl extends OssFile {
  /** 完整URL(计算属性:OSS_URL_PREFIX + filePath) */
  fileUrl: string;
}

用途:为文件对象添加可直接访问的完整URL。

📁 文件夹操作接口

创建文件夹

export interface FolderCreateRequest {
  /** 文件夹名称 */
  name: string;
  /** 父文件夹ID */
  parentId?: string;
  /** 备注 */
  remark?: string;
}

更新文件夹

export interface FolderUpdateRequest {
  /** 文件夹ID */
  id: string;
  /** 文件夹名称 */
  name?: string;
  /** 备注 */
  remark?: string;
}

删除文件夹

export interface FolderDeleteRequest {
  /** 文件夹ID */
  id: string;
  /** 是否删除OSS上的实际文件 */
  deleteOssFiles?: boolean;
}

注意deleteOssFiles 参数控制是否同时删除OSS上的实际文件,默认为 false(仅软删除)。

查询文件夹

export interface FolderQueryRequest {
  /** 父文件夹ID,为空查询根目录 */
  parentId?: string;
  /** 页码 */
  page?: number;
  /** 每页数量 */
  pageSize?: number;
}

📄 文件操作接口

创建文件

export interface FileCreateRequest {
  /** 文件名 */
  fileName: string;
  /** 文件类型 */
  fileType: string;
  /** 文件大小 */
  fileSize: number;
  /** 文件夹ID */
  folderId?: string;
  /** 文件路径(OSS相对路径) */
  filePath: string;
  /** 文件哈希值 */
  fileHash?: string;
  /** 备注 */
  remark?: string;
}

更新文件

export interface FileUpdateRequest {
  /** 文件ID */
  id: string;
  /** 文件名 */
  fileName?: string;
  /** 文件夹ID */
  folderId?: string;
  /** 备注 */
  remark?: string;
}

删除文件

export interface FileDeleteRequest {
  /** 文件ID */
  id: string;
  /** 是否删除OSS上的实际文件 */
  deleteOssFile?: boolean;
}

查询文件

export interface FileQueryRequest {
  /** 文件夹ID */
  folderId?: string;
  /** 文件名(模糊搜索) */
  fileName?: string;
  /** 文件类型 */
  fileType?: string;
  /** 页码 */
  page?: number;
  /** 每页数量 */
  pageSize?: number;
}

特点

  • 支持按文件夹过滤
  • 支持文件名模糊搜索
  • 支持按文件类型过滤
  • 支持分页查询

移动文件

export interface FileMoveRequest {
  /** 文件ID */
  id: string;
  /** 目标文件夹ID */
  targetFolderId?: string;
}

用途:将文件移动到其他文件夹,targetFolderId 为空表示移动到根目录。

批量删除文件

export interface FileBatchDeleteRequest {
  /** 文件ID列表 */
  ids: string[];
  /** 是否删除OSS上的实际文件 */
  deleteOssFiles?: boolean;
}

🔗 URL 相关接口

获取文件URL请求

export interface FileUrlRequest {
  /** 文件路径 */
  filePath: string;
}

文件URL响应

export interface FileUrlResponse {
  /** 完整URL */
  url: string;
  /** 文件路径 */
  filePath: string;
}

⚙️ 配置接口

OSS配置

export interface OssConfig {
  /** OSS URL前缀 */
  urlPrefix: string;
}

用途:存储OSS服务的配置信息,主要用于生成文件的完整访问URL。

🌳 树形结构支持

文件夹树节点

export interface FolderTreeNode extends OssFolder {
  /** 子文件夹 */
  children?: FolderTreeNode[];
}

用途:构建文件夹的树形结构,支持递归展示。

❌ 错误处理

错误消息常量

export const OSS_ERROR_MESSAGES = {
  EMPTY_FOLDER_NAME: "文件夹名称不能为空",
  EMPTY_FILE_NAME: "文件名不能为空",
  EMPTY_FILE_PATH: "文件路径不能为空",
  FOLDER_NOT_FOUND: "文件夹不存在",
  FILE_NOT_FOUND: "文件不存在",
  FOLDER_NOT_EMPTY: "文件夹不为空,无法删除",
  INVALID_PARENT_FOLDER: "父文件夹不存在",
  CIRCULAR_REFERENCE: "不能将文件夹移动到其子文件夹下",
  DUPLICATE_FOLDER_NAME: "同一目录下已存在同名文件夹",
  DUPLICATE_FILE_NAME: "同一文件夹下已存在同名文件",
};

特点

  • 统一的错误消息定义
  • 便于国际化处理
  • 提高错误处理的一致性

🎯 使用示例

创建文件夹

const createFolderRequest: FolderCreateRequest = {
  name: "用户头像",
  parentId: "root_folder_id",
  remark: "存储用户头像图片"
};

上传文件

const createFileRequest: FileCreateRequest = {
  fileName: "avatar.jpg",
  fileType: "image/jpeg",
  fileSize: 1024000,
  folderId: "avatar_folder_id",
  filePath: "/images/avatars/avatar.jpg",
  fileHash: "abc123def456",
  remark: "用户头像"
};

查询文件

const queryRequest: FileQueryRequest = {
  folderId: "avatar_folder_id",
  fileName: "avatar",
  fileType: "image/jpeg",
  page: 1,
  pageSize: 20
};

批量删除文件

const batchDeleteRequest: FileBatchDeleteRequest = {
  ids: ["file1_id", "file2_id", "file3_id"],
  deleteOssFiles: true
};

🔧 实现建议

1. 数据库索引

建议为以下字段创建索引以优化查询性能:

// 文件夹索引
db.ossFolders.createIndex({ userId: 1, parentId: 1 })
db.ossFolders.createIndex({ userId: 1, path: 1 })
 
// 文件索引
db.ossFiles.createIndex({ userId: 1, folderId: 1 })
db.ossFiles.createIndex({ userId: 1, fileName: 1 })
db.ossFiles.createIndex({ fileHash: 1 })

2. 开发规约遵循

Service 层实现规范

import type { UserResponse } from "../types/user";
import type { OssFile, FileCreateRequest } from "../types/oss";
import { BaseService } from "../types/BaseService";
import { ValidationException } from "../exceptions/AppException";
 
// 数据库集合定义
const db = cloud.database();
const ossFilesCollection = db.collection("ossFiles");
const ossFoldersCollection = db.collection("ossFolders");
 
/**
 * OSS文件服务类
 * 提供文件和文件夹的创建、查询、更新、删除等核心功能
 */
export class OssService extends BaseService {
  /**
   * 创建新文件
   */
  public async createFile(
    params: FileCreateRequest,
    userInfo: UserResponse,
  ): Promise<{ id: string; }> {
    // 参数验证
    if (!params.fileName) {
      throw new ValidationException("文件名不能为空");
    }
    
    if (!params.filePath) {
      throw new ValidationException("文件路径不能为空");
    }
    
    // 构建基础查询条件
    const baseCondition = this.buildBaseQueryCondition(userInfo);
    
    // 检查文件是否已存在
    const existingFile = await ossFilesCollection
      .where({
        ...baseCondition,
        filePath: params.filePath,
        delFlag: false
      })
      .get();
      
    if (existingFile.data.length > 0) {
      throw new ValidationException("文件路径已存在");
    }
    
    // 创建文件记录
    const fileData = {
      ...params,
      userId: userInfo.id,
      delFlag: false,
      createdAt: Date.now(),
      updatedAt: Date.now()
    };
    
    const result = await ossFilesCollection.add(fileData);
    return { id: result._id };
  }
  
  /**
   * 查询文件列表
   */
  public async queryFiles(
    params: FileQueryRequest,
    userInfo: UserResponse,
  ): Promise<Page<OssFile>> {
    const baseCondition = this.buildBaseQueryCondition(userInfo);
    const whereCondition = {
      ...baseCondition,
      ...(params.folderId && { folderId: params.folderId }),
      ...(params.fileName && { fileName: { $regex: params.fileName } }),
      ...(params.fileType && { fileType: params.fileType })
    };
    
    return this.paginateQuery(
      ossFilesCollection,
      whereCondition,
      params.page || 1,
      params.pageSize || 20,
      [{ field: "createdAt", order: "desc" }]
    );
  }
}

Controller 层实现规范

import type { AppResponse, Page } from "../types/appResponse";
import type { UserResponse } from "../types/user";
import type {
  FileCreateRequest,
  FileQueryRequest,
  FileUpdateRequest,
  OssFile,
} from "../types/oss";
import { OssService } from "../services/ossService";
import { BaseController } from "../types/BaseController";
 
/**
 * OSS文件管理控制器
 */
export class OssController extends BaseController {
  private ossService: OssService;
 
  constructor() {
    super();
    this.ossService = new OssService();
  }
 
  /**
   * 创建文件
   */
  async createFile(
    params: FileCreateRequest,
    userInfo: UserResponse,
  ): Promise<AppResponse<{ id: string; }>> {
    return this.wrapAsync(
      async () => {
        return this.ossService.createFile(params, userInfo);
      },
      "创建文件成功",
    );
  }
 
  /**
   * 查询文件列表
   */
  async queryFiles(
    params: FileQueryRequest,
    userInfo: UserResponse,
  ): Promise<AppResponse<Page<OssFile>>> {
    return this.wrapAsync(
      async () => {
        return this.ossService.queryFiles(params, userInfo);
      },
      "查询文件列表成功",
    );
  }
 
  /**
   * 获取文件URL(不需要用户信息的场景)
   */
  async getFileUrl(
    params: FileUrlRequest,
    _userInfo: UserResponse, // 使用下划线前缀表示有意忽略的参数
  ): Promise<AppResponse<FileUrlResponse>> {
    return this.wrapAsync(
      async () => {
        return this.ossService.getFileUrl(params);
      },
      "获取文件URL成功",
    );
  }
}

路由配置规范

// OSS相关路由
export const ossActions = [
  { action: "createFile", requireAuth: true },
  { action: "queryFiles", requireAuth: true },
  { action: "updateFile", requireAuth: true },
  { action: "deleteFile", requireAuth: true },
  { action: "getFileUrl", requireAuth: false }, // 获取文件URL不需要认证
  { action: "createFolder", requireAuth: true },
  { action: "queryFolders", requireAuth: true },
  { action: "updateFolder", requireAuth: true },
  { action: "deleteFolder", requireAuth: true },
];
 
// 构建路由配置
export const routes: Record<string, RouteConfig> = {
  ...buildRoutes("oss", ossActions, ossController),
};

3. 文件去重

利用 fileHash 字段实现文件去重:

// 检查文件是否已存在
const existingFile = await ossFilesCollection
  .where({ fileHash: newFileHash })
  .get();
 
if (existingFile.data.length > 0) {
  // 文件已存在,返回现有文件信息
  return existingFile.data[0];
}

4. 软删除处理

所有查询都应该过滤已删除的记录:

const query = {
  userId: currentUserId,
  delFlag: false  // 只查询未删除的记录
};

📊 数据流程图

graph TD
    A[用户上传文件] --> B[计算文件哈希]
    B --> C{文件是否已存在?}
    C -->|是| D[返回现有文件信息]
    C -->|否| E[上传到OSS]
    E --> F[创建文件记录]
    F --> G[返回文件信息]
    
    H[用户创建文件夹] --> I[验证父文件夹存在]
    I --> J[创建文件夹记录]
    J --> K[返回文件夹信息]
    
    L[用户删除文件] --> M{是否删除OSS文件?}
    M -->|是| N[删除OSS文件]
    M -->|否| O[仅软删除记录]
    N --> O
    O --> P[更新删除标记]

🚀 扩展功能

1. 文件版本管理

可以扩展 OssFile 接口添加版本控制:

export interface OssFileWithVersion extends OssFile {
  /** 文件版本号 */
  version: number;
  /** 是否为最新版本 */
  isLatest: boolean;
  /** 父版本ID */
  parentVersionId?: string;
}

2. 文件权限管理

可以添加权限控制:

export interface OssFileWithPermission extends OssFile {
  /** 访问权限 */
  permissions: {
    /** 可读用户列表 */
    readUsers: string[];
    /** 可写用户列表 */
    writeUsers: string[];
    /** 公开访问 */
    isPublic: boolean;
  };
}

3. 文件标签系统

支持文件分类和搜索:

export interface OssFileWithTags extends OssFile {
  /** 文件标签 */
  tags: string[];
  /** 文件分类 */
  category?: string;
}

📝 总结

这套 OSS 模块类型定义提供了:

  1. 完整的数据模型:覆盖文件和文件夹的所有必要属性
  2. 丰富的操作接口:支持 CRUD 操作和高级功能
  3. 类型安全:TypeScript 类型检查确保代码质量
  4. 扩展性:易于扩展新功能和属性
  5. 一致性:统一的命名规范和错误处理
  6. 开发规约遵循:严格按照项目开发规约实现 Service 和 Controller 层
  7. 数据库操作规范:遵循统一的数据库操作和查询规范
  8. 异常处理规范:使用统一的异常处理机制

开发规约要点

  • Service 层:继承 BaseService,使用 buildBaseQueryConditionpaginateQuery 等工具方法
  • Controller 层:继承 BaseController,使用 wrapAsync 包装业务逻辑,不使用 public 关键字
  • 路由配置:使用 buildRoutes 函数批量构建路由配置
  • 类型定义:遵循统一的命名规范,Request/Response 类型明确
  • 异常处理:使用具体的业务异常类,提供清晰的错误信息

通过使用这些类型定义和开发规约,可以构建一个功能完整、类型安全、符合项目规范的 OSS 管理系统。