frontend: add workspace context, file content and conflict apis with frontend type contracts

补齐 FE-002:服务端新增 workspace/context、mappings/check-target、files/content、
conflicts 明细与 apply 路由,核心层补充本地文件内容与冲突查询/应用能力及二进制/大文件约束;
前端补齐统一领域类型契约与类型化端点封装。

gradle build / pnpm typecheck 均通过。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
fenghp
2026-06-09 20:35:15 +08:00
co-authored by Cursor
parent 5c633fd024
commit ca4089a885
8 changed files with 921 additions and 13 deletions
+229
View File
@@ -0,0 +1,229 @@
import { apiClient } from "./client"
import type {
AppConfig,
CheckMappingTargetResult,
CheckinResult,
Collection,
ConflictInfo,
ConflictResolution,
ConflictResolutionResult,
ConnectResult,
FileContent,
FileOperationResult,
FolderDiffItem,
GetLatestResult,
HistoryEntry,
MappingInfo,
OperationLogEntry,
PendingChange,
ServerItem,
TextDiff,
WorkspaceContext,
WorkspaceInfo,
} from "./types"
// 文件类操作(checkout / add / delete / undo)的统一请求体。
type FileActionBody = { paths: string[]; recursive?: boolean }
/**
* 读取服务端持久化配置。
*/
export function getConfig() {
return apiClient.get<{ config: AppConfig }>("/api/config")
}
/**
* 保存服务端持久化配置。
*/
export function saveConfig(config: AppConfig) {
return apiClient.put<{ config: AppConfig }>("/api/config", { body: config })
}
/**
* 使用给定连接信息连接 TFS,成功后服务端会保存配置并建立会话。
*/
export function connect(config: Partial<AppConfig> = {}) {
return apiClient.post<ConnectResult>("/api/session/connect", { body: config })
}
/**
* 列出当前账号可见的 Collection。
*/
export function listCollections() {
return apiClient.get<{ collections: Collection[] }>("/api/collections")
}
/**
* 浏览服务端目录树节点。
*/
export function getServerTree(params: { path?: string; collection?: string } = {}) {
return apiClient.get<{ path: string; items: ServerItem[] }>("/api/server-tree", { query: params })
}
/**
* 读取当前目录下一级文件与文件夹列表。
*/
export function getFolderItems(params: { path?: string; collection?: string } = {}) {
return apiClient.get<{ path: string; items: ServerItem[] }>("/api/server-folder/items", { query: params })
}
/**
* 读取当前 Workspace 名称与 Mapping 列表。
*/
export function getWorkspace() {
return apiClient.get<{ workspace: string; mappings: MappingInfo[] }>("/api/workspace")
}
/**
* 确保当前 Collection 的默认 Workspace 存在,必要时创建。
*/
export function ensureWorkspace(body: { collection?: string; workspace?: string; comment?: string } = {}) {
return apiClient.post<{ workspace: WorkspaceInfo }>("/api/workspace/ensure", { body })
}
/**
* 读取工作台固定上下文(serverUri / collection / workspace / mappings)。
*/
export function getWorkspaceContext() {
return apiClient.get<{ context: WorkspaceContext }>("/api/workspace/context")
}
/**
* 读取当前 Workspace 的 Mapping 列表。
*/
export function listMappings() {
return apiClient.get<{ mappings: MappingInfo[] }>("/api/mappings")
}
/**
* 预校验 Mapping 目标路径,返回最终本地目标路径及是否已存在。
*/
export function checkMappingTarget(body: { serverPath: string; localParentPath: string }) {
return apiClient.post<CheckMappingTargetResult>("/api/mappings/check-target", { body })
}
/**
* 创建 Mapping,可选立即 Get Latest。
*/
export function addMapping(body: { serverPath: string; localPath: string; getLatest?: boolean }) {
return apiClient.post<{ mapping: MappingInfo; getLatest?: GetLatestResult }>("/api/mappings", { body })
}
/**
* 删除 Mapping,仅解除映射不删除本地文件。
*/
export function deleteMapping(body: { serverPath?: string; localPath?: string }) {
return apiClient.delete<{ mappings: MappingInfo[] }>("/api/mappings", { body })
}
/**
* 获取指定服务端路径的最新文件。
*/
export function getLatest(body: { serverPath?: string; recursive?: boolean } = {}) {
return apiClient.post<{ result: GetLatestResult }>("/api/files/get-latest", { body })
}
/**
* 对已存在文件或目录执行 checkout。
*/
export function checkout(body: FileActionBody) {
return apiClient.post<{ result: FileOperationResult }>("/api/files/checkout", { body })
}
/**
* 将本地新增文件加入 pending add。
*/
export function addFiles(body: FileActionBody) {
return apiClient.post<{ result: FileOperationResult }>("/api/files/add", { body })
}
/**
* 对已版本控制文件或目录执行 pending delete。
*/
export function deleteFiles(body: FileActionBody) {
return apiClient.post<{ result: FileOperationResult }>("/api/files/delete", { body })
}
/**
* 撤销指定路径上的挂起更改。
*/
export function undoFiles(body: FileActionBody) {
return apiClient.post<{ result: FileOperationResult }>("/api/files/undo", { body })
}
/**
* 读取当前 Workspace 的挂起更改。
*/
export function getPendingChanges(params: { serverPath?: string } = {}) {
return apiClient.get<{ pendingChanges: PendingChange[] }>("/api/pending-changes", { query: params })
}
/**
* 提交带注释的签入,paths 为空时签入全部挂起更改。
*/
export function checkin(body: { paths?: string[]; comment: string }) {
return apiClient.post<{ checkin: CheckinResult }>("/api/checkin", { body })
}
/**
* 读取服务器内容(latest 或指定 changeset),或读取本地映射文件内容。
*/
export function getFileContent(params: { serverPath?: string; localPath?: string; changeset?: number }) {
return apiClient.get<{ content: FileContent }>("/api/files/content", { query: params })
}
/**
* 对已映射目录执行目录对比。
*/
export function compareFolder(body: { serverPath: string; localPath?: string; recursive?: boolean }) {
return apiClient.post<{ diffs: FolderDiffItem[] }>("/api/compare/folder", { body })
}
/**
* 查询文件或目录历史。
*/
export function getHistory(params: { path: string; folder?: boolean }) {
return apiClient.get<{ history: HistoryEntry[] }>("/api/history", { query: params })
}
/**
* 查询指定 changeset 影响的文件列表。
*/
export function getChangesetFiles(params: { changeset: number }) {
return apiClient.get<{ files: HistoryEntry[] }>("/api/history/changeset", { query: params })
}
/**
* 生成本地文件与服务器 latest 的文本 Diff。
*/
export function diffLocalLatest(body: { serverPath: string; localPath: string }) {
return apiClient.post<{ diff: TextDiff }>("/api/diff/local-latest", { body })
}
/**
* 生成同一服务端文件两个 changeset 之间的文本 Diff。
*/
export function diffRevisions(body: { serverPath: string; sourceChangeset: number; targetChangeset: number }) {
return apiClient.post<{ diff: TextDiff }>("/api/diff/revisions", { body })
}
/**
* 查询当前 Workspace 的冲突明细。
*/
export function listConflicts(params: { serverPath?: string; recursive?: boolean } = {}) {
return apiClient.get<{ conflicts: ConflictInfo[] }>("/api/conflicts", { query: params })
}
/**
* 对单个冲突应用取舍(采用服务器版本或保留本地版本)。
*/
export function applyConflict(body: { conflictId: number; resolution: ConflictResolution }) {
return apiClient.post<{ resolution: ConflictResolutionResult }>("/api/conflicts/apply", { body })
}
/**
* 读取最近操作日志。
*/
export function getLogs() {
return apiClient.get<{ logs: OperationLogEntry[] }>("/api/logs")
}
+27 -1
View File
@@ -1,2 +1,28 @@
export type { ApiRequestOptions, ApiResponse, ApiResult } from "./types"
export type {
ApiRequestOptions,
ApiResponse,
ApiResult,
AppConfig,
CheckMappingTargetResult,
CheckinResult,
Collection,
ConflictInfo,
ConflictResolution,
ConflictResolutionResult,
ConnectResult,
FileContent,
FileOperationResult,
FolderDiffItem,
GetLatestResult,
HistoryEntry,
MappingConfig,
MappingInfo,
OperationLogEntry,
PendingChange,
ServerItem,
TextDiff,
WorkspaceContext,
WorkspaceInfo,
} from "./types"
export { apiClient } from "./client"
export * as api from "./endpoints"
+183
View File
@@ -26,3 +26,186 @@ export interface ApiRequestOptions {
body?: unknown
signal?: AbortSignal
}
// 单条 Mapping 配置,对应服务端 MappingConfig。
export interface MappingConfig {
serverPath: string
localPath: string
}
// 服务端持久化配置,对应 MacTfsServer.AppConfig。
export interface AppConfig {
serverUri?: string
authType?: string
domain?: string
username?: string
password?: string
collection?: string
workspace?: string
mappings: MappingConfig[]
}
// 连接结果摘要,对应 /api/session/connect 返回的概要字段。
export interface ConnectResult {
serverUri?: string
collectionCount?: number
}
// Collection 信息,对应 core TfsCollectionInfo。
export interface Collection {
name: string
id: string
}
// 服务端目录项,对应服务端 serverItems 序列化结构。
export interface ServerItem {
name: string
path: string
serverPath: string
type: "folder" | "file"
folder: boolean
latestVersion: number
checkinDate: number | null
}
// Workspace Mapping 信息,对应 core TfsMappingInfo。
export interface MappingInfo {
serverPath: string
localPath: string
}
// Workspace 概要,对应 core TfsWorkspaceInfo。
export interface WorkspaceInfo {
name: string
ownerName: string
computer: string
comment: string
created: boolean
mappings: MappingInfo[]
}
// 工作台固定上下文,对应 /api/workspace/context 的 context 字段。
export interface WorkspaceContext {
serverUri?: string
authType?: string
collection?: string
workspace?: string
connected: boolean
mappings: MappingInfo[]
}
// Mapping 目标预校验结果,对应 /api/mappings/check-target。
export interface CheckMappingTargetResult {
serverPath: string
localParentPath: string
name: string
targetPath: string
exists: boolean
allowed: boolean
}
// Get Latest 结果,对应 core TfsGetLatestResult。
export interface GetLatestResult {
updated: number
operations: number
conflicts: number
failures: number
}
// 文件操作结果,对应 core TfsFileOperationResult。
export interface FileOperationResult {
operation: string
affected: number
failures: string[]
}
// 挂起更改项,对应 core TfsPendingChangeInfo。
export interface PendingChange {
serverPath: string
localPath: string
name: string
folder: boolean
status: string
changeType: string
version: number
}
// 签入结果,对应 core TfsCheckinResult。
export interface CheckinResult {
changeset: number
submittedChanges: number
}
// 目录对比项,对应 core TfsFolderDiffItem。
export interface FolderDiffItem {
serverPath: string
localPath: string
name: string
folder: boolean
status: string
localVersion: number
latestVersion: number
}
// 历史记录项,对应 core TfsHistoryEntry。
export interface HistoryEntry {
serverPath: string
name: string
changeType: string
itemType: string
changeset: number
author: string
date: number | null
comment: string
}
// 文件内容,对应 core TfsFileContent,binary / tooLarge 时 content 为空串。
export interface FileContent {
serverPath: string
changeset: number
content: string
binary: boolean
size: number
tooLarge: boolean
}
// 文本 Diff,对应 core TfsTextDiff。
export interface TextDiff {
sourceLabel: string
targetLabel: string
lines: string[]
}
// 冲突明细,对应 core TfsConflictInfo。
export interface ConflictInfo {
conflictId: number
type: string
serverPath: string | null
localPath: string | null
yourServerItem: string | null
theirServerItem: string | null
baseServerItem: string | null
resolved: boolean
}
// 第一版支持的冲突取舍:采用服务器版本或保留本地版本。
export type ConflictResolution = "takeServer" | "keepLocal"
// 冲突应用结果,对应 core TfsConflictResolution。
export interface ConflictResolutionResult {
conflictId: number
resolution: string
resolved: boolean
remainingConflicts: number
}
// 操作日志项,对应服务端 OperationLogEntry。
export interface OperationLogEntry {
operation: string
summary: string
startedAt: number
endedAt: number
durationMs: number
success: boolean
errorMessage: string | null
}