18 changed files with 608 additions and 17 deletions
-
90Shentun.Sms.Client/CreateSmsTaskDto.cs
-
18Shentun.Sms.Client/Shentun.Sms.Client.csproj
-
263Shentun.Sms.Client/SmsClientHelper.cs
-
14Shentun.Sms.Client/SmsLoginInputDto.cs
-
16Shentun.Sms.Client/SmsLoginOutDataDto.cs
-
17Shentun.Sms.Client/SmsWebApiOutDto.cs
-
7Shentun.WebPeis.sln
-
30src/Shentun.Utilities/JsonDateTimeConverter.cs
-
27src/Shentun.WebPeis.Application/Persons/PersonAppService.cs
-
1src/Shentun.WebPeis.Application/Shentun.WebPeis.Application.csproj
-
3src/Shentun.WebPeis.Domain/Models/AppointScheduleTemplate.cs
-
45src/Shentun.WebPeis.Domain/Models/AppointScheduleTemplateTime.cs
-
1src/Shentun.WebPeis.Domain/Shentun.WebPeis.Domain.csproj
-
48src/Shentun.WebPeis.EntityFrameworkCore/Configures/AppointScheduleTemplateTimeConfigure.cs
-
2src/Shentun.WebPeis.EntityFrameworkCore/EntityFrameworkCore/WebPeisDbContext.cs
-
10src/Shentun.WebPeis.HttpApi.Host/appsettings.json
-
23test/Shentun.WebPeis.Application.Tests/PersonAppServiceTest.cs
-
10test/Shentun.WebPeis.Application.Tests/appsettings.json
@ -0,0 +1,90 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Shentun.Sms.Client |
|||
{ |
|||
public class CreateSmsTaskDto |
|||
{ |
|||
/// <summary>
|
|||
/// 短信类别ID
|
|||
/// </summary>
|
|||
public Guid SmsTypeId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 人员ID
|
|||
/// </summary>
|
|||
public string PersonId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 姓名
|
|||
/// </summary>
|
|||
public string PersonName { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 手机号国家代码
|
|||
/// </summary>
|
|||
public string CountryCode { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 手机号
|
|||
/// </summary>
|
|||
public string MobileTelephone { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 短信内容
|
|||
/// </summary>
|
|||
public string Content { get; set; } |
|||
|
|||
|
|||
/// <summary>
|
|||
/// 应用ID
|
|||
/// </summary>
|
|||
public Guid SmsAppId { get; set; } |
|||
|
|||
|
|||
/// <summary>
|
|||
/// 第三方系统唯一ID
|
|||
/// </summary>
|
|||
public string? ThirdId { get; set; } |
|||
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// 任务周期类别
|
|||
/// </summary>
|
|||
public char TaskCycleType { get; set; } |
|||
|
|||
|
|||
/// <summary>
|
|||
/// 任务表达式
|
|||
/// </summary>
|
|||
public string? TaskCorn { get; set; } |
|||
|
|||
|
|||
/// <summary>
|
|||
/// 停止执行时间
|
|||
/// </summary>
|
|||
public string? StopTime { get; set; } |
|||
|
|||
|
|||
///// <summary>
|
|||
///// 是否启用
|
|||
///// </summary>
|
|||
//public char IsActive { get; set; } = 'Y';
|
|||
|
|||
/// <summary>
|
|||
/// 发送者用户ID
|
|||
/// </summary>
|
|||
public string? SenderId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 发送者用户名
|
|||
/// </summary>
|
|||
public string? SenderName { get; set; } |
|||
|
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net8.0</TargetFramework> |
|||
<ImplicitUsings>enable</ImplicitUsings> |
|||
<Nullable>enable</Nullable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" /> |
|||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.6.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\src\Shentun.Utilities\Shentun.Utilities.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,263 @@ |
|||
using Microsoft.Extensions.Configuration; |
|||
using Shentun.Utilities; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IdentityModel.Tokens.Jwt; |
|||
using System.Linq; |
|||
using System.Net.Http.Headers; |
|||
using System.Text; |
|||
using System.Text.Json; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Shentun.Sms.Client |
|||
{ |
|||
public class SmsClientHelper |
|||
{ |
|||
protected static IConfiguration? _appConfig; |
|||
private static string? _baseAddress; |
|||
private static string? _createSmsTaskUrl; |
|||
private static string? _accesToken; |
|||
private static string? _user; |
|||
private static string? _password; |
|||
private static string? _email; |
|||
static SmsClientHelper() |
|||
{ |
|||
_appConfig = new ConfigurationBuilder() |
|||
.SetBasePath(DirectoryHelper.GetAppDirectory()) // 设置基础路径为当前目录
|
|||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) |
|||
.Build(); |
|||
_baseAddress = _appConfig.GetSection("Sms") |
|||
.GetSection("BaseAddress").Value; |
|||
_createSmsTaskUrl = _appConfig.GetSection("Sms") |
|||
.GetSection("CreateSmsTaskUrl").Value; |
|||
_user = _appConfig.GetSection("Sms") |
|||
.GetSection("User").Value; |
|||
_password = _appConfig.GetSection("Sms") |
|||
.GetSection("Password").Value; |
|||
} |
|||
|
|||
public async static Task CreateSmsTask(CreateSmsTaskDto createSmsTaskDto) |
|||
{ |
|||
if (createSmsTaskDto == null) |
|||
{ |
|||
throw new Exception("createSmsTaskDto参数不能为空"); |
|||
} |
|||
|
|||
if (createSmsTaskDto.MobileTelephone.Length != 11) |
|||
{ |
|||
|
|||
throw new Exception("手机号长度必须为11位"); |
|||
} |
|||
if(string.IsNullOrWhiteSpace(createSmsTaskDto.PersonId)) |
|||
{ |
|||
throw new Exception("人员ID不能为空"); |
|||
|
|||
} |
|||
if (string.IsNullOrWhiteSpace(createSmsTaskDto.PersonName)) |
|||
{ |
|||
throw new Exception("姓名不能为空"); |
|||
|
|||
} |
|||
if (string.IsNullOrWhiteSpace(createSmsTaskDto.CountryCode)) |
|||
{ |
|||
throw new Exception("国家编码不能为空"); |
|||
|
|||
} |
|||
if (string.IsNullOrWhiteSpace(createSmsTaskDto.Content)) |
|||
{ |
|||
throw new Exception("短信内容不能为空"); |
|||
|
|||
} |
|||
if (createSmsTaskDto.SmsAppId == null || createSmsTaskDto.SmsAppId == Guid.Empty) |
|||
{ |
|||
var smsAppIdStr = _appConfig.GetSection("Sms") |
|||
.GetSection("SmsAppId").Value; |
|||
if (!Guid.TryParse(smsAppIdStr, out var smsAppId)) |
|||
{ |
|||
throw new Exception("解析短信应用ID错误"); |
|||
} |
|||
createSmsTaskDto.SmsAppId = smsAppId; |
|||
} |
|||
|
|||
var reslut = await CallAppServiceAsync<CreateSmsTaskDto, object>(_createSmsTaskUrl, createSmsTaskDto); |
|||
} |
|||
|
|||
public async static Task CreateVerifySmsTask(CreateSmsTaskDto smsDto) |
|||
{ |
|||
var verifySmsTypeIdStr = _appConfig.GetSection("Sms") |
|||
.GetSection("VerifySmsTypeId").Value; |
|||
if (!Guid.TryParse(verifySmsTypeIdStr, out var verifySmsTypeId)) |
|||
{ |
|||
throw new Exception("解析校验短信类别ID错误"); |
|||
} |
|||
smsDto.SmsTypeId = verifySmsTypeId; |
|||
await CreateSmsTask(smsDto); |
|||
} |
|||
public async static Task<TOut> CallAppServiceAsync<TInput, TOut>(string url, TInput data, string method = "post") |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(_baseAddress)) |
|||
{ |
|||
throw new Exception("_baseAddress不能为空"); |
|||
} |
|||
string baseAddress = _baseAddress; |
|||
await CheckLoginAsync(); |
|||
using (var httpClientHandler = new HttpClientHandler()) |
|||
{ |
|||
using (var httpClient = new HttpClient(httpClientHandler)) |
|||
{ |
|||
httpClient.BaseAddress = new Uri(baseAddress); |
|||
|
|||
httpClient.DefaultRequestHeaders.Accept.Add( |
|||
new MediaTypeWithQualityHeaderValue("application/json"));//设置accept标头,告诉JSON是可接受的响应类型
|
|||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _accesToken); |
|||
var jsonOptions = new JsonSerializerOptions |
|||
{ |
|||
WriteIndented = true, // 设置为true以便于可读性更好的JSON输出
|
|||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase, |
|||
// 如果你想要对日期进行格式化,可以使用JsonConverter
|
|||
Converters = { new JsonDateTimeConverter("yyyy-MM-dd HH:mm:ss") } |
|||
}; |
|||
|
|||
var sendData = System.Text.Json.JsonSerializer.Serialize(data, jsonOptions); |
|||
using (HttpContent httpContent = new StringContent(sendData)) |
|||
{ |
|||
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); |
|||
HttpResponseMessage response = null; |
|||
if (method == "post") |
|||
{ |
|||
response = await httpClient.PostAsync(url, httpContent); |
|||
} |
|||
else |
|||
{ |
|||
response = await httpClient.GetAsync(url); |
|||
} |
|||
|
|||
string result; |
|||
if (!response.IsSuccessStatusCode) |
|||
{ |
|||
result = response.Content.ReadAsStringAsync().Result; |
|||
throw new Exception("http通信错误:" + response.StatusCode + ",结果:" + result); |
|||
} |
|||
result = await response.Content.ReadAsStringAsync(); |
|||
|
|||
var resultDto = System.Text.Json.JsonSerializer.Deserialize<SmsWebApiOutDto<TOut>>(result,jsonOptions); |
|||
if (resultDto.Code == -1) |
|||
{ |
|||
throw new Exception($"调用WebApi失败,返回-1,消息:" + resultDto.Message); |
|||
} |
|||
return resultDto.Data; |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
public async static Task<SmsWebApiOutDto<SmsLoginOutDataDto>> LoginAsync() |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(_user)) |
|||
{ |
|||
throw new Exception("SelfUser不能为空"); |
|||
} |
|||
if (string.IsNullOrWhiteSpace(_password)) |
|||
{ |
|||
throw new Exception("SelfPassword不能为空"); |
|||
} |
|||
var relult = await LoginAsync(_user, _password); |
|||
return relult; |
|||
} |
|||
public async static Task<SmsWebApiOutDto<SmsLoginOutDataDto>> LoginAsync(string userId, string password) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(userId)) |
|||
{ |
|||
throw new Exception("用户ID不能为空"); |
|||
} |
|||
if (string.IsNullOrWhiteSpace(password)) |
|||
{ |
|||
throw new Exception("密码不能为空"); |
|||
} |
|||
using (var httpClientHandler = new HttpClientHandler()) |
|||
{ |
|||
using (var httpClient = new HttpClient(httpClientHandler)) |
|||
{ |
|||
httpClient.BaseAddress = new Uri(_baseAddress); |
|||
|
|||
httpClient.DefaultRequestHeaders.Accept.Add( |
|||
new MediaTypeWithQualityHeaderValue("application/json"));//设置accept标头,告诉JSON是可接受的响应类型
|
|||
var url = "api/app/CustomerUser/UserLogin"; |
|||
var jsonOptions = new JsonSerializerOptions |
|||
{ |
|||
WriteIndented = true, // 设置为true以便于可读性更好的JSON输出
|
|||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase, |
|||
// 如果你想要对日期进行格式化,可以使用JsonConverter
|
|||
Converters = { new JsonDateTimeConverter("yyyy-MM-dd HH:mm:ss") } |
|||
}; |
|||
var loginUser = new SmsLoginInputDto() |
|||
{ |
|||
UserName = userId, |
|||
Password = password |
|||
}; |
|||
var sendData = System.Text.Json.JsonSerializer.Serialize(loginUser); |
|||
using (HttpContent httpContent = new StringContent(sendData)) |
|||
{ |
|||
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); |
|||
HttpResponseMessage response = await httpClient.PostAsync(url, httpContent); |
|||
string result; |
|||
if (!response.IsSuccessStatusCode) |
|||
{ |
|||
result = response.Content.ReadAsStringAsync().Result; |
|||
throw new Exception("登录短信系统时http通信错误:" + response.StatusCode + ",结果:" + result); |
|||
} |
|||
result = await response.Content.ReadAsStringAsync(); |
|||
var restultDto = System.Text.Json.JsonSerializer.Deserialize<SmsWebApiOutDto<SmsLoginOutDataDto>>(result, jsonOptions); |
|||
if (restultDto == null) |
|||
{ |
|||
throw new Exception("返回结果是空"); |
|||
} |
|||
if (restultDto.Code != 1) |
|||
{ |
|||
throw new Exception($"短信登录失败{restultDto.Message}"); |
|||
} |
|||
_accesToken = restultDto.Data.access_token; |
|||
return restultDto; |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
private async static Task CheckLoginAsync() |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(_accesToken)) |
|||
{ |
|||
await LoginAsync(); |
|||
} |
|||
else |
|||
{ |
|||
var handler = new JwtSecurityTokenHandler(); |
|||
var payload = handler.ReadJwtToken(_accesToken).Payload; |
|||
var claims = payload.Claims; |
|||
var exp = claims.First(claim => claim.Type == "exp").Value; |
|||
if (exp == null) |
|||
{ |
|||
await LoginAsync(); |
|||
} |
|||
else |
|||
{ |
|||
if (long.TryParse(exp, out var expires)) |
|||
{ |
|||
var expireTime = DateTimeOffset.FromUnixTimeSeconds(expires).LocalDateTime; |
|||
if (expireTime <= DateTime.Now) |
|||
{ |
|||
await LoginAsync(); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
await LoginAsync(); |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Shentun.Sms.Client |
|||
{ |
|||
public class SmsLoginInputDto |
|||
{ |
|||
public string UserName { get; set; } |
|||
public string Password { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Shentun.Sms.Client |
|||
{ |
|||
public class SmsLoginOutDataDto |
|||
{ |
|||
public string access_token { get; set; } |
|||
public string token_type { get; set; } |
|||
public int expires_in { get; set; } |
|||
public string refresh_token { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using NPOI.SS.Formula.Functions; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Shentun.Sms.Client |
|||
{ |
|||
public class SmsWebApiOutDto<T> |
|||
{ |
|||
public int Code { get; set; } |
|||
public string Message { get; set; } |
|||
public T? Data { get; set; } |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Text.Json; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Shentun.Utilities |
|||
{ |
|||
public class JsonDateTimeConverter : System.Text.Json.Serialization.JsonConverter<DateTime> |
|||
{ |
|||
private readonly string _dateFormat; |
|||
|
|||
public JsonDateTimeConverter(string dateFormat) |
|||
{ |
|||
_dateFormat = dateFormat; |
|||
} |
|||
|
|||
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) |
|||
{ |
|||
return DateTime.ParseExact(reader.GetString(), _dateFormat, null); |
|||
} |
|||
|
|||
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) |
|||
{ |
|||
writer.WriteStringValue(value.ToUniversalTime().ToString(_dateFormat)); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
using Volo.Abp.Domain.Entities; |
|||
|
|||
namespace Shentun.WebPeis.Models |
|||
{ |
|||
public class AppointScheduleTemplateTime : AuditedEntity, IHasConcurrencyStamp |
|||
{ |
|||
/// <summary>
|
|||
/// 主键
|
|||
/// </summary>
|
|||
public Guid AppointScheduleTemplateTimeId { get; set; } |
|||
public Guid AppointScheduleTemplateId { get; set; } |
|||
/// <summary>
|
|||
/// 数量限制
|
|||
/// </summary>
|
|||
|
|||
public int NumberLimit { get; set; } |
|||
|
|||
|
|||
/// <summary>
|
|||
/// 开始时间
|
|||
/// </summary>
|
|||
|
|||
public TimeOnly StartTime { get; set; } |
|||
/// <summary>
|
|||
/// 结束时间
|
|||
/// </summary>
|
|||
|
|||
public TimeOnly StopTime { get; set; } |
|||
|
|||
public string? ConcurrencyStamp { get; set; } |
|||
|
|||
public virtual AppointScheduleTemplate AppointScheduleTemplate { get; set; } = null!; |
|||
|
|||
public override object[] GetKeys() |
|||
{ |
|||
return [AppointScheduleTemplateTimeId]; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore.Metadata.Builders; |
|||
using Shentun.WebPeis.Models; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Entities; |
|||
|
|||
namespace Shentun.WebPeis.Configures |
|||
{ |
|||
public class AppointScheduleTemplateTimeConfigure : IEntityTypeConfiguration<AppointScheduleTemplateTime> |
|||
{ |
|||
public void Configure(EntityTypeBuilder<AppointScheduleTemplateTime> entity) |
|||
{ |
|||
entity.HasKey(e => e.AppointScheduleTemplateTimeId).HasName("appoint_schedule_template_time_pkey"); |
|||
|
|||
entity.ToTable("appoint_schedule_template_time"); |
|||
|
|||
entity.HasIndex(e =>new object[] {e.AppointScheduleTemplateId,e.StartTime }, "ix_appoint_schedule_template_time").IsUnique(); |
|||
|
|||
entity.Property(e => e.AppointScheduleTemplateTimeId) |
|||
.ValueGeneratedNever() |
|||
.HasColumnName("appoint_schedule_template_time_id"); |
|||
entity.Property(e => e.StartTime).HasColumnName("start_time"); |
|||
entity.Property(e => e.StopTime).HasColumnName("stop_time"); |
|||
entity.Property(e => e.ConcurrencyStamp) |
|||
.HasMaxLength(40) |
|||
.HasColumnName("concurrency_stamp"); |
|||
entity.Property(e => e.CreationTime) |
|||
.HasColumnType("timestamp without time zone") |
|||
.HasColumnName("creation_time"); |
|||
entity.Property(e => e.CreatorId).HasColumnName("creator_id"); |
|||
entity.Property(e => e.LastModificationTime) |
|||
.HasColumnType("timestamp without time zone") |
|||
.HasColumnName("last_modification_time"); |
|||
entity.Property(e => e.LastModifierId).HasColumnName("last_modifier_id"); |
|||
entity.Property(e => e.NumberLimit).IsRequired().HasColumnName("number_limit"); |
|||
entity.HasOne(d => d.AppointScheduleTemplate).WithMany(p => p.AppointScheduleTemplateTimes) |
|||
.HasForeignKey(d => d.AppointScheduleTemplateId) |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.HasConstraintName("fk_appoint_schedule_template_time"); |
|||
|
|||
|
|||
} |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue