You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在TypeScript中分离Kcell电信API的DTO与处理逻辑?

问题描述

我有若干用于描述Kcell电信API对象的DTO类,示例如下:

/**
 * ...
 * */
export class KcellEventRequest {
    /**
     * ...
     * */
    cmd: 'event'

    /**
     * ...
     * */
    crm_token: string
...
}

每个Kcell事件对应一个此类DTO。接收数据后,我通过序列化器将其转换为对应DTO实例:

private serializer (body: any): KcellEventRequest | KcellHistoryRequest | KcellContactRequest | KcellRatingRequest {
    body.authorization = this.parseToken(body.crm_token)
    switch (body.cmd) {
        case 'event' :
            return plainToInstance(KcellEventRequest, body)
        case 'history':
            return plainToInstance(KcellHistoryRequest, body)
        case 'contact':
            return plainToInstance(KcellContactRequest, body)
        case 'rating':
            return plainToInstance(KcellRatingRequest, body)
        default:
            throw Error('Incorrect cmd')
    }
}

async handleRequest (body: any, headers: any): Promise<any> {
        // serialize object
    const callObject = this.serializer(body)
        // {{read next}}
    return await callObject.handle(headers)
}

这些DTO类均实现了TelephonyAPIHandler接口,包含handle方法以触发业务逻辑。目前我通过callObject.handle(headers)直接调用,但不希望处理逻辑与DTO耦合,请问如何将handle方法从DTO中分离?


解决方案

核心思路是把业务逻辑从DTO中抽离到独立的处理器类,通过命令-处理器映射表关联数据结构与业务行为,实现完全解耦。

步骤1:让DTO回归纯数据结构

移除所有DTO类中的handle方法,只保留字段定义,让它们仅负责承载数据:

// 纯数据DTO,仅定义数据字段
export class KcellEventRequest {
    cmd: 'event'
    crm_token: string
    authorization?: string
    // 其他业务字段...
}

export class KcellHistoryRequest {
    cmd: 'history'
    crm_token: string
    authorization?: string
    // 其他业务字段...
}

// 同理处理KcellContactRequest、KcellRatingRequest

步骤2:创建独立的处理器接口与实现类

定义统一的处理器接口,为每个DTO编写对应的业务逻辑实现:

// 处理器统一契约
interface RequestHandler<T> {
    handle(dto: T, headers: any): Promise<any>
}

// 事件请求专属处理器,实现对应业务逻辑
class KcellEventHandler implements RequestHandler<KcellEventRequest> {
    async handle(dto: KcellEventRequest, headers: any): Promise<any> {
        // 这里写原KcellEventRequest.handle中的业务逻辑
        // 比如调用服务、校验参数、返回响应等
        return { code: 200, msg: '事件处理完成', data: {} }
    }
}

// 历史请求专属处理器
class KcellHistoryHandler implements RequestHandler<KcellHistoryRequest> {
    async handle(dto: KcellHistoryRequest, headers: any): Promise<any> {
        // 历史查询业务逻辑
        return { code: 200, msg: '历史数据查询完成', data: [] }
    }
}

// 同理实现KcellContactHandler、KcellRatingHandler

步骤3:建立命令与处理器的映射关系

用映射表替代原有的switch逻辑,后续新增命令只需扩展映射表即可:

// 映射表:cmd -> DTO类 + 对应处理器实例
const handlerMap: Record<string, {
    dtoClass: any,
    handler: RequestHandler<any>
}> = {
    'event': {
        dtoClass: KcellEventRequest,
        handler: new KcellEventHandler()
    },
    'history': {
        dtoClass: KcellHistoryRequest,
        handler: new KcellHistoryHandler()
    },
    'contact': {
        dtoClass: KcellContactRequest,
        handler: new KcellContactHandler()
    },
    'rating': {
        dtoClass: KcellRatingRequest,
        handler: new KcellRatingHandler()
    }
}

步骤4:修改请求处理流程

更新序列化与请求处理逻辑,通过映射表匹配处理器执行业务逻辑:

private getProcessEntry(body: any): { dto: any, handler: RequestHandler<any> } {
    body.authorization = this.parseToken(body.crm_token)
    const cmd = body.cmd as string
    const entry = handlerMap[cmd]

    if (!entry) {
        throw new Error('Incorrect cmd')
    }

    const dto = plainToInstance(entry.dtoClass, body)
    return { dto, handler: entry.handler }
}

async handleRequest(body: any, headers: any): Promise<any> {
    const { dto, handler } = this.getProcessEntry(body)
    return await handler.handle(dto, headers)
}

方案优势

  • 职责单一:DTO仅负责数据结构,处理器仅负责业务逻辑,便于维护和分工
  • 扩展性强:新增命令时,只需添加DTO类、处理器类,更新映射表即可,无需修改原有代码
  • 可测试性高:处理器可以单独进行单元测试,无需依赖DTO实例化逻辑

内容的提问来源于stack exchange,提问作者vitaliyJR

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 03:35:05