本文档主要对以下模型进行技术性说明:
- User:Bullet用户对象
- Room:房间对象,为用户创建,可公开或私有
- Bullet:弹幕对象
- Resource:资源对象
- Tag:标签对象
- Invitation:房间邀请
- Friend:好友邀请
type User {
userId: ID!
username: String!
password: String
email: String
firstname: String
lastname: String
avatar: String
friends: [User]
}
- userId:用户唯一标识符
- username:用户用户名,必填项。MongoDB会建立userId和username的双索引
- password:用户密码,使用bcrypt用环境变量中的JWT_SECRET进行加密
- email:邮箱,可选。以后可以同过用户名或密码进行登录
- avatar、firstname、lastname:用户资料中存储的姓、名、头像
- 一开始所有图片资源尝试过base64存储在数据库中,特别占据索引
- 目前前后端统一意见,所有媒体类型只支持URI
- 使用S3桶或者腾讯对象存储COS进行哈希和存储
- friends:用户的所有好友,后端存储为用户ID,前端会自动填充为User对象
createUser(username: String! password: String! email: String firstname: String lastname: String avatar: String): LoginResponse!
login(username: String! password: String!): LoginResponse!
返回类型
type LoginResponse {
user: User!
token: String!
}
用户成功登录或注册以后的返回结果
- user为用户对象,可选择性地提取属性(GraphQL特性)
- token为JWT,前端或插件会保存到对应的localStorage或者chrome.sync.storage中
users: [User],
user(userId: ID username: String): User!
currentUser: User
updateUser(password: String email: String firstname: String lastname: String avatar: String): User!
delete(username: String!): User!
findUser(username: String): [UserSearchResponse]
返回类型
type UserSearchResponse {
userId: ID!
username: String!
password: String
email: String
firstname: String
lastname: String
avatar: String
pending: Int
isFriend: Int
}
verifyToken(token: String!): User!
验证JWT合法性,用户开发模式
type Room {
roomId: ID!
alias: String!
users: [User]!
admins: [User]!
pending: [User]!
creator: User!
public: Boolean
widgets: [String]
avatar: String
updatedAt: String
}
- roomId:房间唯一标识符
- alias:房间昵称,注意可能不同用户创建相同昵称的房间
- users:房间里的所有用户,全集
- admins:房间管理员,为用户的子集
- pending:待进入房间用户,目前无用。为以后的批量导入留有空间(Bulk injection)
- creator:房间创建者
- public:房间是否公开,如果公开其他用户不能检索房间内部的任何资源,只能看到房间预告(Teaser)。目前部分API对私有房间进行了支持
- widgets:房间组件。预留属性,用于组件的延展。不光是视频资源、用户可以选择添加笔记、代办等组件。
- updatedAt:房间更新时间
rooms: [Room] // 所有房间,开发时使用,Query类型
allRooms(userId: ID): [Room] // 特定用户的所有房间,可见度根据房间public属性决定,Query类型
room(roomId: ID!): Room // 单个房间查询,Query类型
createRoom(alias: String! users: JSON! admins: JSON! public: Boolean avatar: String widgets: JSON ): Room! // 创建房间,Mutation类型
updateRoom(roomId: ID! alias: String admins: JSON users: JSON public: Boolean avatar: String widgets: JSON): Room // 更新房间,Mutation类型
deleteRoom(roomId: ID!): Room // 删除房间,Mutation类型
resourceTeasersInRoom(roomId: ID! limit: Int): [ResourceResponse]
获取给定房间(roomId)中的所有资源预告(上限由limit)决定。
- 聚合有效性判断(如果用户和房间的组合不符合规则,放弃聚合)
- 向Bullet类进行如下操作获得资源组
- 过滤该房间下(roomId)所有弹幕
- 根据资源ID(resourceId)进行组合
- 获取资源内最后一条弹幕的更新时间(max)作为聚合记录的排序条件
- 只投影获取资源ID和更新时间
- 更新时间降序排列
- 对于每个资源组
- 获取该房间(roomId)、该资源(resourceId)的弹幕
- 更新时间降序排列
- 上限为limit个,预设值为2
- 填充两个数组,一个结果对象数组、一个非空资源组
- 对于每一个房间下的资源
- 判断该资源(roomId)是否在非空资源组中
- 如果该资源不在非空资源组中,说明该资源聚合后没有弹幕,手工添加到结果对象数组中
具体实现参见 graphql/resolvers/room.js:328
。
type Bullet {
bulletId: ID!
user: User!
room: Room
source: String!
resource: Resource!
tags: [Tag]
row: Int
timestamp: Int!
content: String!
updatedAt: String
createdAt: String
}
- bulletId:弹幕ID,唯一标识
- user:弹幕发送者
- room:弹幕从属房间
- source:弹幕源(URI类型,比如视频URL)
- resource:对应资源(同一房间中)
- tags:弹幕标签(详情见Tag文档)
- row:弹幕行,用于插件注入展示
- timestamp:弹幕时间戳,可以理解为溢出屏幕的弹幕列
- content:弹幕内容
- updatedAt:弹幕更新时间
- createdAt:弹幕创建时间
bullets: [Bullet] // 所有弹幕,开发时使用,Query类型
createBullet(roomId: ID! resourceId: ID! source: String! timestamp: Int! row: Int content: String!): Bullet! // 创建弹幕,Mutation类型
updateBullet(bulletId: ID! content: String resourceId: ID tags: JSON timestamp: Int row: Int): Bullet // 更新弹幕,Mutation类型
deleteBullet(bulletId: ID!): Bullet // 删除特定弹幕,Mutation类型
bulletsByUser(roomId: ID source: String resourceId: String userId: ID!): [Bullet]
获取用户的所有弹幕。目前前端没有应用。预想应用场景:用户可以检索自己的所有历史弹幕和其他互动,进行CRUD操作;用户可以选取部分弹幕添加至个人主页。
allBulletsInRoom(roomId: ID!): [Bullet]
房间里所有的弹幕。目前前端没有应用。预想应用场景:用于房间总览,热度排序,全房间弹幕过滤迅速定位资源(Advanced Bullet Search)。
allBulletsInResource(roomId: ID! resourceId: ID!): [Bullet]
特定资源下所有的弹幕。前端使用极广,是房间下资源详情页面加载时调用的API。用于资源内弹幕时间线展示。
type Resource {
resourceId: ID!
name: String
room: Room!
url: String
avatar: String
description: String
tags: [Tag]
user: User!
updatedAt: String
createdAt: String
hidden: Boolean
}
- resourceId:资源ID,唯一标识
- name:资源名称
- room:资源所在房间
- url:资源URI
- avatar:资源图片
- description:资源描述
- tags:资源标签
- user:资源创建者
- updatedAt:资源更新时间
- createdAt:资源创建时间
- hidden:资源可见,用于替换删除操作(保留数据和标签)
resources: [Resource] // 所有资源,Query类型
resource(resourceId: ID!): Resource // 单个资源查询
createResource(roomId: ID! name: String! description: String url: String avatar: String tags: JSON): Resource! // 创建资源,Mutation类型
updateResource(resourceId: ID! name: String description: String url: String avatar: String tags: JSON): Resource // 更新特定资源,Mutation类型
deleteResource(resourceId: ID!): Resource // 删除特定资源,Mutation类型
findResources(userId: ID roomId: ID): [Resource]
- 查找特定用户在特定房间创建的所有资源
- 查找特定房间的所有资源
- 查找特定用户创建的所有资源
type Tag {
tagId: ID!
name: String!
count: Int!
}
- tagId:标签ID,唯一标识
- name:标签名称
- count:标签被引用次数
tags: [Tag] // 所有标签
searchTagByName(name: String): [Tag] // 正则搜索标签
- 当单个资源对象或弹幕对象被创建且带有标签时:
- 对于所有已有标签,其引用+1
- 对于所有为存在标签,创建一个1引用的标签
- 当单个弹幕对象被创建但没有标签时,会默认继承所属资源标签。
- 当资源或弹幕对象被删除时,会减少其所含标签的1个引用
- 当资源被隐藏(hidden)时,同样减少对应标签引用。
type RoomInvitation {
invitationId: ID!
user: User!
room: Room
accepted: Int!
sentAt: String!
}
invitations: [RoomInvitation] // 当前用户所有的房间邀请,Query类型
roomInvitations(history: Boolean): [RoomInvitation] // 当前用户所有的房间邀请,包含无效邀请,Query类型
createRoomInvitation(roomId: ID! userId: ID!): RoomInvitation! // 创建房间邀请给目标用户,Mutation类型
acceptRoomInvitation(invitationId: ID!): RoomInvitation! // 接受目标房间邀请,Mutation类型
declineRoomInvitation(invitationId: ID!): RoomInvitation! // 拒绝目标房间邀请,Mutation类型
type FriendInvitation {
invitationId: ID!
from: User!
to: User!
accepted: Int!
sentAt: String!
}
friendInvitations(history: Boolean): [FriendInvitation] // 当前用户所有的好友邀请,包含历史已同意或拒绝的邀请,Query类型
allFriendInvitations: [FriendInvitation] // 当前用户待接受的所有好友邀请,Query类型
createFriendInvitation(to: ID!): FriendInvitation! // 创建好友邀请给目标用户,Mutation类型
acceptFriendInvitation(invitationId: ID!): FriendInvitation! // 接受目标用户好友邀请,Mutation类型
declineFriendInvitation(invitationId: ID!): FriendInvitation! // 拒绝目标用户好友邀请,Mutation类型
deleteFriend(userId: ID!): User // 双向删除好友,Mutation类型
addFriendWithoutInvitation(to: ID!): FriendInvitation! // 测试用API,跳过邀请直接添加双向好友,Mutation类型
两种邀请类型,目前是存储在不同的集合,因为邀请所对应的属性不一样。未来考虑插入一个新的type
字段来帮助动态决定对象模型,这样可以把from
、to
动态映射到不同的对象,比如Room、User。
- -2:邀请从属实体被删除、或邀请被撤销。比如房间被删除
- -1:邀请待回复
- 0:邀请被拒绝
- 1:邀请被同意