diff --git a/Blog.Core.Api/Blog.Core.Model.xml b/Blog.Core.Api/Blog.Core.Model.xml index 36975d1..ea9857b 100644 --- a/Blog.Core.Api/Blog.Core.Model.xml +++ b/Blog.Core.Api/Blog.Core.Model.xml @@ -982,6 +982,76 @@ Tibug 博文 + + + users自定义服务器 + + + + + 用户流量每月汇总表 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Trojan服务器 + + + + + users自定义URL服务器 + + + + + Trojan用户 + + + + + 历史流量记录 + + 用户跟角色关联表 @@ -2407,6 +2477,73 @@ + + + 限制流量dto + 作者:胡丁文 + 时间:2020-4-27 16:57:07 + + + + + 用户 + + + + + 流量(-1为无限,单位为最小单位byte) + + + + + Trojan服务器拼接服务器和订阅地址 + + + + + 普通订阅连接 + + + + + clash订阅连接 + + + + + 备用clash订阅连接 + + + + + Trojan用户流量统计分组 + + + + + 用户ID + + + + + 月度 + + + + + 上传流量 + + + + + 下载流量 + + + + + 下载流量 + + 微信接口消息DTO diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index 3321d53..a14a900 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -650,6 +650,159 @@ + + + 获取Trojan用户 + + + + + + + + + 获取Trojan用户-下拉列表用 + + + + + + 添加Trojan用户 + + + + + + + 更新Trojan用户 + + + + + + + 删除用户 + + + + + + + 重置流量 + + + + + + + 限制流量 + + + + + + + 重置链接密码 + + + + + + + 获取Trojan服务器 + + + + + + 获取拼接后的Trojan服务器 + + passwordshow + + + + + 删除Trojan服务器 + + + + + + + 更新Trojan服务器 + + + + + + + 添加Trojan服务器 + + + + + + + 获取Cus服务器 + + + + + + 删除Cus服务器 + + + + + + + 更新Cus服务器 + + + + + + + 添加Cus服务器 + + + + + + + 获取Url服务器 + + + + + + 删除Url服务器 + + + + + + + 更新Url服务器 + + + + + + + 添加Url服务器 + + + + + + + 获取订阅数据 + + 链接密码 + 是否使用base64加密 + + 用户管理 @@ -1062,6 +1215,13 @@ 卡片消息对象 + + + 推送卡片消息接口 + + 卡片消息对象 + + 推送文本消息 diff --git a/Blog.Core.Api/Controllers/TrojanController.cs b/Blog.Core.Api/Controllers/TrojanController.cs new file mode 100644 index 0000000..8ec64c8 --- /dev/null +++ b/Blog.Core.Api/Controllers/TrojanController.cs @@ -0,0 +1,451 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Blog.Core.Common; +using Blog.Core.Common.Extensions; +using Blog.Core.Common.Helper; +using Blog.Core.Common.HttpContextUser; +using Blog.Core.IServices; +using Blog.Core.IServices.BASE; +using Blog.Core.Model; +using Blog.Core.Model.Models; +using Blog.Core.Model.ViewModels; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Blog.Core.Controllers +{ + [Route("api/[controller]/[action]")] + [ApiController] + [Authorize(Permissions.Name)] + public class TrojanController : ControllerBase + { + private ITrojanUsersServices _trojanUsersServices; + public IBaseServices _baseServicesServers; + public IBaseServices _baseServicesDetails; + public IBaseServices _baseServicesCusServers; + public IBaseServices _baseServicesUrlServers; + private IUser _user; + public TrojanController(ITrojanUsersServices trojanUsersServices,IUser user + , IBaseServices baseServicesServers + , IBaseServices baseServicesDetails + , IBaseServices baseServicesCusServers + , IBaseServices baseServicesUrlServers) + { + _baseServicesDetails = baseServicesDetails; + _baseServicesServers = baseServicesServers; + _trojanUsersServices = trojanUsersServices; + _baseServicesCusServers = baseServicesCusServers; + _baseServicesUrlServers = baseServicesUrlServers; + _user = user; + } + /// + /// 获取Trojan用户 + /// + /// + /// + /// + /// + [HttpGet] + public async Task>> GetUser([FromQuery]PaginationModel pagination, [FromQuery] string name, [FromQuery] bool isuse) + { + var whereFind = LinqHelper.True(); + if (!string.IsNullOrEmpty(name)) + whereFind = whereFind.And(t=>t.username.Contains(name)); + if (isuse) + whereFind = whereFind.And(t => t.upload > 0 || t.download > 0); + var data = await _trojanUsersServices.QueryPage(whereFind, pagination.PageIndex, pagination.PageSize); + if (data.data.Count > 0) + { + var ids = data.data.Select(t => t.id).ToList(); + var where = LinqHelper.True(); + where = where.And(t => ids.Contains(t.userId));//.And(t => t.calDate < DateTime.Now).And(t => t.calDate > DateTime.Now.AddMonths(-12)); + var userDetails = await _baseServicesDetails.Query(where); + foreach (var trojanUser in data.data) + { + var ls = from t in userDetails + where t.userId == trojanUser.id + group t by new { moth = t.calDate.ToString("yyyy-MM"), id = t.userId } into g + orderby g.Key.moth descending + select new TrojanUseDetailDto { userId = g.Key.id, moth = g.Key.moth, up = g.Sum(t => Convert.ToDecimal(t.upload)), down = g.Sum(t => Convert.ToDecimal(t.download)) }; + var lsData = ls.ToList(); + trojanUser.useList = lsData; + } + } + return MessageModel>.Success("获取成功", data); + } + + /// + /// 获取Trojan用户-下拉列表用 + /// + /// + [HttpGet] + public async Task> GetAllTrojanUser() + { + var data = await _trojanUsersServices.QueryTable("select id,username from users"); + return MessageModel.Success("获取成功", data); + } + /// + /// 添加Trojan用户 + /// + /// + /// + [HttpPost] + public async Task> AddUser([FromBody]TrojanUsers user) + { + var find = await _trojanUsersServices.Query(t => t.username == user.username); + if(find!=null && find.Count>0) return MessageModel.Fail("用户名已存在"); + var pass = StringHelper.GetGUID(); + var passEcrypt = ShaHelper.Sha224(pass); + //user.quota = 0; + user.upload = 0; + user.download = 0; + user.password = passEcrypt; + user.passwordshow = pass; + var data = await _trojanUsersServices.Add(user); + return MessageModel.Success("添加成功", data); + } + /// + /// 更新Trojan用户 + /// + /// + /// + [HttpPut] + public async Task> UpdateUser([FromBody]TrojanUsers user) + { + var find = await _trojanUsersServices.QueryById(user.id); + if (find == null) return MessageModel.Fail("用户名不存在"); + find.username = user.username; + var data = await _trojanUsersServices.Update(find, new List { "username" }); + return MessageModel.Success("更新成功", data); + } + + /// + /// 删除用户 + /// + /// + /// + [HttpPut] + public async Task> DelUser([FromBody]int[] users) + { + var data = await _trojanUsersServices.Query(t => users.Contains(t.id)); + var list = data.Select(t => t.id.ToString()).ToArray(); + await _trojanUsersServices.DeleteByIds(list); + return MessageModel.Success("删除成功"); + } + /// + /// 重置流量 + /// + /// + /// + [HttpPut] + public async Task> ResetFlow([FromBody]int[] users) + { + var data = await _trojanUsersServices.Query(t => users.Contains(t.id)); + foreach (var item in data) + { + item.upload = 0; + item.download = 0; + await _trojanUsersServices.Update(item, new List { "upload", "download" }); + } + return MessageModel.Success("重置流量成功"); + } + /// + /// 限制流量 + /// + /// + /// + [HttpPut] + public async Task> LimitFlow([FromBody] TrojanLimitFlowDto limit) + { + var data = await _trojanUsersServices.Query(t => limit.users.Contains(t.id)); + foreach (var item in data) + { + item.quota = limit.quota; + await _trojanUsersServices.Update(item, new List { "quota" }); + } + return MessageModel.Success("限制流量成功"); + } + /// + /// 重置链接密码 + /// + /// + /// + [HttpPut] + public async Task> ResetPass([FromBody]int[] users) + { + var data = await _trojanUsersServices.Query(t => users.Contains(t.id)); + var pass = StringHelper.GetGUID(); + var passEcrypt = ShaHelper.Sha224(pass); + foreach (var item in data) + { + item.password = passEcrypt; + item.passwordshow = pass; + await _trojanUsersServices.Update(item, new List { "password" , "passwordshow" }); + } + return MessageModel.Success("重置链接密码成功"); + } + /// + /// 获取Trojan服务器 + /// + /// + [HttpGet] + public async Task>> GetServers() + { + var data = await _baseServicesServers.Query(); + data = data.OrderBy(t => t.servername).ToList(); + return MessageModel>.Success("获取成功", data); + } + /// + /// 获取拼接后的Trojan服务器 + /// + /// passwordshow + /// + [HttpGet] + public async Task> GetSpliceServers(string id) + { + var data = await _baseServicesServers.Query(); + data = data.OrderBy(t => t.servername).ToList(); + var res = new TrojanServerSpliceDto(); + res.normalApi = AppSettings.app(new string[] { "trojan", "normalApi" }).ObjToString(); + res.clashApi = AppSettings.app(new string[] { "trojan", "clashApi" }).ObjToString(); + res.clashApiBackup = AppSettings.app(new string[] { "trojan", "clashApiBackup" }).ObjToString(); + foreach (var item in data) + { + var serverSplice = GetSplice(item, id); + res.list.Add(new TrojanServerDto { name = item.servername, value = serverSplice }); + } + return MessageModel.Success("获取成功", res); ; + + } + /// + /// 删除Trojan服务器 + /// + /// + /// + [HttpPut] + public async Task>> DelServers([FromBody]int[] servers) + { + var data = await _baseServicesServers.DeleteByIds(servers.Select(t=>t.ToString()).ToArray()); + if (data) + return MessageModel>.Success("删除成功"); + else + return MessageModel>.Fail("删除失败"); + } + /// + /// 更新Trojan服务器 + /// + /// + /// + [HttpPut] + public async Task>> UpdateServers(TrojanServers server) + { + var data = await _baseServicesServers.Update(server); + return MessageModel>.Success("更新成功"); + } + /// + /// 添加Trojan服务器 + /// + /// + /// + [HttpPost] + public async Task>> AddServers(TrojanServers server) + { + var data = await _baseServicesServers.Add(server); + return MessageModel>.Success("添加成功"); + } + + /// + /// 获取Cus服务器 + /// + /// + [HttpGet] + public async Task>> GetCusServers() + { + var data = await _baseServicesCusServers.Query(); + data = data.OrderBy(t => t.servername).ToList(); + return MessageModel>.Success("获取成功", data); + } + /// + /// 删除Cus服务器 + /// + /// + /// + [HttpPut] + public async Task>> DelCusServers([FromBody] int[] servers) + { + var data = await _baseServicesCusServers.DeleteByIds(servers.Select(t => t.ToString()).ToArray()); + if (data) + return MessageModel>.Success("删除成功"); + else + return MessageModel>.Fail("删除失败"); + } + /// + /// 更新Cus服务器 + /// + /// + /// + [HttpPut] + public async Task>> UpdateCusServers(TrojanCusServers server) + { + var data = await _baseServicesCusServers.Update(server); + return MessageModel>.Success("更新成功"); + } + /// + /// 添加Cus服务器 + /// + /// + /// + [HttpPost] + public async Task>> AddCusServers(TrojanCusServers server) + { + var data = await _baseServicesCusServers.Add(server); + return MessageModel>.Success("添加成功"); + } + + + /// + /// 获取Url服务器 + /// + /// + [HttpGet] + public async Task>> GetUrlServers() + { + var data = await _baseServicesUrlServers.Query(); + data = data.OrderBy(t => t.servername).ToList(); + return MessageModel>.Success("获取成功", data); + } + /// + /// 删除Url服务器 + /// + /// + /// + [HttpPut] + public async Task>> DelUrlServers([FromBody] int[] servers) + { + var data = await _baseServicesUrlServers.DeleteByIds(servers.Select(t => t.ToString()).ToArray()); + if (data) + return MessageModel>.Success("删除成功"); + else + return MessageModel>.Fail("删除失败"); + } + /// + /// 更新Url服务器 + /// + /// + /// + [HttpPut] + public async Task>> UpdateUrlServers(TrojanUrlServers server) + { + var data = await _baseServicesUrlServers.Update(server); + return MessageModel>.Success("更新成功"); + } + /// + /// 添加Url服务器 + /// + /// + /// + [HttpPost] + public async Task>> AddUrlServers(TrojanUrlServers server) + { + var data = await _baseServicesUrlServers.Add(server); + return MessageModel>.Success("添加成功"); + } + private string GetSplice(TrojanServers item,string passwordshow) + { + if ("0".Equals(item.servertype)) + return $"trojan://{passwordshow}@{item.serveraddress}:{item.serverport}?allowinsecure=0&tfo=0&peer={(string.IsNullOrEmpty(item.serverpeer) ? item.serverpeer : item.serveraddress)}#{item.servername}"; + else if ("1".Equals(item.servertype)) + return $"trojan://{passwordshow}@{item.serveraddress}:{item.serverport}?wspath={item.serverpath}&ws=1&peer={(string.IsNullOrEmpty(item.serverpeer) ? item.serverpeer : item.serveraddress)}#{item.servername}"; + else + return $"servertype:({item.servertype})错误"; + } + private List GetSplice(List items, string passwordshow) + { + List ls = new List(); + foreach (var item in items) + { + ls.Add(GetSplice(item, passwordshow)); + } + return ls; + } + /// + /// 获取订阅数据 + /// + /// 链接密码 + /// 是否使用base64加密 + /// + [HttpGet] + [AllowAnonymous] + public async Task RSS(string id,bool isUseBase64=true) + { + StringBuilder sb = new StringBuilder(); + try + { + var user = (await _trojanUsersServices.Query(t => t.passwordshow == id)).FirstOrDefault(); + if (user == null) throw new Exception("用户不存在"); + var data = await _baseServicesServers.Query(t => (t.userid == user.id || t.userid <= 0) && t.serverenable); + if (data != null) + { + data = data.OrderBy(t => t.servername).ToList(); + foreach (var item in data) + { + sb.AppendLine(GetSplice(item, user.passwordshow)); + } + } + var cusData = await _baseServicesCusServers.Query(t=> (t.userid == user.id || t.userid <=0) && t.serverenable); + if (cusData != null) + { + cusData = cusData.OrderBy(t => t.servername).ToList(); + foreach (var item in cusData) + { + sb.AppendLine(item.serveraddress); + } + } + var urlData = await _baseServicesUrlServers.Query(t => (t.userid == user.id || t.userid <= 0) && t.serverenable); + if (urlData != null) + { + urlData = urlData.OrderBy(t => t.servername).ToList(); + foreach (var item in urlData) + { + try + { + var urlStrObj = await HttpHelper.GetAsync(item.serveraddress); + var lines = ""; + try + { + lines = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(urlStrObj)); + } + catch (Exception) + { + lines = urlStrObj; + } + finally + { + sb.AppendLine(lines); + } + } + catch (Exception ex) + { + sb.AppendLine($"trojan://xxxxxx@xxxxxx.xx:443?allowinsecure=0&tfo=0#{ex.Message}"); + } + } + } + } + catch (Exception ex) + { + sb.AppendLine($"trojan://xxxxxx@xxxxxx.xx:443?allowinsecure=0&tfo=0#{ex.Message}"); + } + if (isUseBase64) + { + return Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(sb.ToString())); + } + else{ + return sb.ToString(); + } + } + } +} \ No newline at end of file diff --git a/Blog.Core.Api/Controllers/WeChatController.cs b/Blog.Core.Api/Controllers/WeChatController.cs index 4c4877f..c215f56 100644 --- a/Blog.Core.Api/Controllers/WeChatController.cs +++ b/Blog.Core.Api/Controllers/WeChatController.cs @@ -146,6 +146,18 @@ namespace Blog.Core.Controllers return await _weChatConfigServices.PushCardMsg(msg, pushUserIP); } /// + /// 推送卡片消息接口 + /// + /// 卡片消息对象 + /// + [HttpGet] + [AllowAnonymous] + public async Task> PushCardMsgGet([FromQuery] WeChatCardMsgDataDto msg) + { + string pushUserIP = $"{Request.HttpContext.Connection.RemoteIpAddress}:{Request.HttpContext.Connection.RemotePort}"; + return await _weChatConfigServices.PushCardMsg(msg, pushUserIP); + } + /// /// 推送文本消息 /// /// 消息对象 diff --git a/Blog.Core.Api/Dockerfile b/Blog.Core.Api/Dockerfile index 4361807..1eb0572 100644 --- a/Blog.Core.Api/Dockerfile +++ b/Blog.Core.Api/Dockerfile @@ -3,7 +3,7 @@ #FROM swr.cn-south-1.myhuaweicloud.com/mcr/aspnet:5.0-alpine #FROM mcr.microsoft.com/dotnet/core/aspnet:5.0-buster-slim -FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim +FROM mcr.microsoft.com/dotnet/aspnet:6.0-bullseye-slim RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime RUN echo 'Asia/Shanghai' >/etc/timezone diff --git a/Blog.Core.IServices/BASE/IBaseServices.cs b/Blog.Core.IServices/BASE/IBaseServices.cs index 27daae9..de4a7a0 100644 --- a/Blog.Core.IServices/BASE/IBaseServices.cs +++ b/Blog.Core.IServices/BASE/IBaseServices.cs @@ -26,6 +26,7 @@ namespace Blog.Core.IServices.BASE Task DeleteByIds(object[] ids); Task Update(TEntity model); + Task Update(List model); Task Update(TEntity entity, string where); Task Update(object operateAnonymousObjects); diff --git a/Blog.Core.IServices/ITrojanUsersServices.cs b/Blog.Core.IServices/ITrojanUsersServices.cs new file mode 100644 index 0000000..92bac1d --- /dev/null +++ b/Blog.Core.IServices/ITrojanUsersServices.cs @@ -0,0 +1,14 @@ +using Blog.Core.IServices.BASE; +using Blog.Core.Model.Models; + +namespace Blog.Core.IServices +{ + /// + /// TrojanUsersServices + /// + public interface ITrojanUsersServices : IBaseServices + { + + } +} + diff --git a/Blog.Core.Model/Models/TrojanCusServers.cs b/Blog.Core.Model/Models/TrojanCusServers.cs new file mode 100644 index 0000000..03f0253 --- /dev/null +++ b/Blog.Core.Model/Models/TrojanCusServers.cs @@ -0,0 +1,26 @@ + +using System; +using System.Linq; +using System.Text; +using SqlSugar; + +namespace Blog.Core.Model.Models +{ + /// + ///users自定义服务器 + /// + [SugarTable("users_cus", "users自定义服务器")] + [TenantAttribute("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') + public partial class TrojanCusServers + { + + [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = true)] + public int id { set; get; } + public int userid { get; set; } + public string servername { set; get; } + public string serveraddress { set; get; } + [SugarColumn(IsNullable = true)] + public string serverremark { get; set; } + public bool serverenable { get; set; } + } +} diff --git a/Blog.Core.Model/Models/TrojanDetails.cs b/Blog.Core.Model/Models/TrojanDetails.cs new file mode 100644 index 0000000..dda53d9 --- /dev/null +++ b/Blog.Core.Model/Models/TrojanDetails.cs @@ -0,0 +1,63 @@ + +//模板自动生成(请勿修改) +//作者:胡丁文 +using System; +using System.Linq; +using System.Text; +using SqlSugar; + +namespace Blog.Core.Model.Models +{ + /// + ///用户流量每月汇总表 + /// + [SugarTable("users_detail", "用户流量每月汇总表")] + [TenantAttribute("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') + public partial class TrojanDetails + { + + /// + /// + /// + [SugarColumn(IsPrimaryKey = true, IsIdentity = true)] + public int id { get; set; } + + /// + /// + /// + public int userId { get; set; } + + /// + /// + /// + public DateTime calDate { get; set; } + + /// + /// + /// + public ulong download { get; set; } + + /// + /// + /// + public ulong upload { get; set; } + + /// + /// + /// + [SugarColumn(IsNullable = true)] + public int? CreateId { get; set; } + + /// + /// + /// + [SugarColumn(IsNullable = true)] + public string CreateBy { get; set; } + + /// + /// + /// + [SugarColumn(IsNullable = true)] + public DateTime? CreateTime { get; set; } + } +} diff --git a/Blog.Core.Model/Models/TrojanServers.cs b/Blog.Core.Model/Models/TrojanServers.cs new file mode 100644 index 0000000..d9d8275 --- /dev/null +++ b/Blog.Core.Model/Models/TrojanServers.cs @@ -0,0 +1,31 @@ + +using System; +using System.Linq; +using System.Text; +using SqlSugar; + +namespace Blog.Core.Model.Models +{ + /// + ///Trojan服务器 + /// + [SugarTable("servers", "Trojan服务器")] + [TenantAttribute("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') + public partial class TrojanServers + { + + [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = true)] + public int id { set; get; } + public int userid { get; set; } + public string servername { set; get; } + public string serveraddress { set; get; } + public int serverport { get; set; } + [SugarColumn(IsNullable = true)] + public string serverremark { get; set; } + public bool serverenable { get; set; } + public string serverpeer { get; set; } + [SugarColumn(IsNullable = true)] + public string serverpath { get; set; } + public string servertype { get; set; } + } +} diff --git a/Blog.Core.Model/Models/TrojanUrlServers.cs b/Blog.Core.Model/Models/TrojanUrlServers.cs new file mode 100644 index 0000000..db48343 --- /dev/null +++ b/Blog.Core.Model/Models/TrojanUrlServers.cs @@ -0,0 +1,26 @@ + +using System; +using System.Linq; +using System.Text; +using SqlSugar; + +namespace Blog.Core.Model.Models +{ + /// + ///users自定义URL服务器 + /// + [SugarTable("users_url", "users自定义URL服务器")] + [TenantAttribute("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') + public partial class TrojanUrlServers + { + + [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = true)] + public int id { set; get; } + public int userid { get; set; } + public string servername { set; get; } + public string serveraddress { set; get; } + [SugarColumn(IsNullable = true)] + public string serverremark { get; set; } + public bool serverenable { get; set; } + } +} diff --git a/Blog.Core.Model/Models/TrojanUsers.cs b/Blog.Core.Model/Models/TrojanUsers.cs new file mode 100644 index 0000000..796c044 --- /dev/null +++ b/Blog.Core.Model/Models/TrojanUsers.cs @@ -0,0 +1,39 @@ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Blog.Core.Model.ViewModels; +using SqlSugar; + +namespace Blog.Core.Model.Models +{ + /// + ///Trojan用户 + /// + [SugarTable("users", "Trojan用户表")] + [TenantAttribute("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') + public partial class TrojanUsers + { + + [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = true)] + public int id { set; get; } + public string username { set; get; } + public string password { set; get; } + public Int64 quota { set; get; } + public UInt64 download { set; get; } + public UInt64 upload { set; get; } + public string passwordshow { set; get; } + [SugarColumn(IsNullable = true)] + public int CreateId { get; set; } + [SugarColumn(IsNullable = true)] + public string CreateBy { get; set; } + [SugarColumn(IsNullable = true)] + public DateTime? CreateTime { get; set; } + /// + /// 历史流量记录 + /// + [SugarColumn(IsIgnore = true)] + public List useList { get; set; } + } +} diff --git a/Blog.Core.Model/ViewModels/TrojanLimitFlowDto.cs b/Blog.Core.Model/ViewModels/TrojanLimitFlowDto.cs new file mode 100644 index 0000000..c0efd34 --- /dev/null +++ b/Blog.Core.Model/ViewModels/TrojanLimitFlowDto.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Blog.Core.Model.ViewModels +{ + /// + /// 限制流量dto + /// 作者:胡丁文 + /// 时间:2020-4-27 16:57:07 + /// + public class TrojanLimitFlowDto + { + /// + /// 用户 + /// + public int[] users { get; set; } + /// + /// 流量(-1为无限,单位为最小单位byte) + /// + public Int64 quota { get; set; } + } +} diff --git a/Blog.Core.Model/ViewModels/TrojanServerDto.cs b/Blog.Core.Model/ViewModels/TrojanServerDto.cs new file mode 100644 index 0000000..89cb87e --- /dev/null +++ b/Blog.Core.Model/ViewModels/TrojanServerDto.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Blog.Core.Model.ViewModels +{ + public class TrojanServerDto + { + public string name { get; set; } + public string value { get; set; } + } +} diff --git a/Blog.Core.Model/ViewModels/TrojanServerSpliceDto.cs b/Blog.Core.Model/ViewModels/TrojanServerSpliceDto.cs new file mode 100644 index 0000000..e83adfd --- /dev/null +++ b/Blog.Core.Model/ViewModels/TrojanServerSpliceDto.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Blog.Core.Model.ViewModels +{ + /// + /// Trojan服务器拼接服务器和订阅地址 + /// + public class TrojanServerSpliceDto + { + /// + /// 普通订阅连接 + /// + public string normalApi { get; set; } + /// + /// clash订阅连接 + /// + public string clashApi { get; set; } + /// + /// 备用clash订阅连接 + /// + public string clashApiBackup { get; set; } + public List list { get; set; } = new List(); + } +} diff --git a/Blog.Core.Model/ViewModels/TrojanUseDetailDto.cs b/Blog.Core.Model/ViewModels/TrojanUseDetailDto.cs new file mode 100644 index 0000000..7746b10 --- /dev/null +++ b/Blog.Core.Model/ViewModels/TrojanUseDetailDto.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Blog.Core.Model.ViewModels +{ + /// + /// Trojan用户流量统计分组 + /// + public class TrojanUseDetailDto + { + /// + /// 用户ID + /// + public int userId { get; set; } + /// + /// 月度 + /// + public string moth { get; set; } + /// + /// 上传流量 + /// + public decimal up { get; set; } + /// + /// 下载流量 + /// + public decimal down { get; set; } + /// + /// 下载流量 + /// + public decimal total { get { return up + down; } } + } +} diff --git a/Blog.Core.Repository/BASE/BaseRepository.cs b/Blog.Core.Repository/BASE/BaseRepository.cs index 8290068..ead9f9a 100644 --- a/Blog.Core.Repository/BASE/BaseRepository.cs +++ b/Blog.Core.Repository/BASE/BaseRepository.cs @@ -144,6 +144,19 @@ namespace Blog.Core.Repository.Base //这种方式会以主键为条件 return await _db.Updateable(entity).ExecuteCommandHasChangeAsync(); } + /// + /// 更新实体数据 + /// + /// 博文实体类 + /// + public async Task Update(List entity) + { + ////这种方式会以主键为条件 + //var i = await Task.Run(() => _db.Updateable(entity).ExecuteCommand()); + //return i > 0; + //这种方式会以主键为条件 + return await _db.Updateable(entity).ExecuteCommandHasChangeAsync(); + } public async Task Update(TEntity entity, string where) { diff --git a/Blog.Core.Repository/BASE/IBaseRepository.cs b/Blog.Core.Repository/BASE/IBaseRepository.cs index 2978350..9b72a00 100644 --- a/Blog.Core.Repository/BASE/IBaseRepository.cs +++ b/Blog.Core.Repository/BASE/IBaseRepository.cs @@ -69,6 +69,12 @@ namespace Blog.Core.IRepository.Base /// /// Task Update(TEntity model); + /// + /// 更新model + /// + /// + /// + Task Update(List model); /// /// 根据model,更新,带where条件 diff --git a/Blog.Core.Services/BASE/BaseServices.cs b/Blog.Core.Services/BASE/BaseServices.cs index ec685be..232c6fa 100644 --- a/Blog.Core.Services/BASE/BaseServices.cs +++ b/Blog.Core.Services/BASE/BaseServices.cs @@ -76,6 +76,15 @@ namespace Blog.Core.Services.BASE { return await BaseDal.Update(entity); } + /// + /// 更新实体数据 + /// + /// 博文实体类 + /// + public async Task Update(List entity) + { + return await BaseDal.Update(entity); + } public async Task Update(TEntity entity, string where) { return await BaseDal.Update(entity, where); diff --git a/Blog.Core.Services/TrojanUsersServices.cs b/Blog.Core.Services/TrojanUsersServices.cs new file mode 100644 index 0000000..f42f51d --- /dev/null +++ b/Blog.Core.Services/TrojanUsersServices.cs @@ -0,0 +1,18 @@ +using Blog.Core.Common; +using Blog.Core.IRepository.Base; +using Blog.Core.IServices; +using Blog.Core.Model.Models; +using Blog.Core.Services.BASE; +using System.Linq; +using System.Threading.Tasks; + +namespace Blog.Core.Services +{ + /// + /// TrojanUsersServices + /// + public class TrojanUsersServices : BaseServices, ITrojanUsersServices + { + + } +} diff --git a/Blog.Core.Tasks/Blog.Core.Tasks.csproj b/Blog.Core.Tasks/Blog.Core.Tasks.csproj index 5e17355..bb748f6 100644 --- a/Blog.Core.Tasks/Blog.Core.Tasks.csproj +++ b/Blog.Core.Tasks/Blog.Core.Tasks.csproj @@ -11,6 +11,7 @@ + diff --git a/Blog.Core.Tasks/HostedService/Job1TimedService.cs b/Blog.Core.Tasks/HostedService/Job1TimedService.cs new file mode 100644 index 0000000..9777aff --- /dev/null +++ b/Blog.Core.Tasks/HostedService/Job1TimedService.cs @@ -0,0 +1,60 @@ +using Blog.Core.Common; +using Blog.Core.IServices; +using Microsoft.Extensions.Hosting; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Blog.Core.Tasks +{ + public class Job1TimedService : IHostedService, IDisposable + { + private Timer _timer; + private readonly IBlogArticleServices _blogArticleServices; + + // 这里可以注入 + public Job1TimedService(IBlogArticleServices blogArticleServices) + { + _blogArticleServices = blogArticleServices; + } + + public Task StartAsync(CancellationToken cancellationToken) + { + Console.WriteLine("Job 1 is starting."); + + _timer = new Timer(DoWork, null, TimeSpan.Zero, + TimeSpan.FromSeconds(60 * 60));//一个小时 + + return Task.CompletedTask; + } + + private void DoWork(object state) + { + try + { + var model = _blogArticleServices.GetBlogDetails(1).Result; + Console.WriteLine($"Job 1 启动成功,获取id=1的博客title为:{model?.btitle}"); + } + catch (Exception ex) + { + Console.WriteLine($"Error:{ex.Message}"); + } + + ConsoleHelper.WriteSuccessLine($"Job 1: {DateTime.Now}"); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + Console.WriteLine("Job 1 is stopping."); + + _timer?.Change(Timeout.Infinite, 0); + + return Task.CompletedTask; + } + + public void Dispose() + { + _timer?.Dispose(); + } + } +} diff --git a/Blog.Core.Tasks/HostedService/Job2TimedService.cs b/Blog.Core.Tasks/HostedService/Job2TimedService.cs new file mode 100644 index 0000000..ee7f2c5 --- /dev/null +++ b/Blog.Core.Tasks/HostedService/Job2TimedService.cs @@ -0,0 +1,47 @@ +using Blog.Core.Common; +using Microsoft.Extensions.Hosting; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Blog.Core.Tasks +{ + public class Job2TimedService : IHostedService, IDisposable + { + private Timer _timer; + + // 这里可以注入 + public Job2TimedService() + { + } + + public Task StartAsync(CancellationToken cancellationToken) + { + Console.WriteLine("Job 2 is starting."); + + _timer = new Timer(DoWork, null, TimeSpan.Zero, + TimeSpan.FromSeconds(60 * 60 * 2));//两个小时 + + return Task.CompletedTask; + } + + private void DoWork(object state) + { + ConsoleHelper.WriteWarningLine($"Job 2: {DateTime.Now}"); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + Console.WriteLine("Job 2 is stopping."); + + _timer?.Change(Timeout.Infinite, 0); + + return Task.CompletedTask; + } + + public void Dispose() + { + _timer?.Dispose(); + } + } +} diff --git a/Blog.Core.Tasks/QuartzNet/Jobs/Job_Trojan_Quartz.cs b/Blog.Core.Tasks/QuartzNet/Jobs/Job_Trojan_Quartz.cs new file mode 100644 index 0000000..f964074 --- /dev/null +++ b/Blog.Core.Tasks/QuartzNet/Jobs/Job_Trojan_Quartz.cs @@ -0,0 +1,79 @@ +using Blog.Core.Repository.UnitOfWorks; +using Blog.Core.IServices; +using Blog.Core.IServices.BASE; +using Blog.Core.Model.Models; +using Microsoft.Extensions.Logging; +using Quartz; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +/// +/// 这里要注意下,命名空间和程序集是一样的,不然反射不到(任务类要去JobSetup添加注入) +/// +namespace Blog.Core.Tasks +{ + public class Job_Trojan_Quartz : JobBase, IJob + { + private readonly IUnitOfWorkManage _unitOfWorkManage; + public IBaseServices_DetailServices; + private readonly ITrojanUsersServices _TrojanUsers; + private readonly ILogger _logger; + + public Job_Trojan_Quartz(IUnitOfWorkManage unitOfWorkManage, IBaseServices iusers_DetailServices, ITrojanUsersServices trojanUsers, ITasksQzServices tasksQzServices, ILogger logger) + { + _tasksQzServices = tasksQzServices; + _unitOfWorkManage = unitOfWorkManage; + _DetailServices = iusers_DetailServices; + _TrojanUsers = trojanUsers; + _logger = logger; + } + public async Task Execute(IJobExecutionContext context) + { + //var param = context.MergedJobDataMap; + // 可以直接获取 JobDetail 的值 + var jobKey = context.JobDetail.Key; + var jobId = jobKey.Name; + var executeLog = await ExecuteJob(context, async () => await Run(context, jobId.ObjToInt())); + + } + public async Task Run(IJobExecutionContext context, int jobid) + { + if (jobid > 0) + { + try + { + //获取每月用户的数据 + _unitOfWorkManage.BeginTran(); + var now = DateTime.Now.AddMonths(-1); + + var list = await _TrojanUsers.Query(); + List ls = new List(); + foreach (var us in list) + { + TrojanDetails u = new TrojanDetails(); + u.calDate = now; + u.userId = us.id; + u.download = us.download; + u.upload = us.upload; + //清零 + us.download = 0; + us.upload = 0; + ls.Add(u); + } + await _TrojanUsers.Update(list); + await _DetailServices.Add(ls); + _unitOfWorkManage.CommitTran(); + } + catch (Exception) + { + _unitOfWorkManage.RollbackTran(); + throw; + } + } + } + } + + + +} diff --git a/Blog.Core.Tasks/QuartzNet/Jobs/Job_URL_Quartz.cs b/Blog.Core.Tasks/QuartzNet/Jobs/Job_URL_Quartz.cs new file mode 100644 index 0000000..666a8d4 --- /dev/null +++ b/Blog.Core.Tasks/QuartzNet/Jobs/Job_URL_Quartz.cs @@ -0,0 +1,51 @@ +using Blog.Core.Common.Helper; +using Blog.Core.Repository.UnitOfWorks; +using Blog.Core.IServices; +using Blog.Core.IServices.BASE; +using Blog.Core.Model.Models; +using Microsoft.Extensions.Logging; +using Quartz; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +/// +/// 这里要注意下,命名空间和程序集是一样的,不然反射不到(任务类要去JobSetup添加注入) +/// +namespace Blog.Core.Tasks +{ + public class Job_URL_Quartz : JobBase, IJob + { + private readonly ILogger _logger; + + public Job_URL_Quartz(ITasksQzServices tasksQzServices, ILogger logger) + { + _tasksQzServices = tasksQzServices; + _logger = logger; + } + public async Task Execute(IJobExecutionContext context) + { + // 可以直接获取 JobDetail 的值 + var jobKey = context.JobDetail.Key; + var jobId = jobKey.Name; + var executeLog = await ExecuteJob(context, async () => await Run(context, jobId.ObjToInt())); + + } + public async Task Run(IJobExecutionContext context, int jobid) + { + if (jobid > 0) + { + JobDataMap data = context.JobDetail.JobDataMap; + string pars = data.GetString("JobParam"); + if (!string.IsNullOrWhiteSpace(pars)) + { + var log = await HttpHelper.GetAsync(pars); + _logger.LogInformation(log); + } + } + } + } + + + +}