增加微信分发机制

This commit is contained in:
sulu 2024-08-12 23:45:18 +08:00
parent ac96a27072
commit 66b61e0ac4
14 changed files with 487 additions and 38 deletions

View File

@ -12,10 +12,7 @@ public class ApplicationDbContext : DbContext
} }
private static readonly ILoggerFactory _loggerFactory = new LoggerFactory(); private static readonly ILoggerFactory _loggerFactory = new LoggerFactory();
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseLoggerFactory(_loggerFactory);
}
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
{ {

View File

@ -22,6 +22,7 @@
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
<PackageReference Include="SharpMap" Version="1.2.0" /> <PackageReference Include="SharpMap" Version="1.2.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="TencentCloudSDK" Version="3.0.1065" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -19,9 +19,9 @@ public class CloseWorker : BackgroundService
{ {
while (!stoppingToken.IsCancellationRequested) while (!stoppingToken.IsCancellationRequested)
{ {
using var scope = StaticServiceProvider.Current.CreateScope(); var scope = StaticServiceProvider.Current.CreateScope();
var settingServices = scope.ServiceProvider.GetRequiredService<SettingServices>(); var settingServices = scope.ServiceProvider.GetRequiredService<SettingServices>();
var options = settingServices.GetClientOptions(); var options = await settingServices.GetClientOptions();
if (_logger.IsEnabled(LogLevel.Information)) if (_logger.IsEnabled(LogLevel.Information))
{ {
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
@ -39,6 +39,7 @@ public class CloseWorker : BackgroundService
finally finally
{ {
await Task.Delay(options.Delay, stoppingToken); await Task.Delay(options.Delay, stoppingToken);
scope.Dispose();
} }
} }
} }

View File

@ -217,20 +217,20 @@ public class UserController : ControllerBase
} }
[HttpGet("/api/Setting")] [HttpGet("/api/Setting")]
public SpiderResponse<ClientOptions> GetClientOptions([FromServices] SettingServices settingServices) public async Task<SpiderResponse<ClientOptions>> GetClientOptions([FromServices] SettingServices settingServices)
{ {
return new SpiderResponse<ClientOptions>() return new SpiderResponse<ClientOptions>()
{ {
IsSuccess = true, Code = SpiderResponseCode.Success, Message = "获取配置成功", IsSuccess = true, Code = SpiderResponseCode.Success, Message = "获取配置成功",
Result = settingServices.GetClientOptions() Result = await settingServices.GetClientOptions()
}; };
} }
[HttpPost("/api/Setting")] [HttpPost("/api/Setting")]
public SpiderResponse<ClientOptions> SettingClientOptions([FromBody] ClientOptionsReq options, public async Task<SpiderResponse<ClientOptions>> SettingClientOptions([FromBody] ClientOptionsReq options,
[FromServices] SettingServices settingServices) [FromServices] SettingServices settingServices)
{ {
var setting = settingServices.GetClientOptions(); var setting = await settingServices.GetClientOptions();
setting.Delay = options.Delay; setting.Delay = options.Delay;
setting.DispatchingRunning = options.DispatchingRunning; setting.DispatchingRunning = options.DispatchingRunning;
setting.CloseFileRunning = options.CloseFileRunning; setting.CloseFileRunning = options.CloseFileRunning;

View File

@ -13,7 +13,7 @@ public class LogInfo
public LogInfo(string level, string message, [CallerMemberName] string from = "") public LogInfo(string level, string message, [CallerMemberName] string from = "")
{ {
Id = Guid.NewGuid(); Id = Guid.NewGuid();
CreateTime = DateTime.Now; CreateTime = DateTime.UtcNow;
Message = message; Message = message;
Level = level; Level = level;
From = from; From = from;

View File

@ -0,0 +1,185 @@
// <auto-generated />
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using WorkerService1;
using WorkerService1.Domains;
#nullable disable
namespace AutoDispathingWork.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20240812130932_dbv3")]
partial class dbv3
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.7")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("WorkerService1.Domains.LogInfo", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("CreateTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("From")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Level")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Message")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("CreateTime");
b.ToTable("LogInfos");
});
modelBuilder.Entity("WorkerService1.Domains.Polygon", b =>
{
b.Property<Guid?>("PolygonId")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasColumnType("text");
b.Property<List<List<Points>>>("Points")
.HasColumnType("jsonb");
b.Property<List<string>>("RangeCameras")
.HasColumnType("jsonb");
b.Property<string>("UserId")
.HasColumnType("text");
b.Property<string>("UserName")
.HasColumnType("text");
b.HasKey("PolygonId");
b.ToTable("Polygons");
});
modelBuilder.Entity("WorkerService1.Domains.SmsSendRecord", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Content")
.IsRequired()
.HasColumnType("text");
b.Property<string>("ErrorMessage")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsSuccess")
.HasColumnType("boolean");
b.Property<string>("PhoneNumber")
.IsRequired()
.HasColumnType("text");
b.Property<string>("RefrenceId")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("SendTime")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("SendTime");
b.ToTable("SmsSendRecords");
});
modelBuilder.Entity("WorkerService1.Dto.Configuration.ClientOptions", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("ApiGateway")
.IsRequired()
.HasColumnType("text");
b.Property<string>("CloseFileApi")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("CloseFileRunning")
.HasColumnType("boolean");
b.Property<int>("Delay")
.HasColumnType("integer");
b.Property<string>("DiposeOrder")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("DispatchingRunning")
.HasColumnType("boolean");
b.Property<string>("GetCamerasApi")
.IsRequired()
.HasColumnType("text");
b.Property<string>("GetTaskApi")
.IsRequired()
.HasColumnType("text");
b.Property<string>("GetUserApi")
.IsRequired()
.HasColumnType("text");
b.Property<string>("LoginApi")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("text")
.HasAnnotation("Relational:JsonPropertyName", "password");
b.Property<string>("UserName")
.IsRequired()
.HasColumnType("text")
.HasAnnotation("Relational:JsonPropertyName", "username");
b.HasKey("Id");
b.ToTable("ClientOptions");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AutoDispathingWork.Migrations
{
/// <inheritdoc />
public partial class dbv3 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "PhoneNumber",
table: "Polygons",
type: "text",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "PhoneNumber",
table: "Polygons");
}
}
}

View File

@ -62,6 +62,9 @@ namespace AutoDispathingWork.Migrations
.IsRequired() .IsRequired()
.HasColumnType("text"); .HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasColumnType("text");
b.Property<List<List<Points>>>("Points") b.Property<List<List<Points>>>("Points")
.HasColumnType("jsonb"); .HasColumnType("jsonb");

View File

@ -30,7 +30,7 @@ builder.Services.AddCors(x =>
}); });
}); });
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection"); var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(x => builder.Services.AddDbContextPool<ApplicationDbContext>(x =>
{ {
var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString); var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString);
// 启用动态 JSON 序列化 // 启用动态 JSON 序列化
@ -40,7 +40,6 @@ builder.Services.AddDbContext<ApplicationDbContext>(x =>
.UseLoggerFactory(new LoggerFactory()) .UseLoggerFactory(new LoggerFactory())
.LogTo(Console.WriteLine, LogLevel.Information); .LogTo(Console.WriteLine, LogLevel.Information);
x.ConfigureWarnings(warnings => warnings.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning)); x.ConfigureWarnings(warnings => warnings.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning));
} }
); );
builder.Services.AddSwaggerGen(); builder.Services.AddSwaggerGen();
@ -53,7 +52,8 @@ builder.Services.AddMemoryCache();
builder.Services.AddLogging(); builder.Services.AddLogging();
// builder.Services.AddMvcCore(); // builder.Services.AddMvcCore();
builder.Services.AddSingleton<SpiderServices>(); builder.Services.AddSingleton<SpiderServices>();
builder.Services.AddScoped<SettingServices>(); builder.Services.AddSingleton<SettingServices>();
builder.Services.AddSingleton<WechatRobot>();
// #if !DEBUG // #if !DEBUG
builder.Services.AddHostedService<Worker>(); builder.Services.AddHostedService<Worker>();
builder.Services.AddHostedService<CloseWorker>(); builder.Services.AddHostedService<CloseWorker>();
@ -67,6 +67,9 @@ app.UseCors("AllowAllOrigin");
#if DEBUG #if DEBUG
app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI(); app.UseSwaggerUI();
var wechatRobot = app.Services.GetService<WechatRobot>();
wechatRobot.SetKey("e68b3791-e040-4c9b-9ac0-6f424e662185");
#endif #endif
app.UseDefaultFiles(); app.UseDefaultFiles();

View File

@ -1,5 +1,6 @@
using AutoDispathingWork.Utils; using AutoDispathingWork.Utils;
using LiteDB; using LiteDB;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using WorkerService1.Dto.Configuration; using WorkerService1.Dto.Configuration;
@ -10,7 +11,6 @@ public class SettingServices
private readonly IOptionsMonitor<ClientOptions> _optionsMonitor; private readonly IOptionsMonitor<ClientOptions> _optionsMonitor;
public SettingServices(IOptionsMonitor<ClientOptions> optionsMonitor) public SettingServices(IOptionsMonitor<ClientOptions> optionsMonitor)
{ {
_optionsMonitor = optionsMonitor; _optionsMonitor = optionsMonitor;
@ -21,23 +21,27 @@ public class SettingServices
SettingClientOptions(_optionsMonitor.CurrentValue); SettingClientOptions(_optionsMonitor.CurrentValue);
} }
public ClientOptions GetClientOptions() public async Task<ClientOptions> GetClientOptions()
{ {
using var scope = StaticServiceProvider.Current.CreateScope(); var scope = StaticServiceProvider.Current.CreateScope();
var _db = scope.ServiceProvider.GetService<ApplicationDbContext>(); var _db = scope.ServiceProvider.GetService<ApplicationDbContext>();
var clientOptions = _db.ClientOptions; var clientOptions = _db.ClientOptions;
var dbOptions = clientOptions.FirstOrDefault(); var dbOptions = await clientOptions.FirstOrDefaultAsync();
if (dbOptions != null) if (dbOptions != null)
{ {
await _db.DisposeAsync();
scope.Dispose();
return dbOptions; return dbOptions;
} }
await _db.DisposeAsync();
scope.Dispose();
return _optionsMonitor.CurrentValue; return _optionsMonitor.CurrentValue;
} }
public ClientOptions SettingClientOptions(ClientOptions options) public ClientOptions SettingClientOptions(ClientOptions options)
{ {
using var scope = StaticServiceProvider.Current.CreateScope(); var scope = StaticServiceProvider.Current.CreateScope();
var _db = scope.ServiceProvider.GetService<ApplicationDbContext>(); var _db = scope.ServiceProvider.GetService<ApplicationDbContext>();
var clientOptions = _db.ClientOptions; var clientOptions = _db.ClientOptions;
var dbOptions = clientOptions.FirstOrDefault(); var dbOptions = clientOptions.FirstOrDefault();
@ -49,6 +53,9 @@ public class SettingServices
clientOptions.Add(options); clientOptions.Add(options);
_db.SaveChanges(); _db.SaveChanges();
_db.Dispose();
scope.Dispose();
return options; return options;
} }
} }

View File

@ -1,12 +1,117 @@
namespace WorkerService1.Services; using TencentCloud.Common;
using TencentCloud.Common.Profile;
using TencentCloud.Cvm.V20170312;
using TencentCloud.Cvm.V20170312.Models;
using TencentCloud.Sms.V20210111;
using TencentCloud.Sms.V20210111.Models;
namespace WorkerService1.Services;
public class SmsService public class SmsService
{ {
public Task SendSmsAsync(string phoneNumber, string message, string refrenceId) public Task SendSmsAsync(string phoneNumber, string message, string refrenceId)
{ {
//发送短信逻辑 try
{
// 必要步骤:
// 实例化一个认证对象,入参需要传入腾讯云账户密钥对 SecretIdSecretKey。
// 为了保护密钥安全,建议将密钥设置在环境变量中或者配置文件中。
// 硬编码密钥到代码中有可能随代码泄露而暴露,有安全隐患,并不推荐。
// 这里采用的是从环境变量读取的方式,需要在环境变量中先设置这两个值。
// SecretId、SecretKey 查询https://console.cloud.tencent.com/cam/capi
Credential cred = new Credential
{
SecretId = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_ID"),
SecretKey = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_KEY")
};
/* :
* */
ClientProfile clientProfile = new ClientProfile
{
/* SDK默认用TC3-HMAC-SHA256进行签名
* */
SignMethod = ClientProfile.SIGN_TC3SHA256
};
/*
* */
var httpProfile = new HttpProfile
{
/* SDK默认使用POST方法
* 使GET方法GET方法无法处理一些较大的请求 */
ReqMethod = "GET",
Timeout = 10, // 请求连接超时时间,单位为秒(默认60秒)
/* 指定接入地域域名,默认就近地域接入域名为 sms.tencentcloudapi.com ,也支持指定地域域名访问,例如广州地域的域名为 sms.ap-guangzhou.tencentcloudapi.com */
Endpoint = "sms.tencentcloudapi.com"
};
// 代理服务器,当您的环境下有代理服务器时设定(无需要直接忽略)
// httpProfile.WebProxy = Environment.GetEnvironmentVariable("HTTPS_PROXY");
clientProfile.HttpProfile = httpProfile;
/* (sms为例)client对象
* ap-guangzhou https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8 */
var client = new SmsClient(cred, "ap-guangzhou", clientProfile);
/*
* SDK源码确定SendSmsRequest有哪些属性可以设置
*
* 使IDE进行开发便 */
var req = new SendSmsRequest
{
/* :
* SDK采用的是指针风格指定参数使
* SDK提供对基本类型的指针引用封装函数
*
* : https://console.cloud.tencent.com/smsv2
* : https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81 */
/* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId示例如1400006666 */
// 应用 ID 可前往 [短信控制台](https://console.cloud.tencent.com/smsv2/app-manage) 查看
SmsSdkAppId = "1400787878",
/* 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名 */
// 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看
SignName = "腾讯云",
/* 模板 ID: 必须填写已审核通过的模板 ID */
// 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看
TemplateId = "449739",
/* 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致,若无模板参数,则设置为空 */
TemplateParamSet = new String[] { "1234" },
/* E.164 +[][]
* +8613711112222 + 8613711112222200*/
PhoneNumberSet = new String[] { "+86" + phoneNumber },
/* 用户的 session 内容(无需要可忽略): 可以携带用户侧 ID 等上下文信息server 会原样返回 */
SessionContext = "",
/* 短信码号扩展号(无需要可忽略): 默认未开通,如需开通请联系 [腾讯云短信小助手] */
ExtendCode = "",
/* 国内短信无需填写该项;国际/港澳台短信已申请独立 SenderId 需要填写该字段,默认使用公共 SenderId无需填写该字段。注月度使用量达到指定量级可申请独立 SenderId 使用,详情请联系 [腾讯云短信小助手](https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81)。 */
SenderId = ""
};
SendSmsResponse resp = client.SendSmsSync(req);
// 输出json格式的字符串回包
Console.WriteLine(AbstractModel.ToJsonString(resp));
/*
* [FailedOperation.SignatureIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.signatureincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
* [FailedOperation.TemplateIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.templateincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
* [UnauthorizedOperation.SmsSdkAppIdVerifyFail](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunauthorizedoperation.smssdkappidverifyfail-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
* [UnsupportedOperation.ContainDomesticAndInternationalPhoneNumber](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunsupportedoperation.containdomesticandinternationalphonenumber-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
* [](https://tccc.qcloud.com/web/im/index.html#/chat?webAppId=8fa15978f85cb41f7e2ea36920cb3ae1&title=Sms)
*/
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Console.Read();
Console.WriteLine($"Sending SMS to {phoneNumber} with message {message} and refrenceId {refrenceId}");
return Task.CompletedTask; return Task.CompletedTask;
} }
} }

View File

@ -44,7 +44,7 @@ public class SpiderServices
{ {
using var scope = _serviceProvider.CreateScope(); using var scope = _serviceProvider.CreateScope();
using var client = _httpClientFactory.CreateClient(); using var client = _httpClientFactory.CreateClient();
var getClientOptions = scope.ServiceProvider.GetService<SettingServices>()? var getClientOptions = await scope.ServiceProvider.GetService<SettingServices>()?
.GetClientOptions(); .GetClientOptions();
var loginApi = getClientOptions.LoginUrl; var loginApi = getClientOptions.LoginUrl;
var json = $@" var json = $@"
@ -137,7 +137,7 @@ public class SpiderServices
// {"state":[1],"pageNo":1,"pageSize":50,"sortType":[20,10],"createStartTime":1697290618034,"createEndTime":1699882618034} // {"state":[1],"pageNo":1,"pageSize":50,"sortType":[20,10],"createStartTime":1697290618034,"createEndTime":1699882618034}
// {\"state\":[1],\"pageNo\":1,\"pageSize\":50,\"sortType\":[20,10],\"createStartTime\":1697204639551,\"createEndTime\":1697204639551} // {\"state\":[1],\"pageNo\":1,\"pageSize\":50,\"sortType\":[20,10],\"createStartTime\":1697204639551,\"createEndTime\":1697204639551}
using var scope = _serviceProvider.CreateScope(); using var scope = _serviceProvider.CreateScope();
var getClientOptions = scope.ServiceProvider.GetService<SettingServices>()? var getClientOptions = await scope.ServiceProvider.GetService<SettingServices>()?
.GetClientOptions(); .GetClientOptions();
var taskPath = getClientOptions.GetTaskUrl; var taskPath = getClientOptions.GetTaskUrl;
var response = await client.PostAsync(taskPath, var response = await client.PostAsync(taskPath,
@ -183,7 +183,7 @@ public class SpiderServices
client.DefaultRequestHeaders.Add("module-source", "megcity-web"); client.DefaultRequestHeaders.Add("module-source", "megcity-web");
var request = query ?? new UserQuery(); var request = query ?? new UserQuery();
using var scope = _serviceProvider.CreateScope(); using var scope = _serviceProvider.CreateScope();
var getClientOptions = scope.ServiceProvider.GetService<SettingServices>()? var getClientOptions = await scope.ServiceProvider.GetService<SettingServices>()?
.GetClientOptions(); .GetClientOptions();
var userPath = getClientOptions.GetUserUrl; var userPath = getClientOptions.GetUserUrl;
var response = await client.PostAsync(userPath, var response = await client.PostAsync(userPath,
@ -248,7 +248,7 @@ public class SpiderServices
TypeCode = typeCode TypeCode = typeCode
}; };
using var scope = _serviceProvider.CreateScope(); using var scope = _serviceProvider.CreateScope();
var getClientOptions = scope.ServiceProvider.GetService<SettingServices>()? var getClientOptions = await scope.ServiceProvider.GetService<SettingServices>()?
.GetClientOptions(); .GetClientOptions();
var dispatchPath = string.Format(getClientOptions.DiposeOrderUrl, caseNumber); var dispatchPath = string.Format(getClientOptions.DiposeOrderUrl, caseNumber);
@ -263,8 +263,8 @@ public class SpiderServices
if (spiderRes?.code == 0) if (spiderRes?.code == 0)
{ {
var msg = $"成功分发任务,任务编号:{caseNumber},任务地址:{cameraName},任务类型:{typeCode},处理人:{userRealName}"; var msg = $"成功分发任务,任务编号:{caseNumber},任务地址:{cameraName},任务类型:{typeCode},处理人:{userRealName}";
var smsService = scope.ServiceProvider.GetService<SmsService>(); var smsService = scope.ServiceProvider.GetRequiredService<WechatRobot>();
await smsService.SendSmsAsync(phoneNumber:phone, $"{userRealName}您好,您有一条新案件,请及时处置,谢谢。",caseNumber); await smsService.SendText($"{userRealName},您好,您有一条新案件,请及时处置,谢谢。", false);
_logger.LogInformation(msg); _logger.LogInformation(msg);
return new SpiderResponse<object>() return new SpiderResponse<object>()
@ -303,7 +303,7 @@ public class SpiderServices
form.Add(new StringContent(caseNumber), "caseNumber"); form.Add(new StringContent(caseNumber), "caseNumber");
form.Add(new StringContent(suggestion), "suggestion"); form.Add(new StringContent(suggestion), "suggestion");
using var scope = _serviceProvider.CreateScope(); using var scope = _serviceProvider.CreateScope();
var getClientOptions = scope.ServiceProvider.GetService<SettingServices>()? var getClientOptions = await scope.ServiceProvider.GetService<SettingServices>()?
.GetClientOptions(); .GetClientOptions();
var closeFile = getClientOptions.CloseFileUrl; var closeFile = getClientOptions.CloseFileUrl;
@ -352,7 +352,7 @@ public class SpiderServices
{{""managementIds"":[""6e9232ef-7b84-11e8-86b1-6c92bf4e6960""],""name"":""{name}"" ,""action"":""all"",""pageNo"":1,""pageSize"":200}} {{""managementIds"":[""6e9232ef-7b84-11e8-86b1-6c92bf4e6960""],""name"":""{name}"" ,""action"":""all"",""pageNo"":1,""pageSize"":200}}
"; ";
using var scope = _serviceProvider.CreateScope(); using var scope = _serviceProvider.CreateScope();
var getClientOptions = scope.ServiceProvider.GetService<SettingServices>()? var getClientOptions = await scope.ServiceProvider.GetService<SettingServices>()?
.GetClientOptions(); .GetClientOptions();
var getCamersPath = getClientOptions.GetCamerasUrl; var getCamersPath = getClientOptions.GetCamerasUrl;
var response = await client.PostAsync(getCamersPath, var response = await client.PostAsync(getCamersPath,

View File

@ -0,0 +1,118 @@
using System.Text;
using Microsoft.Net.Http.Headers;
namespace WorkerService1;
public class WechatRobot
{
private const string ApiUrl = "https://qyapi.weixin.qq.com";
private const int TextUtf8MaxLength = 2000;
private const int MarkdownUtf8MaxLength = 4000;
private string _key;
private static readonly MediaTypeHeaderValue ApplicationJson =
new MediaTypeHeaderValue("application/json")
{
Charset = "utf-8"
};
private readonly HttpClient _client;
private readonly ILogger<WechatRobot> _logger;
public WechatRobot(ILogger<WechatRobot> logger, HttpClient client)
{
client.BaseAddress = new Uri(ApiUrl);
this._logger = logger;
_client = client;
}
public WechatRobot SetKey(string key)
{
this._key = key;
return this;
}
public async Task SendText(string content, bool isAtAll)
{
await this.Post($"/cgi-bin/webhook/send?key={_key}", BuildTextBody(content, isAtAll));
}
public async Task SendMarkdown(string content)
{
await this.Post($"/cgi-bin/webhook/send?key={_key}", BuildMarkdownBody(content));
}
private string BuildTextBody(string content, bool isAtAll)
{
var result = new StringBuilder("{\"msgtype\": \"text\",");
result.Append("\"text\": {");
result.Append($"\"content\": \"{Substring(content, TextUtf8MaxLength)}\",");
if (isAtAll)
{
result.Append($"\"mentioned_list\":[\"@all\"]");
}
result.Append("}}");
return result.ToString();
}
private string BuildMarkdownBody(string content)
{
var result = new StringBuilder("{\"msgtype\": \"markdown\",");
result.Append("\"markdown\": {");
result.Append($"\"content\": \"{Substring(FilterSpecialChar(content), MarkdownUtf8MaxLength)}\"");
result.Append("}}");
return result.ToString();
}
/// <summary>
/// 截取字符串
/// </summary>
/// <param name="content">字符串内容</param>
/// <param name="maxLength">字符串最大长度</param>
/// <returns>如果长度超出则按照 maxLength 截取返回,若没超出则原样返回 </returns>
private string Substring(string content, int maxLength)
{
var bytes = Encoding.UTF8.GetBytes(content);
if (bytes.Length > maxLength)
{
var temporaryBytes = bytes.Take(maxLength).ToArray();
return $"{Encoding.UTF8.GetString(temporaryBytes)}";
}
return content;
}
/// <summary>
/// 过滤 markdown 特殊字符
/// </summary>
/// <param name="content"></param>
/// <returns></returns>
private string FilterSpecialChar(string content) => content?.Replace("\"", "''").Replace("`", "'");
private async Task Post(string url, string parameters)
{
try
{
HttpContent content = null;
if (!String.IsNullOrEmpty(parameters))
{
content = new StringContent(parameters, Encoding.UTF8);
if (content.Headers.ContentType != null)
{
content.Headers.ContentType.MediaType = "application/json";
content.Headers.ContentType.CharSet = "utf-8";
}
}
var resp = await _client.PostAsync(url, content);
await resp.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
this._logger.LogError(ex, $"WechatRobot Post fail,Url:{url},Parameters:{parameters}");
}
}
}

View File

@ -17,9 +17,9 @@ public class Worker : BackgroundService
{ {
while (!stoppingToken.IsCancellationRequested) while (!stoppingToken.IsCancellationRequested)
{ {
using var scope = StaticServiceProvider.Current.CreateScope(); var scope = StaticServiceProvider.Current.CreateScope();
var settingServices = scope.ServiceProvider.GetRequiredService<SettingServices>(); var settingServices = scope.ServiceProvider.GetRequiredService<SettingServices>();
var options = settingServices.GetClientOptions(); var options = await settingServices.GetClientOptions();
if (_logger.IsEnabled(LogLevel.Information)) if (_logger.IsEnabled(LogLevel.Information))
{ {
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now.LocalDateTime); _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now.LocalDateTime);
@ -37,6 +37,7 @@ public class Worker : BackgroundService
finally finally
{ {
await Task.Delay(options.Delay, stoppingToken); await Task.Delay(options.Delay, stoppingToken);
scope.Dispose();
} }
} }
} }