27 changed files with 1383 additions and 0 deletions
@ -0,0 +1,9 @@ |
|||
<Application x:Class="BBWYB.Client.App" |
|||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
xmlns:local="clr-namespace:BBWYB.Client" |
|||
StartupUri="MainWindow.xaml"> |
|||
<Application.Resources> |
|||
|
|||
</Application.Resources> |
|||
</Application> |
@ -0,0 +1,17 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Configuration; |
|||
using System.Data; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using System.Windows; |
|||
|
|||
namespace BBWYB.Client |
|||
{ |
|||
/// <summary>
|
|||
/// Interaction logic for App.xaml
|
|||
/// </summary>
|
|||
public partial class App : Application |
|||
{ |
|||
} |
|||
} |
@ -0,0 +1,10 @@ |
|||
using System.Windows; |
|||
|
|||
[assembly: ThemeInfo( |
|||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
|||
//(used if a resource is not found in the page,
|
|||
// or application resource dictionaries)
|
|||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
|||
//(used if a resource is not found in the page,
|
|||
// app, or any theme specific resource dictionaries)
|
|||
)] |
@ -0,0 +1,10 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<OutputType>WinExe</OutputType> |
|||
<TargetFramework>net6.0-windows</TargetFramework> |
|||
<Nullable>enable</Nullable> |
|||
<UseWPF>true</UseWPF> |
|||
</PropertyGroup> |
|||
|
|||
</Project> |
@ -0,0 +1,12 @@ |
|||
<Window x:Class="BBWYB.Client.MainWindow" |
|||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" |
|||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" |
|||
xmlns:local="clr-namespace:BBWYB.Client" |
|||
mc:Ignorable="d" |
|||
Title="MainWindow" Height="450" Width="800"> |
|||
<Grid> |
|||
|
|||
</Grid> |
|||
</Window> |
@ -0,0 +1,28 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using System.Windows; |
|||
using System.Windows.Controls; |
|||
using System.Windows.Data; |
|||
using System.Windows.Documents; |
|||
using System.Windows.Input; |
|||
using System.Windows.Media; |
|||
using System.Windows.Media.Imaging; |
|||
using System.Windows.Navigation; |
|||
using System.Windows.Shapes; |
|||
|
|||
namespace BBWYB.Client |
|||
{ |
|||
/// <summary>
|
|||
/// Interaction logic for MainWindow.xaml
|
|||
/// </summary>
|
|||
public partial class MainWindow : Window |
|||
{ |
|||
public MainWindow() |
|||
{ |
|||
InitializeComponent(); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,16 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<ImplicitUsings>enable</ImplicitUsings> |
|||
<Nullable>enable</Nullable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="AutoMapper" Version="12.0.1" /> |
|||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.2-mauipre.1.22102.15" /> |
|||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.2-mauipre.1.22102.15" /> |
|||
<PackageReference Include="Swifter.Json" Version="1.6.0.6" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
@ -0,0 +1,102 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
|
|||
namespace BBWY.Common.Extensions |
|||
{ |
|||
public static class DateTimeExtension |
|||
{ |
|||
private static readonly DateTime beginTime = new DateTime(1970, 1, 1, 0, 0, 0, 0); |
|||
|
|||
/// <summary>
|
|||
/// 时间戳转时间
|
|||
/// </summary>
|
|||
/// <param name="timeStamp">时间</param>
|
|||
/// <param name="len13">true:13, false:10</param>
|
|||
/// <returns></returns>
|
|||
[Obsolete] |
|||
public static DateTime StampToDateTime(this long timeStamp) |
|||
{ |
|||
DateTime dt = TimeZone.CurrentTimeZone.ToLocalTime(beginTime); |
|||
return timeStamp.ToString().Length == 13 ? dt.AddMilliseconds(timeStamp) : dt.AddSeconds(timeStamp); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 时间转时间戳
|
|||
/// </summary>
|
|||
/// <param name="time">时间</param>
|
|||
/// <param name="len13">true:13, false:10</param>
|
|||
/// <returns></returns>
|
|||
public static long DateTimeToStamp(this DateTime time, bool len13 = true) |
|||
{ |
|||
TimeSpan ts = time.ToUniversalTime() - beginTime; |
|||
if (len13) |
|||
return Convert.ToInt64(ts.TotalMilliseconds); |
|||
else |
|||
return Convert.ToInt64(ts.TotalSeconds); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将秒数转换为时分秒形式
|
|||
/// </summary>
|
|||
/// <param name="second"></param>
|
|||
/// <returns></returns>
|
|||
public static string FormatToHHmmss(long second) |
|||
{ |
|||
if (second < 60) |
|||
return $"00:00:{(second >= 10 ? $"{second}" : $"0{second}")}"; |
|||
if (second < 3600) |
|||
{ |
|||
var minute = second / 60; |
|||
var s = second % 60; |
|||
return $"00:{(minute >= 10 ? $"{minute}" : $"0{minute}")}:{(s >= 10 ? $"{s}" : $"0{s}")}"; |
|||
} |
|||
else |
|||
{ |
|||
var hour = second / 3600; |
|||
var minute = (second - (hour * 3600)) / 60; |
|||
var s = (second - ((hour * 3600) + minute * 60)) % 60; |
|||
return $"{(hour >= 10 ? $"{hour}" : $"0{hour}")}:{(minute >= 10 ? $"{minute}" : $"0{minute}")}:{(s >= 10 ? $"{s}" : $"0{s}")}"; |
|||
} |
|||
} |
|||
|
|||
#region SetLocalTime
|
|||
[DllImport("Kernel32.dll")] |
|||
private static extern bool SetLocalTime(ref SYSTEMTIME lpSystemTime); |
|||
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
private struct SYSTEMTIME |
|||
{ |
|||
public ushort wYear; |
|||
public ushort wMonth; |
|||
public ushort wDayOfWeek; |
|||
public ushort wDay; |
|||
public ushort wHour; |
|||
public ushort wMinute; |
|||
public ushort wSecond; |
|||
public ushort wMilliseconds; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 修改系统时间(需要管理员权限)
|
|||
/// </summary>
|
|||
/// <param name="date"></param>
|
|||
public static void SetSystemTime(DateTime date) |
|||
{ |
|||
SYSTEMTIME lpTime = new SYSTEMTIME(); |
|||
lpTime.wYear = Convert.ToUInt16(date.Year); |
|||
lpTime.wMonth = Convert.ToUInt16(date.Month); |
|||
lpTime.wDayOfWeek = Convert.ToUInt16(date.DayOfWeek); |
|||
lpTime.wDay = Convert.ToUInt16(date.Day); |
|||
DateTime time = date; |
|||
lpTime.wHour = Convert.ToUInt16(time.Hour); |
|||
lpTime.wMinute = Convert.ToUInt16(time.Minute); |
|||
lpTime.wSecond = Convert.ToUInt16(time.Second); |
|||
lpTime.wMilliseconds = Convert.ToUInt16(time.Millisecond); |
|||
var r = SetLocalTime(ref lpTime); |
|||
Console.WriteLine($"修改系统时间 {r}"); |
|||
} |
|||
#endregion
|
|||
} |
|||
} |
@ -0,0 +1,59 @@ |
|||
using AutoMapper; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
|
|||
namespace BBWY.Common.Extensions |
|||
{ |
|||
public static class MapperExtension |
|||
{ |
|||
private static IMapper _mapper; |
|||
|
|||
/// <summary>
|
|||
/// 注册对象映射器
|
|||
/// </summary>
|
|||
/// <param name="services"></param>
|
|||
/// <param name="profile"></param>
|
|||
/// <returns></returns>
|
|||
public static IServiceCollection AddMapper(this IServiceCollection services, Profile profile) |
|||
{ |
|||
var config = new MapperConfiguration(cfg => |
|||
{ |
|||
cfg.AddProfile(profile); |
|||
}); |
|||
_mapper = config.CreateMapper(); |
|||
return services; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 设置对象映射执行者
|
|||
/// </summary>
|
|||
/// <param name="mapper">映射执行者</param>
|
|||
public static void SetMapper(IMapper mapper) |
|||
{ |
|||
_mapper = mapper; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将对象映射为指定类型
|
|||
/// </summary>
|
|||
/// <typeparam name="TTarget">要映射的目标类型</typeparam>
|
|||
/// <param name="source">源对象</param>
|
|||
/// <returns>目标类型的对象</returns>
|
|||
public static TTarget Map<TTarget>(this object source) |
|||
{ |
|||
return _mapper.Map<TTarget>(source); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 使用源类型的对象更新目标类型的对象
|
|||
/// </summary>
|
|||
/// <typeparam name="TSource">源类型</typeparam>
|
|||
/// <typeparam name="TTarget">目标类型</typeparam>
|
|||
/// <param name="source">源对象</param>
|
|||
/// <param name="target">待更新的目标对象</param>
|
|||
/// <returns>更新后的目标类型对象</returns>
|
|||
public static TTarget Map<TSource, TTarget>(this TSource source, TTarget target) |
|||
{ |
|||
return _mapper.Map(source, target); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,37 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
|
|||
namespace BBWY.Common.Extensions |
|||
{ |
|||
public static class StartupExtension |
|||
{ |
|||
public static void BatchRegisterServices(this IServiceCollection serviceCollection, Assembly[] assemblys, Type baseType, ServiceLifetime serviceLifetime = ServiceLifetime.Singleton, bool registerSelf = true) |
|||
{ |
|||
List<Type> typeList = new List<Type>(); //所有符合注册条件的类集合
|
|||
foreach (var assembly in assemblys) |
|||
{ |
|||
var types = assembly.GetTypes().Where(t => t.IsClass && !t.IsSealed && !t.IsAbstract && baseType.IsAssignableFrom(t)); |
|||
typeList.AddRange(types); |
|||
} |
|||
|
|||
if (typeList.Count() == 0) |
|||
return; |
|||
|
|||
foreach (var instanceType in typeList) |
|||
{ |
|||
if (registerSelf) |
|||
{ |
|||
serviceCollection.Add(new ServiceDescriptor(instanceType, instanceType, serviceLifetime)); |
|||
continue; |
|||
} |
|||
var interfaces = instanceType.GetInterfaces(); |
|||
if (interfaces != null && interfaces.Length > 0) |
|||
foreach (var interfaceType in interfaces) |
|||
serviceCollection.Add(new ServiceDescriptor(interfaceType, instanceType, serviceLifetime)); |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,456 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Net; |
|||
using System.Net.Http; |
|||
using System.Threading; |
|||
|
|||
namespace BBWY.Common.Http |
|||
{ |
|||
/// <summary>
|
|||
/// Http下载器
|
|||
/// </summary>
|
|||
public class HttpDownloader |
|||
{ |
|||
|
|||
private IHttpClientFactory httpClientFactory; |
|||
|
|||
public HttpDownloader(IHttpClientFactory httpClientFactory, DownloadSetting dwSetting = null) |
|||
{ |
|||
this.httpClientFactory = httpClientFactory; |
|||
complateArgs = new DownloadCompletedEventArgs(); |
|||
changeArgs = new DownloadProgressChangedEventArgs(); |
|||
if (dwSetting == null) |
|||
dwSetting = new DownloadSetting(); |
|||
downloadSetting = dwSetting; |
|||
buffer = new byte[downloadSetting.BufferSize]; |
|||
} |
|||
|
|||
~HttpDownloader() |
|||
{ |
|||
this.buffer = null; |
|||
this.downloadSetting = null; |
|||
this.complateArgs.Error = null; |
|||
this.complateArgs = null; |
|||
this.changeArgs = null; |
|||
} |
|||
|
|||
#region Property
|
|||
|
|||
/// <summary>
|
|||
/// 下载进度变化参数
|
|||
/// </summary>
|
|||
private DownloadProgressChangedEventArgs changeArgs; |
|||
|
|||
/// <summary>
|
|||
/// 下载完成参数
|
|||
/// </summary>
|
|||
private DownloadCompletedEventArgs complateArgs; |
|||
|
|||
/// <summary>
|
|||
/// 下载参数配置类
|
|||
/// </summary>
|
|||
private DownloadSetting downloadSetting; |
|||
|
|||
/// <summary>
|
|||
/// 下载缓冲区
|
|||
/// </summary>
|
|||
private byte[] buffer; |
|||
|
|||
#endregion
|
|||
|
|||
#region Delegate
|
|||
public delegate void DownloadProgressChangedEventHandler(object sender, DownloadProgressChangedEventArgs e); |
|||
|
|||
public delegate void DownloadCompletedEventHandler(object sender, DownloadCompletedEventArgs e); |
|||
|
|||
public delegate void ReDownloadEventHandler(object sender, DownloadCompletedEventArgs e); |
|||
#endregion
|
|||
|
|||
#region Event
|
|||
/// <summary>
|
|||
/// 下载进度变化事件
|
|||
/// </summary>
|
|||
public event DownloadProgressChangedEventHandler OnDownloadProgressChanged; |
|||
|
|||
/// <summary>
|
|||
/// 下载完成事件
|
|||
/// </summary>
|
|||
public event DownloadCompletedEventHandler OnDownloadComplated; |
|||
|
|||
/// <summary>
|
|||
/// 自动重下事件
|
|||
/// </summary>
|
|||
public event ReDownloadEventHandler OnReDownload; |
|||
#endregion
|
|||
|
|||
#region Method
|
|||
/// <summary>
|
|||
/// 设置下载参数
|
|||
/// </summary>
|
|||
/// <param name="dwSetting"></param>
|
|||
public void SetDownloadSetting(DownloadSetting dwSetting) |
|||
{ |
|||
if (dwSetting != null) |
|||
downloadSetting = dwSetting; |
|||
} |
|||
|
|||
private void DoProcessChangedEvent(DownloadProgressChangedEventArgs args) |
|||
{ |
|||
OnDownloadProgressChanged?.Invoke(this, args); |
|||
} |
|||
|
|||
private void DoCompletedEvent(DownloadCompletedEventArgs args) |
|||
{ |
|||
OnDownloadComplated?.Invoke(this, args); |
|||
} |
|||
|
|||
private void DoReDownloadEvent(DownloadCompletedEventArgs args) |
|||
{ |
|||
OnReDownload?.Invoke(this, args); |
|||
} |
|||
|
|||
private void ArgsInit() |
|||
{ |
|||
changeArgs.Cancel = false; |
|||
complateArgs.Cancelled = false; |
|||
complateArgs.Error = null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取文件总大小
|
|||
/// </summary>
|
|||
/// <param name="url"></param>
|
|||
/// <returns></returns>
|
|||
public long GetTotalLength(string url) |
|||
{ |
|||
long length = 0; |
|||
try |
|||
{ |
|||
using (var httpClient = httpClientFactory.CreateClient()) |
|||
{ |
|||
var requestMessage = new HttpRequestMessage(HttpMethod.Head, url); |
|||
var httpResponse = httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead).Result; |
|||
if (httpResponse.IsSuccessStatusCode) |
|||
length = httpResponse.Content.Headers.ContentLength ?? 0; |
|||
} |
|||
|
|||
//var req = (HttpWebRequest)WebRequest.Create(new Uri(url));
|
|||
//req.Method = "HEAD";
|
|||
//req.Timeout = 5000;
|
|||
//var res = (HttpWebResponse)req.GetResponse();
|
|||
//if (res.StatusCode == HttpStatusCode.OK)
|
|||
//{
|
|||
// length = res.ContentLength;
|
|||
//}
|
|||
//res.Close();
|
|||
return length; |
|||
} |
|||
catch (WebException wex) |
|||
{ |
|||
throw wex; |
|||
} |
|||
} |
|||
|
|||
|
|||
private bool DownloadFile(string url, string savePath, long startPosition, long totalSize) |
|||
{ |
|||
long currentReadSize = 0; //当前已经读取的字节数
|
|||
if (startPosition != 0) |
|||
currentReadSize = startPosition; |
|||
using (var httpClient = httpClientFactory.CreateClient()) |
|||
{ |
|||
try |
|||
{ |
|||
httpClient.Timeout = new TimeSpan(0, 0, 20); |
|||
if (currentReadSize != 0 && currentReadSize == totalSize) |
|||
{ |
|||
changeArgs.CurrentSize = changeArgs.TotalSize = totalSize; |
|||
return true; |
|||
} |
|||
if (currentReadSize != 0) |
|||
{ |
|||
try |
|||
{ |
|||
httpClient.DefaultRequestHeaders.Range = new System.Net.Http.Headers.RangeHeaderValue(currentReadSize, totalSize); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
//http 416
|
|||
currentReadSize = 0; |
|||
} |
|||
} |
|||
|
|||
using (var response = httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead).Result) |
|||
{ |
|||
using (var responseStream = response.Content.ReadAsStreamAsync().Result) |
|||
{ |
|||
changeArgs.TotalSize = totalSize; |
|||
using (var fs = new FileStream(savePath, currentReadSize == 0 ? FileMode.Create : FileMode.Append, FileAccess.Write)) |
|||
{ |
|||
//判断服务器是否支持断点续下
|
|||
if (currentReadSize != 0 && |
|||
response.StatusCode == HttpStatusCode.PartialContent && |
|||
response.Content.Headers.ContentLength != totalSize) |
|||
fs.Seek(startPosition, SeekOrigin.Begin); //支持,从本地文件流末尾进行写入
|
|||
else |
|||
currentReadSize = 0; //不支持,从本地文件流开始进行写入
|
|||
|
|||
if (buffer.Length != downloadSetting.BufferSize) |
|||
buffer = new byte[downloadSetting.BufferSize]; |
|||
int size = responseStream.Read(buffer, 0, buffer.Length); |
|||
while (size != 0) |
|||
{ |
|||
fs.Write(buffer, 0, size); |
|||
if (buffer.Length != downloadSetting.BufferSize) |
|||
buffer = new byte[downloadSetting.BufferSize]; |
|||
size = responseStream.Read(buffer, 0, buffer.Length); |
|||
currentReadSize += size; //累计下载大小
|
|||
//执行下载进度变化事件
|
|||
changeArgs.CurrentSize = currentReadSize; |
|||
DoProcessChangedEvent(changeArgs); |
|||
//判断挂起状态
|
|||
if (changeArgs.Cancel) |
|||
return true; |
|||
} |
|||
|
|||
//执行下载进度变化事件
|
|||
changeArgs.CurrentSize = changeArgs.TotalSize; |
|||
DoProcessChangedEvent(changeArgs); |
|||
fs.Flush(true); |
|||
} |
|||
|
|||
//验证远程服务器文件字节数和本地文件字节数是否一致
|
|||
long localFileSize = 0; |
|||
try |
|||
{ |
|||
localFileSize = new FileInfo(savePath).Length; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
localFileSize = changeArgs.CurrentSize; |
|||
} |
|||
|
|||
if (totalSize != localFileSize) |
|||
{ |
|||
complateArgs.Error = new Exception(string.Format("远程文件字节:[{0}]与本地文件字节:[{1}]不一致", totalSize, localFileSize)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
catch (IOException ex) |
|||
{ |
|||
complateArgs.Error = ex; |
|||
} |
|||
catch (WebException ex) |
|||
{ |
|||
complateArgs.Error = ex; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
complateArgs.Error = ex; |
|||
} |
|||
} |
|||
return complateArgs.Error == null; |
|||
} |
|||
|
|||
|
|||
public bool DownloadFile(string url, string saveFolderPath, string saveFileName, long? totalSize) |
|||
{ |
|||
ArgsInit(); |
|||
var result = false; |
|||
//验证文件夹
|
|||
try |
|||
{ |
|||
if (!Directory.Exists(saveFolderPath)) |
|||
Directory.CreateDirectory(saveFolderPath); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
complateArgs.Error = ex; |
|||
DoCompletedEvent(complateArgs); |
|||
return result; |
|||
} |
|||
|
|||
if (totalSize == null || totalSize.Value == 0) |
|||
{ |
|||
try |
|||
{ |
|||
totalSize = GetTotalLength(url); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Console.WriteLine("获取远程服务器字节数失败 {0}", ex.Message); |
|||
complateArgs.Error = ex; |
|||
DoCompletedEvent(complateArgs); |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
string saveFilePath = Path.Combine(saveFolderPath, saveFileName); |
|||
long startPosition = 0; |
|||
for (var i = 1; i <= downloadSetting.TryCount; i++) |
|||
{ |
|||
if (File.Exists(saveFilePath)) |
|||
{ |
|||
try |
|||
{ |
|||
startPosition = new FileInfo(saveFilePath).Length; |
|||
} |
|||
catch |
|||
{ |
|||
startPosition = 0; |
|||
} |
|||
} |
|||
|
|||
result = DownloadFile(url, saveFilePath, startPosition, totalSize.Value); |
|||
if (result) |
|||
{ |
|||
break; |
|||
} |
|||
else |
|||
{ |
|||
if (i != downloadSetting.TryCount) |
|||
{ |
|||
complateArgs.TryCount = i + 1; |
|||
DoReDownloadEvent(complateArgs); |
|||
Thread.Sleep(downloadSetting.SleepTime); |
|||
if (complateArgs.Cancelled) |
|||
break; |
|||
ArgsInit(); |
|||
} |
|||
} |
|||
if (complateArgs.Cancelled) |
|||
break; |
|||
} |
|||
DoCompletedEvent(complateArgs); |
|||
return result; |
|||
} |
|||
|
|||
public byte[] DwonloadBytes(string url) |
|||
{ |
|||
ArgsInit(); |
|||
using (var httpClient = httpClientFactory.CreateClient()) |
|||
{ |
|||
return httpClient.GetByteArrayAsync(url).Result; |
|||
} |
|||
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 取消/暂停下载
|
|||
/// </summary>
|
|||
public void CancelDownload() |
|||
{ |
|||
this.changeArgs.Cancel = true; |
|||
this.complateArgs.Cancelled = true; |
|||
} |
|||
#endregion
|
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// 下载进度变化参数
|
|||
/// </summary>
|
|||
public class DownloadProgressChangedEventArgs |
|||
{ |
|||
public DownloadProgressChangedEventArgs() |
|||
{ |
|||
ProgressPercentage = 0.0; |
|||
Cancel = false; |
|||
} |
|||
/// <summary>
|
|||
/// 下载进度百分比
|
|||
/// </summary>
|
|||
public double ProgressPercentage; |
|||
|
|||
|
|||
private long currentSize; |
|||
|
|||
/// <summary>
|
|||
/// 当前下载总大小
|
|||
/// </summary>
|
|||
public long CurrentSize |
|||
{ |
|||
get { return currentSize; } |
|||
set |
|||
{ |
|||
currentSize = value; |
|||
this.ProgressPercentage = Math.Round((CurrentSize * 1.0 / TotalSize) * 100, 2); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 文件总大小
|
|||
/// </summary>
|
|||
public long TotalSize; |
|||
|
|||
/// <summary>
|
|||
/// 取消状态
|
|||
/// </summary>
|
|||
public bool Cancel; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 下载完成参数
|
|||
/// </summary>
|
|||
public class DownloadCompletedEventArgs |
|||
{ |
|||
public DownloadCompletedEventArgs() |
|||
{ |
|||
Cancelled = false; |
|||
Error = null; |
|||
} |
|||
/// <summary>
|
|||
/// 下载异常
|
|||
/// </summary>
|
|||
public Exception Error; |
|||
|
|||
/// <summary>
|
|||
/// 重试次数
|
|||
/// </summary>
|
|||
public int TryCount; |
|||
|
|||
/// <summary>
|
|||
/// 下载操作是否被取消 【取消则为true;默认false】
|
|||
/// </summary>
|
|||
public bool Cancelled; |
|||
} |
|||
|
|||
public class DownloadSetting |
|||
{ |
|||
public DownloadSetting() |
|||
{ |
|||
this.BufferSize = 1024 * 1024 * 1; |
|||
this.TryCount = 5; |
|||
this.SleepTime = 5000; |
|||
} |
|||
|
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
/// <param name="bufferSize"></param>
|
|||
/// <param name="tryCount"></param>
|
|||
/// <param name="sleepTime">毫秒</param>
|
|||
public DownloadSetting(int bufferSize, int tryCount, int sleepTime) |
|||
{ |
|||
this.BufferSize = bufferSize; |
|||
this.TryCount = tryCount; |
|||
this.SleepTime = sleepTime; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 下载缓冲区大小
|
|||
/// </summary>
|
|||
public int BufferSize; |
|||
|
|||
/// <summary>
|
|||
/// 重试次数
|
|||
/// </summary>
|
|||
public int TryCount; |
|||
|
|||
/// <summary>
|
|||
/// 自动重下休眠时间, 毫秒
|
|||
/// </summary>
|
|||
public int SleepTime; |
|||
} |
|||
} |
@ -0,0 +1,117 @@ |
|||
using BBWY.Common.Extensions; |
|||
using Swifter.Json; |
|||
using System.Net; |
|||
using System.Net.Http.Headers; |
|||
using System.Text; |
|||
|
|||
namespace BBWY.Common.Http |
|||
{ |
|||
public class RestApiService |
|||
{ |
|||
public const string ContentType_Json = "application/json"; |
|||
public const string ContentType_Form = "application/x-www-form-urlencoded"; |
|||
public TimeSpan TimeOut { get; set; } = new TimeSpan(0, 0, 40); |
|||
|
|||
private IHttpClientFactory httpClientFactory; |
|||
|
|||
public RestApiService(IHttpClientFactory httpClientFactory) |
|||
{ |
|||
this.httpClientFactory = httpClientFactory; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 发送请求
|
|||
/// </summary>
|
|||
/// <param name="apiHost"></param>
|
|||
/// <param name="apiPath"></param>
|
|||
/// <param name="param"></param>
|
|||
/// <param name="requestHeaders"></param>
|
|||
/// <param name="httpMethod"></param>
|
|||
/// <param name="contentType"></param>
|
|||
/// <param name="paramPosition"></param>
|
|||
/// <param name="enableRandomTimeStamp"></param>
|
|||
/// <param name="getResponseHeader"></param>
|
|||
/// <param name="httpCompletionOption"></param>
|
|||
/// <param name="httpClientName"></param>
|
|||
/// <returns></returns>
|
|||
/// <exception cref="Exception"></exception>
|
|||
public RestApiResult SendRequest(string apiHost, |
|||
string apiPath, |
|||
object param, |
|||
IDictionary<string, string> requestHeaders, |
|||
HttpMethod httpMethod, |
|||
string contentType = ContentType_Json, |
|||
ParamPosition paramPosition = ParamPosition.Body, |
|||
bool enableRandomTimeStamp = false, |
|||
bool getResponseHeader = false, |
|||
HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead, |
|||
string httpClientName = "", |
|||
int timeOutSeconds = 0) |
|||
{ |
|||
//Get和Delete强制使用QueryString形式传参
|
|||
if (httpMethod == HttpMethod.Get) |
|||
paramPosition = ParamPosition.Query; |
|||
|
|||
//拼接Url
|
|||
|
|||
var url = $"{apiHost}{(apiHost.EndsWith("/") ? string.Empty : (string.IsNullOrEmpty(apiPath) ? string.Empty : "/"))}{(apiPath.StartsWith("/") ? apiPath.Substring(1) : apiPath)}"; |
|||
|
|||
var isCombineParam = false; |
|||
if (paramPosition == ParamPosition.Query && param != null) |
|||
{ |
|||
url = $"{url}{(param.ToString().StartsWith("?") ? string.Empty : "?")}{param}"; |
|||
isCombineParam = true; |
|||
} |
|||
|
|||
//使用时间戳绕过CDN
|
|||
if (enableRandomTimeStamp) |
|||
url = $"{url}{(isCombineParam ? "&" : "?")}t={DateTime.Now.DateTimeToStamp()}"; |
|||
|
|||
using (var httpClient = string.IsNullOrEmpty(httpClientName) ? httpClientFactory.CreateClient() : httpClientFactory.CreateClient(httpClientName)) |
|||
{ |
|||
if (timeOutSeconds == 0) |
|||
httpClient.Timeout = TimeOut; |
|||
else |
|||
httpClient.Timeout = TimeSpan.FromSeconds(timeOutSeconds); |
|||
using (var request = new HttpRequestMessage(httpMethod, url)) |
|||
{ |
|||
if (requestHeaders != null && requestHeaders.Count > 0) |
|||
foreach (var key in requestHeaders.Keys) |
|||
request.Headers.Add(key, requestHeaders[key]); |
|||
|
|||
if (paramPosition == ParamPosition.Body && param != null) |
|||
request.Content = new StringContent(contentType == ContentType_Json ? JsonFormatter.SerializeObject(param) : param.ToString(), Encoding.UTF8, contentType); |
|||
|
|||
using (var response = httpClient.SendAsync(request, httpCompletionOption).Result) |
|||
{ |
|||
return new RestApiResult() |
|||
{ |
|||
StatusCode = response.StatusCode, |
|||
Content = httpCompletionOption == HttpCompletionOption.ResponseContentRead ? response.Content.ReadAsStringAsync().Result : |
|||
string.Empty, |
|||
Headers = getResponseHeader ? response.Headers : null |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public class RestApiResult |
|||
{ |
|||
public HttpStatusCode StatusCode { get; set; } |
|||
|
|||
public string Content { get; set; } |
|||
|
|||
public HttpResponseHeaders Headers { get; set; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 参数传递位置
|
|||
/// </summary>
|
|||
public enum ParamPosition |
|||
{ |
|||
Query, |
|||
Body |
|||
} |
|||
} |
@ -0,0 +1,34 @@ |
|||
using System; |
|||
|
|||
namespace BBWY.Common.Models |
|||
{ |
|||
public class ApiResponse<T> |
|||
{ |
|||
public bool Success { get { return Code == 200; } } |
|||
|
|||
/// <summary>
|
|||
/// 接口调用状态
|
|||
/// </summary>
|
|||
public int Code { get; set; } = 200; |
|||
|
|||
/// <summary>
|
|||
/// 返回消息
|
|||
/// </summary>
|
|||
public string Msg { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 数据内容
|
|||
/// </summary>
|
|||
public T Data { get; set; } |
|||
|
|||
public static ApiResponse<T> Error(int code, string msg) |
|||
{ |
|||
return new ApiResponse<T>() { Code = code, Msg = msg }; |
|||
} |
|||
} |
|||
|
|||
public class ApiResponse : ApiResponse<object> |
|||
{ |
|||
|
|||
} |
|||
} |
@ -0,0 +1,22 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BBWY.Common.Models |
|||
{ |
|||
/// <summary>
|
|||
/// 业务异常
|
|||
/// </summary>
|
|||
public class BusinessException : Exception |
|||
{ |
|||
public BusinessException(string message) : base(message) |
|||
{ |
|||
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 错误代码
|
|||
/// </summary>
|
|||
public int Code { get; set; } |
|||
} |
|||
} |
@ -0,0 +1,10 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BBWY.Common.Models |
|||
{ |
|||
public interface IDenpendency |
|||
{ |
|||
} |
|||
} |
@ -0,0 +1,70 @@ |
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace BBWY.Common.Trigger |
|||
{ |
|||
/// <summary>
|
|||
/// 延迟触发组件
|
|||
/// </summary>
|
|||
public class DelayTrigger |
|||
{ |
|||
public DelayTrigger(int delayTime = 1000) |
|||
{ |
|||
if (delayTime < 1000) |
|||
delayTime = 1000; |
|||
this.delayTime = delayTime; |
|||
} |
|||
/// <summary>
|
|||
/// 延迟执行时间(ms)
|
|||
/// </summary>
|
|||
private int delayTime; |
|||
|
|||
/// <summary>
|
|||
/// 关键字
|
|||
/// </summary>
|
|||
private string currentKey; |
|||
|
|||
/// <summary>
|
|||
/// 是否可以执行
|
|||
/// </summary>
|
|||
private volatile bool canExecute; |
|||
|
|||
/// <summary>
|
|||
/// 是否正在延迟响应中
|
|||
/// </summary>
|
|||
private volatile bool isDelaying; |
|||
|
|||
|
|||
public Action<string> OnExecute; |
|||
|
|||
|
|||
public void SetKey(string key) |
|||
{ |
|||
currentKey = key; |
|||
if (isDelaying) |
|||
{ |
|||
canExecute = false; |
|||
return; |
|||
} |
|||
Task.Factory.StartNew(delegate |
|||
{ |
|||
isDelaying = true; |
|||
while (true) |
|||
{ |
|||
canExecute = true; |
|||
Thread.Sleep(delayTime); |
|||
if (canExecute) |
|||
{ |
|||
Console.ForegroundColor = ConsoleColor.Red; |
|||
Console.WriteLine($"DelayTrigger {currentKey} Execute at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffff")}"); |
|||
Console.ResetColor(); |
|||
OnExecute?.Invoke(currentKey); |
|||
isDelaying = false; |
|||
break; |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,153 @@ |
|||
//--------------------------------------------------------------------------
|
|||
//
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|||
//
|
|||
// File: LimitedConcurrencyTaskScheduler.cs
|
|||
//
|
|||
//--------------------------------------------------------------------------
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
namespace System.Threading.Tasks.Schedulers |
|||
{ |
|||
/// <summary>
|
|||
/// Provides a task scheduler that ensures a maximum concurrency level while
|
|||
/// running on top of the ThreadPool.
|
|||
/// </summary>
|
|||
public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler |
|||
{ |
|||
/// <summary>Whether the current thread is processing work items.</summary>
|
|||
[ThreadStatic] |
|||
private static bool _currentThreadIsProcessingItems; |
|||
/// <summary>The list of tasks to be executed.</summary>
|
|||
private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); // protected by lock(_tasks)
|
|||
/// <summary>The maximum concurrency level allowed by this scheduler.</summary>
|
|||
private readonly int _maxDegreeOfParallelism; |
|||
/// <summary>Whether the scheduler is currently processing work items.</summary>
|
|||
private int _delegatesQueuedOrRunning = 0; // protected by lock(_tasks)
|
|||
|
|||
/// <summary>
|
|||
/// 最大并发数
|
|||
/// </summary>
|
|||
private readonly int maxConcurrencyCountOfSystem = Environment.ProcessorCount * 2; |
|||
|
|||
/// <summary>
|
|||
/// Initializes an instance of the LimitedConcurrencyLevelTaskScheduler class with the
|
|||
/// specified degree of parallelism.
|
|||
/// </summary>
|
|||
/// <param name="maxDegreeOfParallelism">The maximum degree of parallelism provided by this scheduler.</param>
|
|||
public LimitedConcurrencyLevelTaskScheduler(int maxDegreeOfParallelism) |
|||
{ |
|||
if (maxDegreeOfParallelism < 1) throw new ArgumentOutOfRangeException("maxDegreeOfParallelism"); |
|||
if (maxDegreeOfParallelism > maxConcurrencyCountOfSystem) |
|||
maxDegreeOfParallelism = maxConcurrencyCountOfSystem; |
|||
_maxDegreeOfParallelism = maxDegreeOfParallelism; |
|||
} |
|||
|
|||
/// <summary>Queues a task to the scheduler.</summary>
|
|||
/// <param name="task">The task to be queued.</param>
|
|||
protected sealed override void QueueTask(Task task) |
|||
{ |
|||
// Add the task to the list of tasks to be processed. If there aren't enough
|
|||
// delegates currently queued or running to process tasks, schedule another.
|
|||
lock (_tasks) |
|||
{ |
|||
_tasks.AddLast(task); |
|||
if (_delegatesQueuedOrRunning < _maxDegreeOfParallelism) |
|||
{ |
|||
++_delegatesQueuedOrRunning; |
|||
NotifyThreadPoolOfPendingWork(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Informs the ThreadPool that there's work to be executed for this scheduler.
|
|||
/// </summary>
|
|||
private void NotifyThreadPoolOfPendingWork() |
|||
{ |
|||
ThreadPool.UnsafeQueueUserWorkItem(_ => |
|||
{ |
|||
// Note that the current thread is now processing work items.
|
|||
// This is necessary to enable inlining of tasks into this thread.
|
|||
//new Thread(() =>
|
|||
//{
|
|||
_currentThreadIsProcessingItems = true; |
|||
try |
|||
{ |
|||
// Process all available items in the queue.
|
|||
while (true) |
|||
{ |
|||
Task item; |
|||
lock (_tasks) |
|||
{ |
|||
// When there are no more items to be processed,
|
|||
// note that we're done processing, and get out.
|
|||
if (_tasks.Count == 0) |
|||
{ |
|||
--_delegatesQueuedOrRunning; |
|||
break; |
|||
} |
|||
|
|||
// Get the next item from the queue
|
|||
item = _tasks.First.Value; |
|||
_tasks.RemoveFirst(); |
|||
} |
|||
|
|||
// Execute the task we pulled out of the queue
|
|||
base.TryExecuteTask(item); |
|||
} |
|||
} |
|||
// We're done processing items on the current thread
|
|||
finally { _currentThreadIsProcessingItems = false; } |
|||
//})
|
|||
//{ IsBackground = true }.Start();
|
|||
}, null); |
|||
} |
|||
|
|||
/// <summary>Attempts to execute the specified task on the current thread.</summary>
|
|||
/// <param name="task">The task to be executed.</param>
|
|||
/// <param name="taskWasPreviouslyQueued"></param>
|
|||
/// <returns>Whether the task could be executed on the current thread.</returns>
|
|||
protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) |
|||
{ |
|||
// If this thread isn't already processing a task, we don't support inlining
|
|||
if (!_currentThreadIsProcessingItems) return false; |
|||
|
|||
// If the task was previously queued, remove it from the queue
|
|||
if (taskWasPreviouslyQueued) TryDequeue(task); |
|||
|
|||
// Try to run the task.
|
|||
return base.TryExecuteTask(task); |
|||
} |
|||
|
|||
/// <summary>Attempts to remove a previously scheduled task from the scheduler.</summary>
|
|||
/// <param name="task">The task to be removed.</param>
|
|||
/// <returns>Whether the task could be found and removed.</returns>
|
|||
protected sealed override bool TryDequeue(Task task) |
|||
{ |
|||
lock (_tasks) return _tasks.Remove(task); |
|||
} |
|||
|
|||
/// <summary>Gets the maximum concurrency level supported by this scheduler.</summary>
|
|||
public sealed override int MaximumConcurrencyLevel { get { return _maxDegreeOfParallelism; } } |
|||
|
|||
/// <summary>Gets an enumerable of the tasks currently scheduled on this scheduler.</summary>
|
|||
/// <returns>An enumerable of the tasks currently scheduled.</returns>
|
|||
protected sealed override IEnumerable<Task> GetScheduledTasks() |
|||
{ |
|||
bool lockTaken = false; |
|||
try |
|||
{ |
|||
Monitor.TryEnter(_tasks, ref lockTaken); |
|||
if (lockTaken) return _tasks.ToArray(); |
|||
else throw new NotSupportedException(); |
|||
} |
|||
finally |
|||
{ |
|||
if (lockTaken) Monitor.Exit(_tasks); |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,17 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk.Web"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<Nullable>enable</Nullable> |
|||
<ImplicitUsings>enable</ImplicitUsings> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\BBWYB.Common\BBWYB.Common.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
@ -0,0 +1,33 @@ |
|||
using Microsoft.AspNetCore.Mvc; |
|||
|
|||
namespace BBWYB.Server.API.Controllers |
|||
{ |
|||
[ApiController] |
|||
[Route("[controller]")]
|
|||
public class WeatherForecastController : ControllerBase |
|||
{ |
|||
private static readonly string[] Summaries = new[] |
|||
{ |
|||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" |
|||
}; |
|||
|
|||
private readonly ILogger<WeatherForecastController> _logger; |
|||
|
|||
public WeatherForecastController(ILogger<WeatherForecastController> logger) |
|||
{ |
|||
_logger = logger; |
|||
} |
|||
|
|||
[HttpGet(Name = "GetWeatherForecast")] |
|||
public IEnumerable<WeatherForecast> Get() |
|||
{ |
|||
return Enumerable.Range(1, 5).Select(index => new WeatherForecast |
|||
{ |
|||
Date = DateTime.Now.AddDays(index), |
|||
TemperatureC = Random.Shared.Next(-20, 55), |
|||
Summary = Summaries[Random.Shared.Next(Summaries.Length)] |
|||
}) |
|||
.ToArray(); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,25 @@ |
|||
var builder = WebApplication.CreateBuilder(args); |
|||
|
|||
// Add services to the container.
|
|||
|
|||
builder.Services.AddControllers(); |
|||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
|||
builder.Services.AddEndpointsApiExplorer(); |
|||
builder.Services.AddSwaggerGen(); |
|||
|
|||
var app = builder.Build(); |
|||
|
|||
// Configure the HTTP request pipeline.
|
|||
if (app.Environment.IsDevelopment()) |
|||
{ |
|||
app.UseSwagger(); |
|||
app.UseSwaggerUI(); |
|||
} |
|||
|
|||
app.UseHttpsRedirection(); |
|||
|
|||
app.UseAuthorization(); |
|||
|
|||
app.MapControllers(); |
|||
|
|||
app.Run(); |
@ -0,0 +1,31 @@ |
|||
{ |
|||
"$schema": "https://json.schemastore.org/launchsettings.json", |
|||
"iisSettings": { |
|||
"windowsAuthentication": false, |
|||
"anonymousAuthentication": true, |
|||
"iisExpress": { |
|||
"applicationUrl": "http://localhost:56103", |
|||
"sslPort": 44384 |
|||
} |
|||
}, |
|||
"profiles": { |
|||
"BBWYB.Server.API": { |
|||
"commandName": "Project", |
|||
"dotnetRunMessages": true, |
|||
"launchBrowser": true, |
|||
"launchUrl": "swagger", |
|||
"applicationUrl": "https://localhost:7256;http://localhost:5256", |
|||
"environmentVariables": { |
|||
"ASPNETCORE_ENVIRONMENT": "Development" |
|||
} |
|||
}, |
|||
"IIS Express": { |
|||
"commandName": "IISExpress", |
|||
"launchBrowser": true, |
|||
"launchUrl": "swagger", |
|||
"environmentVariables": { |
|||
"ASPNETCORE_ENVIRONMENT": "Development" |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,13 @@ |
|||
namespace BBWYB.Server.API |
|||
{ |
|||
public class WeatherForecast |
|||
{ |
|||
public DateTime Date { get; set; } |
|||
|
|||
public int TemperatureC { get; set; } |
|||
|
|||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); |
|||
|
|||
public string? Summary { get; set; } |
|||
} |
|||
} |
@ -0,0 +1,8 @@ |
|||
{ |
|||
"Logging": { |
|||
"LogLevel": { |
|||
"Default": "Information", |
|||
"Microsoft.AspNetCore": "Warning" |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,9 @@ |
|||
{ |
|||
"Logging": { |
|||
"LogLevel": { |
|||
"Default": "Information", |
|||
"Microsoft.AspNetCore": "Warning" |
|||
} |
|||
}, |
|||
"AllowedHosts": "*" |
|||
} |
@ -0,0 +1,14 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<ImplicitUsings>enable</ImplicitUsings> |
|||
<Nullable>enable</Nullable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\BBWYB.Common\BBWYB.Common.csproj" /> |
|||
<ProjectReference Include="..\BBWYB.Server.Model\BBWYB.Server.Model.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
@ -0,0 +1,9 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<ImplicitUsings>enable</ImplicitUsings> |
|||
<Nullable>enable</Nullable> |
|||
</PropertyGroup> |
|||
|
|||
</Project> |
@ -0,0 +1,62 @@ |
|||
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00 |
|||
# Visual Studio Version 17 |
|||
VisualStudioVersion = 17.1.32210.238 |
|||
MinimumVisualStudioVersion = 10.0.40219.1 |
|||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BBWYB.Server.API", "BBWYB.Server.API\BBWYB.Server.API.csproj", "{B6039632-07B7-4C74-9F74-F16B6782EF56}" |
|||
EndProject |
|||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Server", "Server", "{99D234A0-6830-4C0C-91E8-C626DA939D59}" |
|||
EndProject |
|||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Client", "Client", "{1E7D36DB-A817-4208-8FC6-36A66FAB17E5}" |
|||
EndProject |
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BBWYB.Client", "BBWYB.Client\BBWYB.Client.csproj", "{D52D0167-EF94-4FC8-91BF-FCE5B3ED9C6A}" |
|||
EndProject |
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BBWYB.Server.Business", "BBWYB.Server.Business\BBWYB.Server.Business.csproj", "{52DF7178-3C36-4CA6-A2E9-D9E2BB41C0B8}" |
|||
EndProject |
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BBWYB.Server.Model", "BBWYB.Server.Model\BBWYB.Server.Model.csproj", "{5707BF58-3A98-4283-A6D0-3B78EF7ED2F1}" |
|||
EndProject |
|||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{1191C1AE-7275-4643-AF24-BEC852717299}" |
|||
EndProject |
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BBWYB.Common", "BBWYB.Common\BBWYB.Common.csproj", "{DD328472-01CE-4CA8-AF29-C098FC499483}" |
|||
EndProject |
|||
Global |
|||
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
|||
Debug|Any CPU = Debug|Any CPU |
|||
Release|Any CPU = Release|Any CPU |
|||
EndGlobalSection |
|||
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
|||
{B6039632-07B7-4C74-9F74-F16B6782EF56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{B6039632-07B7-4C74-9F74-F16B6782EF56}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{B6039632-07B7-4C74-9F74-F16B6782EF56}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{B6039632-07B7-4C74-9F74-F16B6782EF56}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
{D52D0167-EF94-4FC8-91BF-FCE5B3ED9C6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{D52D0167-EF94-4FC8-91BF-FCE5B3ED9C6A}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{D52D0167-EF94-4FC8-91BF-FCE5B3ED9C6A}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{D52D0167-EF94-4FC8-91BF-FCE5B3ED9C6A}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
{52DF7178-3C36-4CA6-A2E9-D9E2BB41C0B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{52DF7178-3C36-4CA6-A2E9-D9E2BB41C0B8}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{52DF7178-3C36-4CA6-A2E9-D9E2BB41C0B8}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{52DF7178-3C36-4CA6-A2E9-D9E2BB41C0B8}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
{5707BF58-3A98-4283-A6D0-3B78EF7ED2F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{5707BF58-3A98-4283-A6D0-3B78EF7ED2F1}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{5707BF58-3A98-4283-A6D0-3B78EF7ED2F1}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{5707BF58-3A98-4283-A6D0-3B78EF7ED2F1}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
{DD328472-01CE-4CA8-AF29-C098FC499483}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{DD328472-01CE-4CA8-AF29-C098FC499483}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{DD328472-01CE-4CA8-AF29-C098FC499483}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{DD328472-01CE-4CA8-AF29-C098FC499483}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
EndGlobalSection |
|||
GlobalSection(SolutionProperties) = preSolution |
|||
HideSolutionNode = FALSE |
|||
EndGlobalSection |
|||
GlobalSection(NestedProjects) = preSolution |
|||
{B6039632-07B7-4C74-9F74-F16B6782EF56} = {99D234A0-6830-4C0C-91E8-C626DA939D59} |
|||
{D52D0167-EF94-4FC8-91BF-FCE5B3ED9C6A} = {1E7D36DB-A817-4208-8FC6-36A66FAB17E5} |
|||
{52DF7178-3C36-4CA6-A2E9-D9E2BB41C0B8} = {99D234A0-6830-4C0C-91E8-C626DA939D59} |
|||
{5707BF58-3A98-4283-A6D0-3B78EF7ED2F1} = {99D234A0-6830-4C0C-91E8-C626DA939D59} |
|||
{DD328472-01CE-4CA8-AF29-C098FC499483} = {1191C1AE-7275-4643-AF24-BEC852717299} |
|||
EndGlobalSection |
|||
GlobalSection(ExtensibilityGlobals) = postSolution |
|||
SolutionGuid = {0D069898-04B7-4D24-A6A4-D7C703B8BFFC} |
|||
EndGlobalSection |
|||
EndGlobal |
Loading…
Reference in new issue