Skip to content

Commit 7343ce0

Browse files
committed
feat: 抽离转换策略
1 parent c423233 commit 7343ce0

File tree

7 files changed

+170
-84
lines changed

7 files changed

+170
-84
lines changed

packages/board-collaboration/src/index.ts

Lines changed: 37 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { OperationSource, type EBoard, type ElementService, type ModelChangeEvent, type ModelService } from "@e-board/board-core";
1+
import { type EBoard, type ElementService, type ModelChangeEvent, type ModelService } from "@e-board/board-core";
22
import { WebSocketProvider, MsgType } from '@e-board/board-websocket';
3+
import { operationManager } from "./operation/transform";
34
const WS_URL = 'ws://localhost:3010/collaboration'; // TODO: 这个配置应该放到app初始化时作为配置传入
45

56
class BoardCollaboration {
@@ -9,110 +10,64 @@ class BoardCollaboration {
910
private disposeList: (() => void)[] = [];
1011
private currentUserid = `user-${Math.floor(Math.random() * 1000)}`;
1112
constructor(private board: EBoard) {
12-
1313
this.modelService = board.getService('modelService')
1414
this.elementService = this.board.getService('elementService')
15-
1615
this.init()
1716
}
1817

1918
private init = () => {
2019
this.websocketProvider = new WebSocketProvider();
20+
this.initRemoteConnection();
21+
this.initLocalSubscription();
22+
}
23+
24+
private initRemoteConnection = () => {
2125
try {
22-
this.websocketProvider.connect(WS_URL);
23-
this.websocketProvider.onMessage((data) => {
24-
console
26+
this.websocketProvider?.connect(WS_URL);
27+
this.websocketProvider?.onMessage((data) => {
2528
if (data.type === MsgType.OPERATION) {
2629
if (data.senderId === this.currentUserid) return; // 忽略自己发送的消息
27-
const operation = JSON.parse(data.data)
28-
// 外存模型转内存模型
29-
if (operation.operation === 'create') {
30-
const element = this.elementService.getElement(operation.model.type);
31-
if (!element) throw new Error(`Unregistered element type: ${operation.model.type}`);
32-
const saveInfoService = this.board.getService('saveInfoService')
33-
saveInfoService.importSaveInfo(operation.model, OperationSource.REMOTE)
34-
} else if (operation.operation === 'update') {
35-
const modelId = operation.modelId;
36-
// 获取type
37-
const model = this.modelService.getModelById(modelId);
38-
const type = model?.type;
39-
if (!type) throw new Error('Operation missing type');
40-
const element = this.elementService.getElement(type);
41-
if (!element) throw new Error(`Unregistered element type: ${type}`);
42-
this.modelService.updateModel(model.id, operation.updates, OperationSource.REMOTE)
43-
} else if (operation.operation === 'delete') {
44-
45-
operation.data.deletedModels.forEach((m: any) => {
46-
const element = this.elementService.getElement(m.type);
47-
if (!element) throw new Error(`Unregistered element type: ${m.type}`);
48-
const model = element.saveInfoProvider.importSaveInfo(m)
49-
this.modelService.deleteModel(model.id, OperationSource.REMOTE)
50-
})
30+
const operationData = JSON.parse(data.data)
31+
const handler = operationManager.getHandler(operationData.operation);
32+
if (handler) {
33+
handler.handleRemote({
34+
data: operationData,
35+
board: this.board,
36+
modelService: this.modelService,
37+
elementService: this.elementService
38+
});
5139
} else {
52-
throw new Error(`Unsupported operation type: ${operation.operation}`);
40+
throw new Error(`Unsupported operation type: ${operationData.operation}`);
5341
}
5442
}
55-
5643
})
5744
} catch (err) {
5845
console.error('WebSocket 连接失败:', err);
5946
}
47+
}
6048

61-
49+
private initLocalSubscription = () => {
6250
const { dispose } = this.modelService.onModelOperation(
6351
(operation: ModelChangeEvent) => {
64-
65-
const body: any = {
66-
type: MsgType.OPERATION,
67-
id: `msg-${Date.now()}`,
68-
senderId: this.currentUserid,
69-
timestamp: Date.now()
70-
}
7152
if (operation.operationSource === 'remote') return; // 忽略远程操作,防止循环广播
7253

73-
// 内存模型转外存模型
74-
if (operation.type === 'create') {
75-
76-
const type = operation.model?.type
77-
if (!type) throw new Error('Operation missing type');
78-
const element = this.elementService.getElement(type);
79-
if (!element) throw new Error(`Unregistered element type: ${type}`);
80-
81-
82-
83-
body.data = JSON.stringify({ operation: 'create', model: element.saveInfoProvider.parse(operation.model) })
84-
} else if (operation.type === 'update') {
85-
const modelId = operation.modelId;
86-
// 获取type
87-
const model = this.modelService.getModelById(modelId);
88-
const type = model?.type;
89-
if (!type) throw new Error('Operation missing type');
90-
const element = this.elementService.getElement(type);
91-
if (!element) throw new Error(`Unregistered element type: ${type}`);
92-
93-
body.data = JSON.stringify({
94-
operation: 'update',
95-
updates: element.saveInfoProvider.parse(operation.updates),
96-
previousState: element.saveInfoProvider.parse(operation.previousState),
97-
modelId: operation.modelId
98-
})
99-
} else if (operation.type === 'delete') {
100-
body.data = JSON.stringify({
101-
operation: 'delete',
102-
deletedModels: Array.from(operation.deletedModels?.values() || []).map(m => {
103-
const type = m?.type;
104-
if (!type) throw new Error('Operation missing type');
105-
const element = this.elementService.getElement(type);
106-
if (!element) throw new Error(`Unregistered element type: ${type}`);
107-
return element.saveInfoProvider.parse(m);
108-
}),
109-
modelId: operation.modelId
110-
})
111-
} else {
112-
throw new Error(`Unsupported operation type: ${operation.type}`);
54+
const handler = operationManager.getHandler(operation.type);
55+
if (handler) {
56+
const payload = handler.handleLocal({
57+
operation,
58+
board: this.board,
59+
modelService: this.modelService,
60+
elementService: this.elementService
61+
});
62+
const body: any = {
63+
type: MsgType.OPERATION,
64+
id: `msg-${Date.now()}`,
65+
senderId: this.currentUserid,
66+
timestamp: Date.now(),
67+
data: JSON.stringify(payload)
68+
}
69+
this.websocketProvider?.send(body)
11370
}
114-
this.websocketProvider?.send(body)
115-
console.log('Received operation:', operation);
11671
}
11772
)
11873
this.disposeList.push(dispose);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { OperationSource } from "@e-board/board-core";
2+
import { IOperationHandler } from "./type";
3+
4+
export class CreateHandler implements IOperationHandler {
5+
type = 'create';
6+
7+
handleLocal({ operation, elementService }: any) {
8+
const type = operation.model?.type;
9+
if (!type) throw new Error('Operation missing type');
10+
11+
const element = elementService.getElement(type);
12+
if (!element) throw new Error(`Unregistered element type: ${type}`);
13+
14+
return {
15+
operation: this.type,
16+
model: element.saveInfoProvider.parse(operation.model)
17+
};
18+
}
19+
20+
handleRemote({ data, board, elementService }: any) {
21+
const element = elementService.getElement(data.model.type);
22+
if (!element) throw new Error(`Unregistered element type: ${data.model.type}`);
23+
24+
const saveInfoService = board.getService('saveInfoService');
25+
saveInfoService.importSaveInfo(data.model, OperationSource.REMOTE);
26+
}
27+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { OperationSource } from "@e-board/board-core";
2+
import { IOperationHandler } from "./type";
3+
4+
export class DeleteHandler implements IOperationHandler {
5+
type = 'delete';
6+
7+
handleLocal({ operation, elementService }: any) {
8+
return {
9+
operation: this.type,
10+
deletedModels: Array.from(operation.deletedModels?.values() || []).map((m: any) => {
11+
const type = m?.type;
12+
if (!type) throw new Error('Operation missing type');
13+
const element = elementService.getElement(type);
14+
if (!element) throw new Error(`Unregistered element type: ${type}`);
15+
return element.saveInfoProvider.parse(m);
16+
}),
17+
modelId: operation.modelId
18+
};
19+
}
20+
21+
handleRemote({ data, modelService, elementService }: any) {
22+
// 修正之前代码里 data.data.deletedModels 的问题,统一使用 data.deletedModels
23+
const deletedModels = data.deletedModels || [];
24+
deletedModels.forEach((m: any) => {
25+
const element = elementService.getElement(m.type);
26+
if (!element) throw new Error(`Unregistered element type: ${m.type}`);
27+
const model = element.saveInfoProvider.importSaveInfo(m);
28+
modelService.deleteModel(model.id, OperationSource.REMOTE);
29+
});
30+
}
31+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { CreateHandler } from "./create";
2+
import { UpdateHandler } from "./update";
3+
import { DeleteHandler } from "./delete";
4+
import { IOperationHandler } from "./type";
5+
6+
class OperationManager {
7+
private handlers: Map<string, IOperationHandler> = new Map();
8+
9+
constructor() {
10+
this.register(new CreateHandler());
11+
this.register(new UpdateHandler());
12+
this.register(new DeleteHandler());
13+
}
14+
15+
register(handler: IOperationHandler) {
16+
this.handlers.set(handler.type, handler);
17+
}
18+
19+
getHandler(type: string) {
20+
return this.handlers.get(type);
21+
}
22+
}
23+
24+
export const operationManager = new OperationManager();
25+
export * from "./type";
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import type { EBoard, ModelChangeEvent, ModelService, ElementService } from "@e-board/board-core";
2+
3+
export interface IOperationHandler {
4+
type: string;
5+
handleLocal(params: {
6+
operation: ModelChangeEvent;
7+
board: EBoard;
8+
modelService: ModelService;
9+
elementService: ElementService;
10+
}): any;
11+
handleRemote(params: {
12+
data: any;
13+
board: EBoard;
14+
modelService: ModelService;
15+
elementService: ElementService;
16+
}): void;
17+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { OperationSource } from "@e-board/board-core";
2+
import { IOperationHandler } from "./type";
3+
4+
export class UpdateHandler implements IOperationHandler {
5+
type = 'update';
6+
7+
handleLocal({ operation, modelService, elementService }: any) {
8+
const modelId = operation.modelId;
9+
const model = modelService.getModelById(modelId);
10+
const type = model?.type;
11+
if (!type) throw new Error('Operation missing type');
12+
13+
const element = elementService.getElement(type);
14+
if (!element) throw new Error(`Unregistered element type: ${type}`);
15+
16+
return {
17+
operation: this.type,
18+
updates: element.saveInfoProvider.parse(operation.updates),
19+
previousState: element.saveInfoProvider.parse(operation.previousState),
20+
modelId: operation.modelId
21+
};
22+
}
23+
24+
handleRemote({ data, modelService }: any) {
25+
const modelId = data.modelId;
26+
const model = modelService.getModelById(modelId);
27+
if (!model) return; // 或者抛错
28+
29+
modelService.updateModel(modelId, data.updates, OperationSource.REMOTE);
30+
}
31+
}

packages/board-core/src/elements/rectElement/saveInfoProvider/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ class SaveInfoProvider {
22
static parse(data: any) {
33
return {
44
type: data.type,
5-
points: [...data.points],
5+
points: data.points ? [...data.points] : [],
66
width: data.width,
77
height: data.height,
88
options: data.options ? { ...data.options } : undefined,
@@ -14,7 +14,7 @@ class SaveInfoProvider {
1414
return {
1515
type: info.type,
1616
isDrawing: false,
17-
points: info.points,
17+
points: info.points ? [...info.points] : [],
1818
width: info.width,
1919
height: info.height,
2020
options: info?.options ? { ...info.options } : undefined,

0 commit comments

Comments
 (0)