提交功能
This commit is contained in:
11
Http/ErrorCode.cs
Normal file
11
Http/ErrorCode.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace ACBuildService;
|
||||
|
||||
public enum ErrorCode
|
||||
{
|
||||
/// <summary>
|
||||
/// 成功
|
||||
/// </summary>
|
||||
Success = 0,
|
||||
|
||||
ArgsError = 1,
|
||||
}
|
||||
24
Http/Extensions/DateTimeExtensions.cs
Normal file
24
Http/Extensions/DateTimeExtensions.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace ACBuildService;
|
||||
|
||||
public static class DateTimeExtensions
|
||||
{
|
||||
public static bool IsSameDay(this DateTime date1, DateTime date2)
|
||||
{
|
||||
return date1.Date == date2.Date;
|
||||
}
|
||||
|
||||
public static bool IsToday(this DateTime date)
|
||||
{
|
||||
return date.Date == DateTime.Today;
|
||||
}
|
||||
|
||||
public static bool IsYesterday(this DateTime date)
|
||||
{
|
||||
return date.Date == DateTime.Today.AddDays(-1);
|
||||
}
|
||||
|
||||
public static bool IsTomorrow(this DateTime date)
|
||||
{
|
||||
return date.Date == DateTime.Today.AddDays(1);
|
||||
}
|
||||
}
|
||||
25
Http/Extensions/FileNameExtensions.cs
Normal file
25
Http/Extensions/FileNameExtensions.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System.Text;
|
||||
|
||||
namespace ACBuildService;
|
||||
|
||||
|
||||
public static class FileNameExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 替换非法字符
|
||||
/// </summary>
|
||||
/// <param name="fileName"></param>
|
||||
/// <returns></returns>
|
||||
public static string ReplaceInvalidCharacters(this string fileName)
|
||||
{
|
||||
var invalidChars = Path.GetInvalidFileNameChars();
|
||||
var replacedFileName = new StringBuilder();
|
||||
|
||||
foreach (var c in fileName)
|
||||
{
|
||||
replacedFileName.Append(!invalidChars.Contains(c) ? c : '#');
|
||||
}
|
||||
|
||||
return replacedFileName.ToString();
|
||||
}
|
||||
}
|
||||
49
Http/Extensions/HttpContextExtension.cs
Normal file
49
Http/Extensions/HttpContextExtension.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace ACBuildService;
|
||||
|
||||
public static class HttpContextExtension
|
||||
{
|
||||
public static async Task Error(this HttpContext context, ErrorCode code, string msg = "")
|
||||
{
|
||||
var res = new ResponseData<string>
|
||||
{
|
||||
Code = (int)code,
|
||||
Data = msg
|
||||
};
|
||||
await context.WriteJson(res);
|
||||
}
|
||||
|
||||
public static async Task Success(this HttpContext context)
|
||||
{
|
||||
var res = new ResponseData<string>
|
||||
{
|
||||
Code = 0,
|
||||
Data = string.Empty
|
||||
};
|
||||
await context.WriteJson(res);
|
||||
}
|
||||
|
||||
public static async Task Success<T>(this HttpContext context, T data)
|
||||
{
|
||||
var res = new ResponseData<T>
|
||||
{
|
||||
Code = 0,
|
||||
Data = data
|
||||
};
|
||||
await context.WriteJson(res);
|
||||
}
|
||||
|
||||
public static async Task WriteJson<T>(this HttpContext context, T obj)
|
||||
{
|
||||
context.Response.ContentType = "application/json";
|
||||
if (obj is string st)
|
||||
{
|
||||
await context.Response.WriteAsync(st);
|
||||
}
|
||||
else
|
||||
{
|
||||
await context.Response.WriteAsync(JSON.Serialize(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
75
Http/HttpService.cs
Normal file
75
Http/HttpService.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace ACBuildService;
|
||||
|
||||
public static class HttpService
|
||||
{
|
||||
public static WebApplication App { get; private set; }
|
||||
public static readonly string BaseRootPath = $"{AppContext.BaseDirectory}wwwroot/";
|
||||
|
||||
public static async Task Start()
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
|
||||
// 添加控制器服务
|
||||
builder.Services.AddControllers();
|
||||
|
||||
builder.WebHost.UseKestrel(options =>
|
||||
{
|
||||
//设置最大1G, 这里的单位是byte
|
||||
options.Limits.MaxRequestBodySize = int.MaxValue;
|
||||
|
||||
// HTTP
|
||||
if (ACBuildService.App.Settings.HttpPort > 0)
|
||||
{
|
||||
options.ListenAnyIP(ACBuildService.App.Settings.HttpPort);
|
||||
}
|
||||
})
|
||||
.ConfigureLogging(logging => { logging.SetMinimumLevel(LogLevel.Error); });
|
||||
|
||||
builder.Services.Configure<FormOptions>(x =>
|
||||
{
|
||||
x.ValueLengthLimit = int.MaxValue;
|
||||
x.MultipartBodyLengthLimit = int.MaxValue;
|
||||
});
|
||||
|
||||
var contentTypeProvider = new FileExtensionContentTypeProvider();
|
||||
contentTypeProvider.Mappings.Add(".bundle", "application/octet-stream");
|
||||
contentTypeProvider.Mappings.Add(".meta", "application/octet-stream");
|
||||
contentTypeProvider.Mappings.Add(".bytes", "application/octet-stream");
|
||||
|
||||
Log.Info($"wwwroot={BaseRootPath}");
|
||||
|
||||
App = builder.Build();
|
||||
|
||||
// 针对 files 文件夹配置
|
||||
var fileServerOptions = new FileServerOptions
|
||||
{
|
||||
RequestPath = "/files",
|
||||
EnableDirectoryBrowsing = true,
|
||||
FileProvider = new PhysicalFileProvider(BaseRootPath),
|
||||
StaticFileOptions =
|
||||
{
|
||||
ContentTypeProvider = contentTypeProvider
|
||||
}
|
||||
};
|
||||
|
||||
App.UseFileServer(fileServerOptions);
|
||||
|
||||
// 使用控制器路由
|
||||
App.MapControllers();
|
||||
|
||||
// 不再需要手动映射路由
|
||||
// App.MapGet("/", HttpHandler.Index);
|
||||
// App.MapPost("/api", HttpHandler.Api);
|
||||
|
||||
await App.StartAsync();
|
||||
Log.Info("http 服务启动成功");
|
||||
}
|
||||
}
|
||||
161
Http/Page/ApiController.cs
Normal file
161
Http/Page/ApiController.cs
Normal file
@@ -0,0 +1,161 @@
|
||||
// Controllers/ApiController.cs
|
||||
|
||||
using ACBuildService.Filters;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ACBuildService.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api")]
|
||||
[SignValidation]
|
||||
public class ApiController : NBControllerBase
|
||||
{
|
||||
#region 信息增删改
|
||||
|
||||
[HttpPost("list")]
|
||||
public async Task<IActionResult> List([FromForm] PageFormArgs data)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (data.Page < 1) data.Page = 1;
|
||||
if (data.PageSize < 1) data.PageSize = 50;
|
||||
|
||||
int totalCount = 0;
|
||||
|
||||
var list = DB.Main.Queryable<VideoTable>().ToPageList(data.Page, data.PageSize, ref totalCount);
|
||||
List<VideoRetData> retList = [];
|
||||
var retData = new PageListData<VideoRetData>
|
||||
{
|
||||
TotalCount = totalCount,
|
||||
Page = data.Page,
|
||||
List = retList,
|
||||
PageSize = data.PageSize
|
||||
};
|
||||
foreach (var video in list)
|
||||
{
|
||||
var r = new VideoRetData
|
||||
{
|
||||
Id = video.Id,
|
||||
FilePath = video.FilePath,
|
||||
Title = video.Title,
|
||||
Desc = video.Desc,
|
||||
Like = video.Like,
|
||||
Message = video.Message,
|
||||
Share = video.Share,
|
||||
Collect = video.Collect,
|
||||
AuthorName = video.AuthorName
|
||||
};
|
||||
retList.Add(r);
|
||||
}
|
||||
|
||||
|
||||
await Task.CompletedTask;
|
||||
|
||||
return Success(retData);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, new { error = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("add")]
|
||||
public async Task<IActionResult> Add([FromForm] AddFormArgs data)
|
||||
{
|
||||
VideoDownload.Add(data.Text);
|
||||
await Task.CompletedTask;
|
||||
return Success();
|
||||
}
|
||||
|
||||
[HttpPost("edit")]
|
||||
public async Task<IActionResult> Edit([FromForm] EditFormArgs data)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
DB.Main.Updateable(new VideoTable()
|
||||
{
|
||||
Id = data.Id,
|
||||
Title = data.Title,
|
||||
Desc = data.Desc,
|
||||
Like = data.Like,
|
||||
Message = data.Message,
|
||||
Share = data.Share,
|
||||
Collect = data.Collect,
|
||||
AuthorName = data.AuthorName
|
||||
}).ExecuteCommand();
|
||||
return Success();
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("remove")]
|
||||
public async Task<IActionResult> Remove([FromForm] IdFormArgs data)
|
||||
{
|
||||
var video = await DB.Main.Queryable<VideoTable>().FirstAsync(t => t.Id == data.Id);
|
||||
if (video != null)
|
||||
{
|
||||
//删除本地文件
|
||||
var filePath = FileUtil.GetVideoPath(video.FilePath);
|
||||
if (System.IO.File.Exists(filePath))
|
||||
{
|
||||
System.IO.File.Delete(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
await DB.Main.Deleteable<VideoTable>().Where(it => it.Id == data.Id).ExecuteCommandAsync();
|
||||
return Success();
|
||||
}
|
||||
|
||||
[HttpPost("info")]
|
||||
public async Task<IActionResult> Info()
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
var list = DB.Main.Queryable<DeviceTable>().ToList();
|
||||
return Success(list);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region 信息统计
|
||||
|
||||
[HttpPost("device")]
|
||||
public async Task<IActionResult> Device([FromForm] DeviceFormArgs data)
|
||||
{
|
||||
var now = DateTime.Now.AddDays(-1);
|
||||
var device = await DB.Main.Queryable<DeviceTable>().FirstAsync(t => t.Id == data.Device);
|
||||
if (device == null)
|
||||
{
|
||||
device = new DeviceTable
|
||||
{
|
||||
Id = data.Device,
|
||||
Name = data.DeviceName,
|
||||
WatchId = data.Id,
|
||||
TotalTime = 0,
|
||||
TodayTime = data.Time,
|
||||
LastTime = now
|
||||
};
|
||||
|
||||
await DB.Main.Insertable(device).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
if (!now.IsSameDay(device.LastTime))
|
||||
{
|
||||
device.TodayTime = data.Time; //重置今日时长
|
||||
}
|
||||
else
|
||||
{
|
||||
device.TodayTime += data.Time; //今日时长增加
|
||||
}
|
||||
|
||||
device.WatchId = data.Id;
|
||||
device.TotalTime += data.Time;
|
||||
|
||||
device.LastTime = now;
|
||||
|
||||
await DB.Main.Updateable(device).ExecuteCommandAsync();
|
||||
|
||||
return Success();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
68
Http/Page/Args.cs
Normal file
68
Http/Page/Args.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
namespace ACBuildService.Controllers;
|
||||
|
||||
public class BaseFormArgs
|
||||
{
|
||||
public string Sign { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class PageFormArgs : BaseFormArgs
|
||||
{
|
||||
public int Page { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
}
|
||||
|
||||
public class AddFormArgs : BaseFormArgs
|
||||
{
|
||||
public string Text { get; set; }
|
||||
}
|
||||
|
||||
public class IdFormArgs : BaseFormArgs
|
||||
{
|
||||
public int Id { get; set; }
|
||||
}
|
||||
|
||||
public class EditFormArgs : VideoRetData
|
||||
{
|
||||
public string Sign { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class DeviceFormArgs : IdFormArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// 设备id
|
||||
/// </summary>
|
||||
public string Device { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备名称
|
||||
/// </summary>
|
||||
public string DeviceName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 观看时长,离上次
|
||||
/// </summary>
|
||||
public long Time { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class PageListData<T>
|
||||
{
|
||||
public int TotalCount { get; set; }
|
||||
public int Page { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public List<T> List { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class VideoRetData
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string FilePath { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Desc { get; set; }
|
||||
public int Like { get; set; }
|
||||
public int Message { get; set; }
|
||||
public int Share { get; set; }
|
||||
public int Collect { get; set; }
|
||||
public string AuthorName { get; set; }
|
||||
}
|
||||
14
Http/Page/HomeController.cs
Normal file
14
Http/Page/HomeController.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ACBuildService.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("/")]
|
||||
public class HomeController : NBControllerBase
|
||||
{
|
||||
[HttpGet("/")]
|
||||
public IActionResult Index()
|
||||
{
|
||||
return Content("service running");
|
||||
}
|
||||
}
|
||||
36
Http/Page/NBControllerBase.cs
Normal file
36
Http/Page/NBControllerBase.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ACBuildService.Controllers;
|
||||
|
||||
public abstract class NBControllerBase : ControllerBase
|
||||
{
|
||||
public OkObjectResult Error(ErrorCode code, string msg = "")
|
||||
{
|
||||
var res = new ResponseData<string>
|
||||
{
|
||||
Code = (int)code,
|
||||
Data = msg
|
||||
};
|
||||
return Ok(res);
|
||||
}
|
||||
|
||||
public OkObjectResult Success()
|
||||
{
|
||||
var res = new ResponseData<string>
|
||||
{
|
||||
Code = 0,
|
||||
Data = string.Empty
|
||||
};
|
||||
return Ok(res);
|
||||
}
|
||||
|
||||
public OkObjectResult Success<T>(T data)
|
||||
{
|
||||
var res = new ResponseData<T>
|
||||
{
|
||||
Code = 0,
|
||||
Data = data
|
||||
};
|
||||
return Ok(res);
|
||||
}
|
||||
}
|
||||
9
Http/ResponseData.cs
Normal file
9
Http/ResponseData.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ACBuildService;
|
||||
|
||||
public class ResponseData<T>
|
||||
{
|
||||
[JsonPropertyName("code")] public int Code { get; set; }
|
||||
[JsonPropertyName("data")] public T Data { get; set; }
|
||||
}
|
||||
58
Http/SignValidationAttribute.cs
Normal file
58
Http/SignValidationAttribute.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
// Filters/SignValidationFilter.cs
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace ACBuildService.Filters;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public class SignValidationAttribute : Attribute, IAsyncActionFilter
|
||||
{
|
||||
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
|
||||
{
|
||||
// 检查请求是否包含 form-data
|
||||
if (context.HttpContext.Request.HasFormContentType)
|
||||
{
|
||||
var form = context.HttpContext.Request.Form;
|
||||
|
||||
// 尝试从表单中获取 Sign
|
||||
if (form.TryGetValue("Sign", out var signValue))
|
||||
{
|
||||
if (!signValue.ToString().Equals(App.Settings.Password))
|
||||
{
|
||||
// 验证失败,直接返回错误
|
||||
context.Result = new ObjectResult(new ResponseData<string>
|
||||
{
|
||||
Code = (int)ErrorCode.ArgsError,
|
||||
Data = "sign is error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 没有 Sign 字段
|
||||
context.Result = new ObjectResult(new ResponseData<string>
|
||||
{
|
||||
Code = (int)ErrorCode.ArgsError,
|
||||
Data = "sign is null"
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Result = new ObjectResult(new ResponseData<string>
|
||||
{
|
||||
Code = (int)ErrorCode.ArgsError,
|
||||
Data = "请求格式必须是 form-data"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证通过,继续执行 Action
|
||||
await next();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user