在线视频平台的用户端和管理端面向不同角色:用户端关注浏览、播放、上传和互动,管理端关注用户、内容、审核和权限。两个前端的界面差异很大,但都需要调用同一套后端能力。
在 HAOVP 中,我分别建立 Vue 3 用户端和管理端。共享后端不等于复制接口代码,也不等于让两个前端共用所有页面组件。更重要的是统一接口契约、认证方式、类型定义和错误处理。
先明确两端职责
用户端主要面向普通用户:
- 注册与登录;
- 浏览和搜索内容;
- 播放、上传与互动;
- 个人资料和使用记录;
- 移动端访问。
管理端主要面向运营和管理角色:
- 管理员登录;
- 用户状态管理;
- 内容审核与治理;
- 分类、标签和评论管理;
- 角色、权限和操作记录。
两端可以调用相同业务服务,但不能只依靠页面入口区分权限。后端仍要根据身份、角色、数据范围和业务状态判断操作是否合法。
共享的是接口契约
一个接口契约至少包括:
请求方法与路径
请求参数
字段类型和必填规则
认证要求
正常响应结构
错误码与错误结构
分页与排序约定例如用户端和管理端都需要获取视频详情,但返回字段和权限可能不同。普通用户只能看到公开内容,管理员还需要审核状态和治理信息。
可以使用不同接口,也可以在统一服务中根据权限返回合适的数据。关键是契约清楚,不能让前端根据隐藏字段自行判断权限。
建立统一的请求封装
两个前端都需要处理基础地址、Token、超时和错误响应。可以在各自项目中建立相同约定的请求层:
export interface ApiResponse<T> {
code: string;
message: string;
data: T;
}export async function request<T>(config: RequestConfig): Promise<T> {
const response = await http<ApiResponse<T>>(config);
if (response.data.code !== "SUCCESS") {
throw new ApiError(response.data.code, response.data.message);
}
return response.data.data;
}代码是简化示例,不对应项目的真实响应码、请求库和文件结构。
共享约定可以减少差异,但用户端和管理端仍可根据各自交互方式展示不同错误提示。
TypeScript 类型从哪里来
如果两个前端分别手写同一份接口类型,后端字段变化后很容易漏改一端。
可以考虑:
- 建立独立的共享类型包;
- 根据 OpenAPI 生成类型;
- 从统一接口文档维护模型;
- 只共享稳定 DTO,页面状态类型留在各端;
- 在 CI 中检查生成文件是否最新。
export interface VideoSummary {
id: string;
title: string;
coverUrl: string;
status: string;
}示例字段是抽象结构。真实业务中,管理端状态和用户端可见状态未必使用同一类型,不能为了共享而暴露内部字段。
认证流程可以统一,令牌存储要谨慎
用户端和管理端都可能使用 Access Token 与 Refresh Token,但认证主体和权限边界可以不同。
我会明确:
- 用户 Token 不能访问管理接口;
- 管理员 Token 不能因为权限高就绕过业务服务规则;
- 刷新接口与普通业务接口分开;
- 两端分别处理登录失效和刷新并发;
- 请求日志不记录完整 Token;
- 客户端存储方式结合 XSS、CSRF 和部署方式决定。
请求封装可以共享思路,但不能硬编码真实 Secret、账号和认证地址。
错误处理分为通用层和页面层
请求层适合处理:
- 网络失败;
- 超时;
- 未认证;
- 权限不足;
- 统一业务错误结构;
- 请求标识记录。
页面层适合处理:
- 表单字段错误;
- 当前业务状态不允许操作;
- 是否保留用户输入;
- 重试和返回入口;
- 具体业务提示。
如果所有错误都在拦截器中弹出同一条消息,页面无法根据业务场景提供正确反馈。
分页和筛选要统一约定
用户端和管理端都会出现列表,但管理端通常筛选条件更多。
需要统一:
页码从 0 还是 1 开始
每页数量字段名
总数与总页数
排序字段格式
空值筛选方式
日期范围边界后端返回统一分页结构,两个前端再根据页面需要组织表格、卡片或移动列表。
文件上传可以共享流程,不共享页面
用户端上传视频,管理端可能上传封面或其他资源。它们都可以复用上传协议:
- 创建上传任务;
- 选择或校验文件;
- 显示进度;
- 处理取消和失败重试;
- 查询处理状态;
- 完成后更新业务记录。
但用户端更关注操作体验,管理端更关注状态、审核和异常处理。共享后端流程不意味着两端使用完全相同的组件。
权限信息怎样驱动页面
后端可以返回当前用户的基础权限或可执行动作,前端据此展示入口。
{
"actions": ["VIEW", "EDIT"],
"status": "DRAFT"
}这是通用示例。隐藏按钮只是用户体验,后端接口仍需再次校验权限和状态。
管理端尤其要避免把全部权限规则写死在前端路由中。角色或权限变化后,前端缓存也需要更新。
是否建立共享前端包
共享代码可以减少重复,但也会增加两个项目之间的耦合。
适合共享的内容:
- 稳定 API 类型;
- 通用响应模型;
- 不含业务页面的工具函数;
- 日期和文件等基础格式化;
- 统一错误类型。
不一定适合共享:
- 页面路由;
- 角色专属组件;
- 交互状态;
- 大量依赖某个 UI 框架的组件;
- 频繁变化的页面模型。
如果共享包每次修改都迫使两端同时发布,说明边界可能过大。
联调时的检查顺序
接口文档与类型一致
-> 两端请求参数一致
-> 认证信息正确
-> 正常响应能解析
-> 错误响应能处理
-> 权限与状态由后端确认
-> PC 与移动场景分别验证我会特别检查一个接口在用户端和管理端是否使用了不同字段含义,以及后端变化后是否只有一端完成更新。
Vue 3 用户端与管理端共享后端能力,核心不是复用越多越好,而是稳定契约统一、业务权限清楚、前端职责独立。共享类型和请求约定可以减少重复,页面、交互和角色规则则应保留各自边界。