Bitzsoft.Integrations.OutboundCall
1.0.0-alpha.10
dotnet add package Bitzsoft.Integrations.OutboundCall --version 1.0.0-alpha.10
NuGet\Install-Package Bitzsoft.Integrations.OutboundCall -Version 1.0.0-alpha.10
<PackageReference Include="Bitzsoft.Integrations.OutboundCall" Version="1.0.0-alpha.10" />
<PackageVersion Include="Bitzsoft.Integrations.OutboundCall" Version="1.0.0-alpha.10" />
<PackageReference Include="Bitzsoft.Integrations.OutboundCall" />
paket add Bitzsoft.Integrations.OutboundCall --version 1.0.0-alpha.10
#r "nuget: Bitzsoft.Integrations.OutboundCall, 1.0.0-alpha.10"
#:package Bitzsoft.Integrations.OutboundCall@1.0.0-alpha.10
#addin nuget:?package=Bitzsoft.Integrations.OutboundCall&version=1.0.0-alpha.10&prerelease
#tool nuget:?package=Bitzsoft.Integrations.OutboundCall&version=1.0.0-alpha.10&prerelease
Bitzsoft.Integrations.OutboundCall
外呼服务抽象层 — 统一 IOutboundCallService 接口、号码工具(清洗 / 分类 / 校验)、境内外分离、白名单 / 黑名单控制、per-call 活动编排引擎。
功能特性
- 统一外呼接口
IOutboundCallService:创建活动、导入名单、启动 / 暂停 / 恢复 / 停止、活动列表 / 详情、话单查询 - 号码校验
PhoneNumberValidator:内置 150+ 国家 / 地区 ITU 号码长度表,支持全角字符、中文 / 英文标签前缀、/ , ;空格换行分隔符等多种容错场景,自动识别+86/0086/(+86)等区号前缀组合 - 号码批量分类
PhoneNumberClassifier:将混合号码列表按区号自动分为境内(86)与境外两组,返回统一清洗格式 - 白名单 / 黑名单过滤
CallListFilter:创建活动前自动过滤号码,支持后缀匹配;非生产环境可强制要求白名单,防止误呼叫真实客户 - 境内外分离:
OutboundCallOptions.GetCallers / GetSdkAppId / GetIvrId按PhoneRegion自动选择主叫号码、应用 ID、IVR 流程 - per-call 编排引擎
CampaignDialer+CampaignRuntime:为无原生 campaign 概念的供应商(Twilio 等)提供串行拨号 + CPS 节流 + 暂停 / 恢复断点(代次守卫)+ 停止的内存活动编排 - 统一异常模型
OutboundCallException:携带供应商标识、错误码、请求 ID,便于问题溯源 - 供应商标识
ProviderName:每个实现暴露固定标识(如Tencent/Aliyun),用于日志区分与异常溯源
安装
.NET CLI
dotnet add package Bitzsoft.Integrations.OutboundCall
PackageReference
<PackageReference Include="Bitzsoft.Integrations.OutboundCall" Version="1.0.0" />
配置
抽象层本身不注册服务,但 OutboundCallOptions 是所有供应商实现共享的配置契约,供应商实现通过 services.Configure<OutboundCallOptions>(...) 绑定。以下 JSON 是该选项类的完整字段(节名由各供应商扩展方法决定,通常为 OutboundCall)。
appsettings.json
{
"OutboundCall": {
"AccessKeyId": "your-secret-id",
"AccessKeySecret": "your-secret-key",
"Region": "ap-guangzhou",
"Endpoint": null,
"SdkAppId": "1400818883",
"Callers": [ "008602160662606" ],
"OverseaCallers": [ "0085230087235" ],
"CnIvrId": 21232,
"EnIvrId": 21408,
"OverseaSdkAppId": "1400818885",
"OverseaCnIvrId": null,
"OverseaEnIvrId": null,
"Whitelist": [ "5678" ],
"Blacklist": [ "008613800000000" ],
"RequireWhitelistInNonProduction": true,
"ExtraProperties": {}
}
}
| 配置项 | 说明 | 必填 | 默认值 |
|---|---|---|---|
AccessKeyId |
API 密钥 ID(腾讯 SecretId / 阿里 AccessKeyId / 容联 AccountSid) | 是 | 空字符串 |
AccessKeySecret |
API 密钥(腾讯 SecretKey / 阿里 AccessKeySecret / 容联 AuthToken) | 是 | 空字符串 |
Region |
地域(如 ap-guangzhou / cn-hangzhou / cn-north-4) |
否 | 空字符串(各供应商使用各自默认地域) |
Endpoint |
API 端点覆盖 | 否 | null(各供应商使用内置默认端点) |
SdkAppId |
应用 ID(腾讯 SdkAppId / 阿里 InstanceId / 容联 AppId) | 视供应商 | null |
Callers |
境内主叫号码列表(中国大陆外呼) | 是 | 空列表 |
OverseaCallers |
境外主叫号码列表(海外外呼) | 否 | 空列表 |
CnIvrId |
中文 IVR 流程 ID(境内呼叫) | 视供应商 | null |
EnIvrId |
英文 IVR 流程 ID(境外呼叫) | 视供应商 | null |
OverseaSdkAppId |
境外独立应用 ID(与境内不同时使用) | 否 | null |
OverseaCnIvrId |
境外中文 IVR 流程 ID | 否 | null |
OverseaEnIvrId |
境外英文 IVR 流程 ID | 否 | null |
Whitelist |
外呼白名单(仅允许呼叫这些号码,支持后缀匹配) | 否 | null |
Blacklist |
外呼黑名单(禁止呼叫这些号码) | 否 | null |
RequireWhitelistInNonProduction |
非生产环境是否强制要求白名单 | 否 | true |
ExtraProperties |
供应商特有扩展参数 | 否 | null |
注册服务
抽象层不提供 AddBitzsoft* 扩展方法。实际注册由各供应商实现包完成,例如:
腾讯云(从 IConfiguration 绑定)
using Bitzsoft.Integrations.OutboundCall;
// 由供应商扩展方法读取该配置节
builder.Configuration.GetSection("OutboundCall");
通过 Options 直接构造(测试 / 号码工具场景)
抽象层的号码工具、过滤器、编排引擎均为静态类或可直接实例化的类型,无需 DI 即可使用:
using Bitzsoft.Integrations.OutboundCall;
using Bitzsoft.Integrations.OutboundCall.PhoneNumber;
// 直接调用静态号码工具,无需注册服务
var (mainland, oversea) = PhoneNumberClassifier.Classify(rawPhones);
var options = new OutboundCallOptions
{
Whitelist = new() { "5678" },
Blacklist = new() { "008613800000000" }
};
var filtered = CallListFilter.Filter(callees, options);
使用示例
号码清洗 + 分类(批量)
using Bitzsoft.Integrations.OutboundCall.PhoneNumber;
var rawPhones = new[]
{
"0086-138-1234 5678",
"+12125550123",
"(+852)30087235",
"电话:13900000000"
};
// 1. 批量解析,自动全角转半角、剥离标签、识别区号、长度校验
var records = PhoneNumberValidator.ExtractAll(rawPhones);
foreach (var r in records)
{
// r.FullNumber "008613812345678" / "+12125550123"
// r.RegionCode "86" / "1"
// r.Region PhoneRegion.Mainland / PhoneRegion.Overseas
// r.IsValid true
}
// 2. 一键分类为境内 / 境外两组(去重)
var (mainland, oversea) = PhoneNumberClassifier.Classify(rawPhones);
// mainland: ["008613812345678", "008613900000000"]
// oversea: ["+12125550123", "+85230087235"]
单个号码校验
var isValid = PhoneNumberValidator.IsValid("008613812345678"); // true
var (regionCode, ok) = PhoneNumberValidator.IdentifyRegion("12025550123"); // ("1", true)
白名单 / 黑名单过滤
using Microsoft.Extensions.Logging;
var options = new OutboundCallOptions
{
Whitelist = new() { "5678" }, // 后缀匹配:仅允许以 5678 结尾
Blacklist = new() { "008613800000000" } // 精确 / 后缀匹配:禁止该号
};
var callees = new[] { "008613812345678", "008613800000000", "00861399995678" };
// 过滤顺序:黑名单优先排除 → 白名单仅保留
var filtered = CallListFilter.Filter(callees, options, logger);
// 结果:["008613812345678", "00861399995678"]
统一外呼接口(消费方)
using Bitzsoft.Integrations.OutboundCall;
using Bitzsoft.Integrations.OutboundCall.Dtos;
public class ReminderService
{
private readonly IOutboundCallService _outbound;
public ReminderService(IOutboundCallService outbound) => _outbound = outbound;
public async Task RunAsync(string[] rawPhones)
{
var (mainland, oversea) = PhoneNumberClassifier.Classify(rawPhones);
// 创建境内活动
var resp = await _outbound.CreateCampaignAsync(new CreateCampaignRequest
{
Name = "催办-境内",
Region = PhoneRegion.Mainland,
Callees = mainland.ToList(),
MaxAttempts = 2,
NotBefore = DateTimeOffset.UtcNow,
NotAfter = DateTimeOffset.UtcNow.AddHours(2)
});
// 暂停 / 恢复 / 停止
await _outbound.PauseCampaignAsync(resp.CampaignId);
await _outbound.ResumeCampaignAsync(resp.CampaignId);
await _outbound.StopCampaignAsync(resp.CampaignId);
// 查询活动列表
var campaigns = await _outbound.ListCampaignsAsync(pageIndex: 1, pageSize: 20);
// 查询话单
var records = await _outbound.QueryCallRecordsAsync(new QueryCallRecordsRequest
{
CampaignId = resp.CampaignId,
PageSize = 50
});
}
}
per-call 活动编排引擎(供应商内部使用)
using Bitzsoft.Integrations.OutboundCall.PerCall;
var runtime = new CampaignRuntime
{
Name = "通知外呼",
Region = PhoneRegion.Overseas,
Contacts = new() { "+12025550123", "+12025550124" }
};
var dialer = new CampaignDialer(logger);
// 注入实际的呼叫创建逻辑(含 429 退避),由各供应商实现提供
CallCreator creator = async (callee, ct) =>
{
var sid = await MyProviderCreateCallAsync(callee, ct);
return new CallAttempt(true, sid, null, null);
};
dialer.StartAsync(runtime, creator, cps: 1, new CancellationTokenSource());
// 暂停(保留断点)→ 恢复(从断点继续)→ 停止(终态)
dialer.Pause(runtime);
dialer.Resume(runtime, creator, cps: 1);
dialer.Stop(runtime);
核心类型一览
接口与选项
| 类型 | 说明 |
|---|---|
IOutboundCallService |
外呼服务统一抽象(创建 / 导入 / 启停 / 查询 / 话单) |
OutboundCallOptions |
全供应商共享配置(鉴权 / 境内外主叫分离 / IVR / 白名单) |
OutboundCallException |
外呼异常(含供应商标识 Provider、错误码 ErrorCode、请求 ID RequestId) |
PhoneRegion |
号码区域枚举:Mainland / Overseas / Unknown |
号码工具
| 类型 | 说明 |
|---|---|
PhoneNumberValidator |
静态号码校验器(Extract / ExtractAll / IsValid / IdentifyRegion,150+ 国家长度表) |
PhoneNumberClassifier |
静态批量分类器(Classify → 境内 / 境外两组) |
PhoneNumberRecord |
解析结果(FullNumber / RegionCode / Region / IsValid / CleanNumber) |
过滤与编排
| 类型 | 说明 |
|---|---|
CallListFilter |
静态白名单 / 黑名单过滤器(黑名单优先,后缀匹配) |
CampaignDialer |
per-call 供应商的活动编排引擎(CPS 节流、代次守卫、暂停 / 恢复断点) |
CampaignRuntime |
per-call 活动内存运行态(联系人队列、状态、断点索引、代次) |
CallAttempt |
单次呼叫尝试结果(Initiated / CallSid / ErrorCode) |
CallCreator |
委托:单个被叫的呼叫创建(由供应商注入,含退避重试) |
请求 / 响应 DTO
| 类型 | 说明 |
|---|---|
CreateCampaignRequest |
创建活动请求(Name / Region / Callees / NotBefore / NotAfter / MaxAttempts / StrategyType / QueueId) |
CreateCampaignResponse |
创建活动响应(CampaignId / Success / ErrorMessage) |
ImportContactsRequest |
导入名单请求(CampaignId / Contacts) |
StartCampaignRequest |
启动活动请求(CampaignId) |
CampaignInfo |
活动信息(CampaignId / Name / Status / CalleeCount / CalledCount / AnsweredCount) |
CampaignStatus |
活动状态枚举:Pending / Running / Paused / Completed / Stopped / Failed / Unknown |
CallRecord |
通话记录(CallId / Caller / Callee / StartTime / DurationSeconds / Status / RecordUrl) |
QueryCallRecordsRequest |
话单查询请求(CampaignId / 时间范围 / 分页) |
OutboundCallResult |
通用操作结果(Success / ErrorMessage / RequestId) |
安全说明
以下安全职责由宿主应用与各供应商实现承担,抽象层不内置实现。
- 凭据注入:
AccessKeyId/AccessKeySecret必须通过环境变量或 Secret Manager 注入,禁止硬编码在源代码或配置文件中 - 白名单(防误呼):开发 / 测试环境务必配置
Whitelist,RequireWhitelistInNonProduction默认true,非白名单号码将在创建活动时自动过滤跳过 - 号码脱敏:
PhoneNumberValidator处理后的FullNumber为明文号码,宿主应用在日志、持久化时应按需脱敏 - 主叫号码合法性:境内外主叫号码分离(
Callers/OverseaCallers)需符合各国家 / 地区电信监管要求
依赖
| 包 | 说明 |
|---|---|
Microsoft.Extensions.Logging.Abstractions |
日志抽象(ILogger) |
Microsoft.Extensions.Options |
选项模式(IOptionsMonitor<OutboundCallOptions>) |
相关包
- Bitzsoft.Integrations.OutboundCall.Tencent -- 腾讯云 CCC 实现
- Bitzsoft.Integrations.OutboundCall.Aliyun -- 阿里云 CCC 实现
- Bitzsoft.Integrations.OutboundCall.Huawei -- 华为云 CEC 实现
- Bitzsoft.Integrations.OutboundCall.Volcengine -- 火山引擎智能外呼实现
- Bitzsoft.Integrations.OutboundCall.Yuntongxun -- 容联云通讯实现
- Bitzsoft.Integrations.OutboundCall.Twilio -- Twilio 实现(per-call 编排)
- Bitzsoft.Integrations.OutboundCall.Vonage -- Vonage 实现(per-call 编排)
- Bitzsoft.Integrations.OutboundCall.All -- 聚合包(全部供应商)
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 is compatible. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Bitzsoft.Integrations.Core (>= 1.0.0-alpha.10)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.10)
- Microsoft.Extensions.Options (>= 10.0.10)
-
net5.0
- Bitzsoft.Integrations.Core (>= 1.0.0-alpha.10)
- Microsoft.Extensions.Logging.Abstractions (>= 5.0.0)
- Microsoft.Extensions.Options (>= 5.0.0)
-
net8.0
- Bitzsoft.Integrations.Core (>= 1.0.0-alpha.10)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.10)
- Microsoft.Extensions.Options (>= 10.0.10)
NuGet packages (8)
Showing the top 5 NuGet packages that depend on Bitzsoft.Integrations.OutboundCall:
| Package | Downloads |
|---|---|
|
Bitzsoft.Integrations.OutboundCall.All
外呼服务聚合包 — 包含全部供应商实现(腾讯云/阿里云/火山引擎/容联云/华为云/国际 Twilio/Vonage) |
|
|
Bitzsoft.Integrations.OutboundCall.Tencent
腾讯云外呼实现 — 手写 TC3-HMAC-SHA256 签名 + HttpClient,实现 IOutboundCallService 统一接口,支持境内外分离与白名单 |
|
|
Bitzsoft.Integrations.OutboundCall.Huawei
华为云云客服外呼实现 — 基于 CEC REST API(AK/SK 签名),实现 IOutboundCallService 统一接口 |
|
|
Bitzsoft.Integrations.OutboundCall.Volcengine
火山引擎智能外呼实现 — 基于 AI 外呼机器人 REST API,实现 IOutboundCallService 统一接口 |
|
|
Bitzsoft.Integrations.OutboundCall.Yuntongxun
容联云通讯外呼实现 — 基于 REST API(ACCOUNT SID + AUTH TOKEN 签名),实现 IOutboundCallService 统一接口 |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0-alpha.10 | 81 | 7/26/2026 |
| 1.0.0-alpha.9 | 82 | 7/12/2026 |
| 1.0.0-alpha.8 | 84 | 7/1/2026 |
| 1.0.0-alpha.7 | 107 | 6/16/2026 |
| 1.0.0-alpha.6 | 98 | 6/16/2026 |