feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -0,0 +1,134 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { snakeCase } from 'lodash-es';
import { injectable, inject } from 'inversify';
import { reporter } from '@coze-workflow/base';
import {
type AddOrDeleteLineOperationValue,
type AddOrDeleteWorkflowNodeOperationValue,
type ChangeNodeDataValue,
FreeOperationType,
} from '@flowgram-adapter/free-layout-editor';
import { DisposableCollection } from '@flowgram-adapter/common';
import { type Operation, OperationService } from '@flowgram-adapter/common';
@injectable()
export class WorkflowOperationReportService {
@inject(OperationService)
private readonly operationService: OperationService;
private toDispose = new DisposableCollection();
private lastOperation: Operation | null = null;
init() {
this.toDispose.pushAll([
this.operationService.onApply((operation: Operation) => {
if (!operation.type) {
return;
}
try {
if (!this.shouldReport(operation)) {
return;
}
const message = this.getMessageByOperation(operation);
reporter.info({ message });
} catch (e) {
reporter.error({
error: e as Error,
message: 'workflow operation report error',
});
}
this.lastOperation = operation;
}),
]);
}
dispose() {
this.toDispose.dispose();
}
private getMessageByOperation(operation: Operation): string {
const { type, value } = operation;
const eventName = this.getEventName(type);
if (
type === FreeOperationType.addLine ||
type === FreeOperationType.deleteLine
) {
const {
from,
to = '',
fromPort = '',
toPort = '',
} = value as AddOrDeleteLineOperationValue;
return `${eventName} from ${from}${this.portToString(fromPort)} to ${to}${this.portToString(toPort)}`;
}
if (
type === FreeOperationType.addNode ||
type === FreeOperationType.deleteNode
) {
const {
node: { id },
} = value as AddOrDeleteWorkflowNodeOperationValue;
return `${eventName} ${id}`;
}
if (this.isChangeDataType(type)) {
const { id, path } = value as ChangeNodeDataValue;
const message = `${eventName} node:${id} path:${path}`;
return message;
}
return eventName;
}
private getEventName(type: string) {
return `workflow_${snakeCase(type)}`;
}
private isChangeDataType(type: string) {
return (
type === FreeOperationType.changeNodeData || type === 'changeFormValues'
);
}
private shouldReport(operation: Operation) {
const { value, type } = operation;
// 修改同一个节点,同一个属性只上报一次, 防止频繁编辑上报过多
if (
this.isChangeDataType(type) &&
this.lastOperation &&
type === this.lastOperation.type
) {
const { path, id } = value as ChangeNodeDataValue;
const { path: lastPath, id: lastId } = this.lastOperation
.value as ChangeNodeDataValue;
if (path === lastPath && id === lastId) {
return false;
}
}
return true;
}
private portToString(port: string | number) {
return port ? `:${port}` : '';
}
}