diff --git a/WGShare.API.sln b/WGShare.API.sln
new file mode 100644
index 0000000..6fad96e
--- /dev/null
+++ b/WGShare.API.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.9.34723.18
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WGShare.API", "WGShare.API\WGShare.API.csproj", "{A5611B2B-6FFE-48BE-9AB5-EA846A47E37D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WGShare.Domain", "WGShare.Domain\WGShare.Domain.csproj", "{93A88636-4DAE-4832-A2F7-CAEB193017A3}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A5611B2B-6FFE-48BE-9AB5-EA846A47E37D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A5611B2B-6FFE-48BE-9AB5-EA846A47E37D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A5611B2B-6FFE-48BE-9AB5-EA846A47E37D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A5611B2B-6FFE-48BE-9AB5-EA846A47E37D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {93A88636-4DAE-4832-A2F7-CAEB193017A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {93A88636-4DAE-4832-A2F7-CAEB193017A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {93A88636-4DAE-4832-A2F7-CAEB193017A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {93A88636-4DAE-4832-A2F7-CAEB193017A3}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {CCBE5CF2-07F8-444A-9664-FA34CBB56143}
+ EndGlobalSection
+EndGlobal
diff --git a/WGShare.API/Controllers/AuthController.cs b/WGShare.API/Controllers/AuthController.cs
new file mode 100644
index 0000000..cf10d26
--- /dev/null
+++ b/WGShare.API/Controllers/AuthController.cs
@@ -0,0 +1,187 @@
+using Masuit.Tools;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
+using System.Configuration;
+using System.Security.Claims;
+using WGShare.API.Controllers.Basic;
+using WGShare.API.Helpers;
+using WGShare.Domain.DTOs.Login;
+using WGShare.Domain.Entities;
+using WGShare.Domain.FriendlyException;
+
+namespace WGShare.API.Controllers
+{
+ [Route("auth")]
+ public class AuthController : BasicController
+ {
+ private readonly ISqlSugarClient _sqlSugar;
+ private readonly JwtHelper _jwtHelper;
+
+ public AuthController(ISqlSugarClient sqlSugar, JwtHelper jwtHelper)
+ {
+ _sqlSugar = sqlSugar;
+ _jwtHelper = jwtHelper;
+ }
+
+ ///
+ /// 检查用户名
+ ///
+ ///
+ ///
+ [HttpGet("check-user"), AllowAnonymous]
+ public async Task CheckUser([FromQuery] string userName)
+ {
+ return await _sqlSugar.Queryable().AnyAsync(x => x.IsDelete == false && x.UserName == userName);
+ }
+
+ ///
+ /// 正常账号登录
+ ///
+ ///
+ [HttpPost("login"), AllowAnonymous]
+ public async Task Login([FromBody] UserLoginDTO loginDTO)
+ {
+ var user = await _sqlSugar.Queryable()
+ .FirstAsync(x => x.UserName == loginDTO.Account && x.IsDelete == false && x.Pwd == loginDTO.Pwd);
+ if (user == null)
+ {
+ throw Oops.Oh("用户名或密码不正确!");
+ }
+
+ var tenant = await _sqlSugar.Queryable().FirstAsync(x => x.Id == user.TenantId);
+ if (tenant == null || tenant.IsDelete == true)
+ {
+ throw Oops.Oh("该区域账号已停用,请联系管理员");
+ }
+
+ if (await _sqlSugar.Queryable().AnyAsync(x => x.IsDelete == true && x.Id == user.RoleId))
+ {
+ throw Oops.Oh("该角色账号已停用,请联系管理员");
+ }
+
+ var perms = await _sqlSugar.Queryable()
+ .InnerJoin((m, rm) => m.Id == rm.PermId)
+ .Where((m, rm) => rm.RoleId == user.RoleId)
+ .Distinct()
+ .ToListAsync();
+
+ var btnAutn = new List();
+ btnAutn.Add(new Claim("perm", perms.Sum(x => x.PermValue).ToString()));
+ btnAutn.Add(new Claim("role", user.RoleId));
+ btnAutn.Add(new Claim("tenant", user.TenantId));
+
+ return Ok(new
+ {
+ perms = perms.Sum(x => x.PermValue),
+ token = _jwtHelper.CreateToken(user.Id, btnAutn),
+ roleId = user.RoleId,
+ userName = user.UserName,
+ tenantName = tenant.TenantName
+ });
+ }
+
+ ///
+ /// 匿名登录,直接进入会议室
+ ///
+ ///
+ [HttpPost("anon-login")]
+ public async Task Login([FromBody] AnonymousLoginDTO loginDTO)
+ {
+
+ var room = await _sqlSugar.Queryable().FirstAsync(x => x.Id == loginDTO.RoomId);
+ if (room == null)
+ {
+ throw Oops.Oh("会议号无效");
+ }
+ var anonRoleId = "2";
+
+ // 匿名登录使用普通用户身份
+ var perms = await _sqlSugar.Queryable()
+ .InnerJoin((m, rm) => m.Id == rm.PermId)
+ .Where((m, rm) => rm.RoleId == anonRoleId)
+ .Distinct()
+ .ToListAsync();
+
+ var tenant = await _sqlSugar.Queryable().FirstAsync(x => x.Id == room.TenantId);
+ if (tenant == null || tenant.IsDelete == true)
+ {
+ throw Oops.Oh("该区域账号已停用,请联系管理员");
+ }
+
+
+ var btnAutn = new List();
+ btnAutn.Add(new Claim("perm", perms.Sum(x => x.PermValue).ToString()));
+ btnAutn.Add(new Claim("role", anonRoleId));
+ btnAutn.Add(new Claim("tenant", room.TenantId));
+ btnAutn.Add(new Claim("mac", loginDTO.Mac));
+ btnAutn.Add(new Claim("machine", loginDTO.MachineName));
+ btnAutn.Add(new Claim("nickName", loginDTO.NickName));
+
+ return Ok(new
+ {
+ perms = perms.Sum(x => x.PermValue),
+ token = _jwtHelper.CreateToken("0", btnAutn),
+ roleId = anonRoleId,
+ userName = loginDTO.NickName,
+ tenantName = tenant.TenantName
+ });
+ }
+
+ ///
+ /// 登出(暂未处理任何业务逻辑)
+ ///
+ ///
+ [HttpPost("logout")]
+ public async Task Logout()
+ {
+ return true;
+ }
+
+
+
+ #region 后台管理员登录接口
+ ///
+ /// 管理员登录
+ ///
+ ///
+ [HttpPost("admin/login"), AllowAnonymous]
+ public async Task LoginForAdmin([FromBody] UserLoginDTO loginDTO)
+ {
+ var adminClient = _sqlSugar.AsTenant().GetConnection("usercenter");
+
+ var user = await adminClient.Queryable()
+ .FirstAsync(x => x.Account == loginDTO.Account && x.Password == loginDTO.Pwd);
+ if (user == null)
+ {
+ throw Oops.Oh("用户名或密码不正确!");
+ }
+
+ return _jwtHelper.CreateToken(user.Id);
+ }
+
+ ///
+ /// 管理员信息
+ ///
+ ///
+ [HttpGet("admin/info")]
+ public async Task GetAdminInfo()
+ {
+ var adminClient = _sqlSugar.AsTenant().GetConnection("usercenter");
+
+ var user = await adminClient.Queryable()
+ .FirstAsync(x => x.Id == UId);
+ if (user == null)
+ {
+ throw Oops.Oh("管理员不存在!");
+ }
+
+ return Ok(new
+ {
+ name = user.Name,
+ id = user.Id,
+ });
+ }
+ #endregion
+ }
+}
diff --git a/WGShare.API/Controllers/Backend/PremController.cs b/WGShare.API/Controllers/Backend/PremController.cs
new file mode 100644
index 0000000..55cea1d
--- /dev/null
+++ b/WGShare.API/Controllers/Backend/PremController.cs
@@ -0,0 +1,93 @@
+using Mapster;
+using Masuit.Tools;
+using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
+using WGShare.API.Controllers.Basic;
+using WGShare.Domain.DTOs.Perm;
+using WGShare.Domain.DTOs.Role;
+using WGShare.Domain.Entities;
+using WGShare.Domain.FriendlyException;
+using Yitter.IdGenerator;
+
+namespace WGShare.API.Controllers.Backend
+{
+ [Route("be/perm")]
+ public class PremController : BasicController
+ {
+ private readonly ISqlSugarClient _sqlSugar;
+
+ public PremController(ISqlSugarClient sqlSugar)
+ {
+ _sqlSugar = sqlSugar;
+ }
+
+
+ [HttpGet("{id}")]
+ public async Task GetSingle([FromRoute] string id)
+ {
+ return await _sqlSugar.Queryable().FirstAsync(x => x.Id == id);
+ }
+
+ [HttpGet("all")]
+ public async Task> GetAll()
+ {
+ return await _sqlSugar.Queryable().ToListAsync();
+ }
+
+ [HttpPost]
+ public async Task Add([FromBody] PermissionInputDTO input)
+ {
+ var entity = input.Adapt();
+ if (await _sqlSugar.Queryable().AnyAsync(x => x.PermValue == input.PermValue))
+ {
+ throw Oops.Oh("权限值不能重复");
+ }
+ entity.Id = YitIdHelper.NextId().ToString();
+
+ return await _sqlSugar.Insertable(entity).ExecuteCommandAsync() > 0;
+ }
+
+ ///
+ /// 获取最新权限值
+ ///
+ ///
+ [HttpGet("vale")]
+ public async Task GetPermValue()
+ {
+ var maxPerm = await _sqlSugar.Queryable().MaxAsync(x => x.PermValue);
+ if (maxPerm == 0)
+ {
+ return 1;
+ }
+ return maxPerm << 1;
+ }
+
+ [HttpPut]
+ public async Task Modify([FromBody] PermissionInputDTO input)
+ {
+ var entity = input.Adapt();
+
+ if (await _sqlSugar.Queryable().AnyAsync(x => x.PermValue == input.PermValue && x.Id != input.Id))
+ {
+ throw Oops.Oh("权限值不能重复");
+ }
+
+ return await _sqlSugar.Updateable(entity).ExecuteCommandAsync() > 0;
+ }
+
+ [HttpDelete]
+ public async Task Delete([FromQuery] string id)
+ {
+ var roles = await _sqlSugar.Queryable()
+ .InnerJoin((r, rp) => r.Id == rp.RoleId)
+ .Where((r, rp) => rp.PermId == id)
+ .Select((r, rp) => r.RoleName).ToListAsync();
+ if (!roles.IsNullOrEmpty())
+ {
+ throw Oops.Oh($@"[{string.Join(',', roles)}]角色已使用,无法删除!");
+ }
+
+ return await _sqlSugar.Deleteable().Where(x => x.Id == id).ExecuteCommandHasChangeAsync();
+ }
+ }
+}
diff --git a/WGShare.API/Controllers/Backend/RoleController.cs b/WGShare.API/Controllers/Backend/RoleController.cs
new file mode 100644
index 0000000..24eea8a
--- /dev/null
+++ b/WGShare.API/Controllers/Backend/RoleController.cs
@@ -0,0 +1,86 @@
+using Mapster;
+using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
+using WGShare.API.Controllers.Basic;
+using WGShare.Domain.DTOs.Role;
+using WGShare.Domain.Entities;
+using Yitter.IdGenerator;
+
+namespace WGShare.API.Controllers.Backend
+{
+ [Route("be/role")]
+ public class RoleController : BasicController
+ {
+ private readonly ISqlSugarClient _sqlSugar;
+
+ public RoleController(ISqlSugarClient sqlSugar)
+ {
+ _sqlSugar = sqlSugar;
+ }
+
+
+ [HttpGet("{id}")]
+ public async Task GetSingle([FromRoute] string id)
+ {
+ return await _sqlSugar.Queryable().FirstAsync(x => x.Id == id);
+ }
+
+ [HttpGet("list")]
+ public async Task> GetPageList()
+ {
+ return await _sqlSugar.Queryable().ToListAsync();
+ }
+
+ [HttpPost]
+ public async Task Add([FromBody] RoleInputDTO inputDTO)
+ {
+ var entity = inputDTO.Adapt();
+ entity.Id = YitIdHelper.NextId().ToString();
+
+ return await _sqlSugar.Insertable(entity).ExecuteCommandAsync() > 0;
+ }
+
+ [HttpPut]
+ public async Task Modify([FromBody] RoleInputDTO inputDTO)
+ {
+ var entity = inputDTO.Adapt();
+
+ return await _sqlSugar.Updateable(entity)
+ .UpdateColumns(x => new { x.RoleName }).ExecuteCommandAsync() > 0;
+ }
+
+ [HttpDelete]
+ public async Task Delete([FromBody] params string[] ids)
+ {
+ return await _sqlSugar.Updateable()
+ .SetColumns(x => x.IsDelete == true)
+ .Where(x => ids.Contains(x.Id)).ExecuteCommandHasChangeAsync();
+ }
+
+ [HttpGet("dp-list")]
+ public async Task> GetDropDownList()
+ {
+ return await _sqlSugar.Queryable().Where(x => x.IsDelete == false).ToListAsync();
+ }
+
+ [HttpPost("auth-perm")]
+ public async Task PermAuth([FromQuery] string roleId, [FromBody] List inputDTO)
+ {
+ var entity = inputDTO.Adapt>();
+
+ await _sqlSugar.Deleteable()
+ .Where(x => x.RoleId == roleId).ExecuteCommandAsync();
+
+ return await _sqlSugar.Insertable(entity).ExecuteCommandAsync() > 0;
+ }
+
+ [HttpGet("auth-perm")]
+ public async Task> GetPermAuth([FromQuery] string roleId)
+ {
+ return await _sqlSugar.Queryable()
+ .InnerJoin((p, rp) => p.Id == rp.PermId)
+ .Where((p, rp) => rp.RoleId == roleId)
+ .ToListAsync();
+ }
+ }
+}
diff --git a/WGShare.API/Controllers/Backend/TenantController.cs b/WGShare.API/Controllers/Backend/TenantController.cs
new file mode 100644
index 0000000..d70dcd4
--- /dev/null
+++ b/WGShare.API/Controllers/Backend/TenantController.cs
@@ -0,0 +1,69 @@
+using Mapster;
+using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
+using WGShare.API.Controllers.Basic;
+using WGShare.Domain.DTOs.Tenant;
+using WGShare.Domain.DTOs.User;
+using WGShare.Domain.Entities;
+using WGShare.Domain.FriendlyException;
+using WGShare.Domain.GeneralModel;
+using Yitter.IdGenerator;
+
+namespace WGShare.API.Controllers.Backend
+{
+ [Route("be/tenant")]
+ public class TenantController : BasicController
+ {
+ private readonly ISqlSugarClient _sqlSugar;
+
+ public TenantController(ISqlSugarClient sqlSugar)
+ {
+ _sqlSugar = sqlSugar;
+ }
+
+
+ [HttpGet("{id}")]
+ public async Task GetSingle([FromRoute] string id)
+ {
+ return await _sqlSugar.Queryable().FirstAsync(x => x.Id == id);
+ }
+
+ [HttpGet("list")]
+ public async Task> GetList()
+ {
+ return await _sqlSugar.Queryable().ToListAsync();
+ }
+
+ [HttpPost]
+ public async Task Add([FromBody] TenantInputDTO inputDTO)
+ {
+ var entity = inputDTO.Adapt();
+ entity.Id = YitIdHelper.NextId().ToString();
+ return await _sqlSugar.Insertable(entity).ExecuteCommandAsync() > 0;
+ }
+
+ [HttpPut]
+ public async Task Modify([FromBody] TenantInputDTO inputDTO)
+ {
+ var entity = inputDTO.Adapt();
+
+
+ return await _sqlSugar.Updateable(entity)
+ .UpdateColumns(x => new { x.TenantName }).ExecuteCommandAsync() > 0;
+ }
+
+ [HttpGet]
+ public async Task Delete([FromQuery] string id, [FromQuery] bool enable)
+ {
+ return await _sqlSugar.Updateable()
+ .SetColumns(x => x.IsDelete == enable)
+ .Where(x => x.Id == id).ExecuteCommandHasChangeAsync();
+ }
+
+ [HttpGet("dp-list")]
+ public async Task> GetDropDownList()
+ {
+ return await _sqlSugar.Queryable().Where(x => x.IsDelete == false).ToListAsync();
+ }
+ }
+}
diff --git a/WGShare.API/Controllers/Backend/UserController.cs b/WGShare.API/Controllers/Backend/UserController.cs
new file mode 100644
index 0000000..60a12ad
--- /dev/null
+++ b/WGShare.API/Controllers/Backend/UserController.cs
@@ -0,0 +1,118 @@
+using Mapster;
+using Microsoft.AspNetCore.Components.Forms;
+using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
+using WGShare.API.Controllers.Basic;
+using WGShare.Domain.DTOs.User;
+using WGShare.Domain.Entities;
+using WGShare.Domain.FriendlyException;
+using WGShare.Domain.GeneralModel;
+using Yitter.IdGenerator;
+
+namespace WGShare.API.Controllers.Backend
+{
+ [Route("be/user")]
+ public class UserController : BasicController
+ {
+ private readonly ISqlSugarClient _sqlSugar;
+
+ public UserController(ISqlSugarClient sqlSugar)
+ {
+ _sqlSugar = sqlSugar;
+ }
+
+
+ [HttpGet("{id}")]
+ public async Task GetSingle([FromRoute] string id)
+ {
+ return await _sqlSugar.Queryable().FirstAsync(x => x.Id == id);
+ }
+
+ [HttpGet("list")]
+ public async Task> GetPageList([FromQuery] UserSearchDTO searchDTO)
+ {
+ RefAsync total = 0;
+
+ var list = await _sqlSugar.Queryable()
+ .InnerJoin((u, r) => u.RoleId == r.Id)
+ .InnerJoin((u, r, t) => u.TenantId == t.Id)
+ .WhereIF(!string.IsNullOrWhiteSpace(searchDTO.keyword),
+ u => u.UserName.Contains(searchDTO.keyword) || u.Account.Contains(searchDTO.keyword) || u.Id.Contains(searchDTO.keyword))
+ .WhereIF(!string.IsNullOrWhiteSpace(searchDTO.TenantId), u => u.TenantId == searchDTO.TenantId)
+ .WhereIF(!string.IsNullOrWhiteSpace(searchDTO.RoleId), u => u.RoleId == searchDTO.RoleId)
+ //.Where(u => u.IsDelete == false)
+ .Select((u, r, t) => new User
+ {
+ Id = u.Id.SelectAll(),
+ RoleName = r.RoleName,
+ TenantName = t.TenantName
+ })
+ .ToPageListAsync(searchDTO.PageIndex, searchDTO.PageSize, total);
+
+ return PagedResult.Create(list, total.Value);
+ }
+
+ [HttpPost]
+ public async Task Add([FromBody] UserInputDTO userInput)
+ {
+ var entity = userInput.Adapt();
+ entity.Id = YitIdHelper.NextId().ToString();
+ if (await _sqlSugar.Queryable().AnyAsync(x => x.Account == entity.Account))
+ {
+ throw Oops.Oh("账号已存在!");
+ }
+
+ return await _sqlSugar.Insertable(entity).ExecuteCommandAsync() > 0;
+ }
+
+ [HttpPut]
+ public async Task Modify([FromBody] UserInputDTO inputDTO)
+ {
+ var entity = inputDTO.Adapt();
+
+ if (await _sqlSugar.Queryable().AnyAsync(x => x.Account == entity.Account && x.Id != inputDTO.Id))
+ {
+ throw Oops.Oh("账号已存在!");
+ }
+
+ return await _sqlSugar.Updateable(entity)
+ .IgnoreColumns(x => new { x.Pwd }).ExecuteCommandAsync() > 0;
+ }
+
+ [HttpPut("pwd")]
+ public async Task ModifyPassword([FromBody] UserChangePwdDTO inputDTO)
+ {
+ var entity = inputDTO.Adapt();
+
+ return await _sqlSugar.Updateable(entity)
+ .UpdateColumns(x => new { x.Pwd }).ExecuteCommandAsync() > 0;
+ }
+
+ [HttpDelete]
+ public async Task Delete([FromBody] params string[] ids)
+ {
+ return await _sqlSugar.Updateable()
+ .SetColumns(x => x.IsDelete == true)
+ .Where(x => ids.Contains(x.Id)).ExecuteCommandHasChangeAsync();
+ }
+
+ ///
+ /// 权限修改
+ ///
+ ///
+ [HttpPut("auth-prem")]
+ [Obsolete]
+ public async Task EditPremissions([FromBody] List inputDTOs)
+ {
+
+ var entity = inputDTOs.Adapt();
+
+
+ await _sqlSugar.Deleteable()
+ .Where(x => inputDTOs.Select(x => x.UserId).Contains(x.UserId)).ExecuteCommandAsync();
+
+
+ await _sqlSugar.Insertable(entity).ExecuteCommandAsync();
+ }
+ }
+}
diff --git a/WGShare.API/Controllers/Basic/BasicController.cs b/WGShare.API/Controllers/Basic/BasicController.cs
new file mode 100644
index 0000000..776fa73
--- /dev/null
+++ b/WGShare.API/Controllers/Basic/BasicController.cs
@@ -0,0 +1,51 @@
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using SqlSugar.Extensions;
+using WGShare.Domain.FriendlyException;
+
+namespace WGShare.API.Controllers.Basic
+{
+ [ApiController]
+ [Authorize]
+ public class BasicController : ControllerBase
+ {
+ public string UId
+ {
+ get
+ {
+ var uid = HttpContext.User.Claims.FirstOrDefault(x => x.Type == "uid").Value;
+ if (string.IsNullOrWhiteSpace(uid))
+ {
+ throw Oops.Oh("用户信息有误,请重新登录");
+ }
+ return uid;
+ }
+ }
+
+ public string RoleId
+ {
+ get
+ {
+ var roleId = HttpContext.User.Claims.FirstOrDefault(x => x.Type == "role").Value;
+ if (string.IsNullOrWhiteSpace(roleId))
+ {
+ throw Oops.Oh("用户信息有误,请重新登录");
+ }
+ return roleId;
+ }
+ }
+
+ public string TenantId
+ {
+ get
+ {
+ var tenant = HttpContext.User.Claims.FirstOrDefault(x => x.Type == "tenant").Value;
+ if (string.IsNullOrWhiteSpace(tenant))
+ {
+ throw Oops.Oh("用户信息有误,请重新登录");
+ }
+ return tenant;
+ }
+ }
+ }
+}
diff --git a/WGShare.API/Controllers/Frontend/HomeController.cs b/WGShare.API/Controllers/Frontend/HomeController.cs
new file mode 100644
index 0000000..7078f5b
--- /dev/null
+++ b/WGShare.API/Controllers/Frontend/HomeController.cs
@@ -0,0 +1,66 @@
+using Mapster;
+using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
+using WGShare.API.Controllers.Basic;
+using WGShare.Domain.DTOs.Room;
+using WGShare.Domain.Entities;
+using WGShare.Domain.FriendlyException;
+using WGShare.Domain.GeneralModel;
+using Yitter.IdGenerator;
+
+namespace WGShare.API.Controllers.Frontend
+{
+ ///
+ /// 首页接口
+ ///
+ [Route("home")]
+ public class HomeController : BasicController
+ {
+ private readonly ISqlSugarClient _sqlSugar;
+
+ public HomeController(ISqlSugarClient sqlSugar)
+ {
+ _sqlSugar = sqlSugar;
+ }
+
+
+
+ ///
+ /// 获取会议室列表
+ ///
+ ///
+ ///
+ [HttpGet("room")]
+ public async Task> GetRooms(PagedBaseDto dto)
+ {
+ RefAsync total = 0;
+
+ var list = await _sqlSugar.Queryable()
+ .Where(x => x.TenantId == TenantId && x.IsDelete == false)
+ .ToPageListAsync(dto.PageIndex, dto.PageSize, total);
+
+ return PagedResult.Create(list.Adapt>(), total.Value);
+ }
+
+ ///
+ /// 创建会议室
+ ///
+ ///
+ ///
+ [HttpPost("room")]
+ public async Task CreateRoom([FromBody] RoomInputDTO inputDTO)
+ {
+ var entity = inputDTO.Adapt();
+ entity.Id = YitIdHelper.NextId().ToString();
+ entity.TenantId = TenantId;
+
+ if (await _sqlSugar.Queryable().AnyAsync(x => x.RoomNum == inputDTO.RoomNum))
+ {
+ throw Oops.Oh("无效会议号,请重新输入");
+ }
+
+ return await _sqlSugar.Insertable(entity).ExecuteCommandAsync() > 0;
+ }
+
+ }
+}
diff --git a/WGShare.API/Controllers/Frontend/RoomController.cs b/WGShare.API/Controllers/Frontend/RoomController.cs
new file mode 100644
index 0000000..19d9116
--- /dev/null
+++ b/WGShare.API/Controllers/Frontend/RoomController.cs
@@ -0,0 +1,163 @@
+using Mapster;
+using Masuit.Tools;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
+using System.CodeDom;
+using WGShare.API.Controllers.Basic;
+using WGShare.Domain.DTOs.File;
+using WGShare.Domain.DTOs.Room;
+using WGShare.Domain.DTOs.User;
+using WGShare.Domain.Entities;
+using WGShare.Domain.FriendlyException;
+using WGShare.Domain.GeneralModel;
+using Yitter.IdGenerator;
+using ZstdSharp.Unsafe;
+
+namespace WGShare.API.Controllers.Frontend
+{
+ ///
+ /// 会议室接口
+ ///
+ [Route("room")]
+ public class RoomController : BasicController
+ {
+ private readonly ISqlSugarClient _sqlSugar;
+
+ public RoomController(ISqlSugarClient sqlSugar)
+ {
+ this._sqlSugar = sqlSugar;
+ }
+
+ ///
+ /// 获取会议室管理员
+ ///
+ ///
+ [HttpGet("manager")]
+ public async Task> GetRoomManager([FromQuery] string roomId)
+ {
+ return await _sqlSugar.Queryable()
+ .Where(x => x.RoomId == roomId)
+ .Select(x => x.UserId)
+ .ToListAsync();
+ }
+
+ ///
+ /// 设置房间管理员
+ ///
+ ///
+ [HttpPost("manager")]
+ public async Task SetRoomManager([FromQuery] string roomId, [FromBody] List userIds)
+ {
+ var entities = userIds.ConvertAll(x => new RoomManager
+ {
+ RoomId = roomId,
+ UserId = x
+ });
+
+ return await _sqlSugar.Insertable(entities).ExecuteCommandAsync() > 0;
+ }
+
+ ///
+ /// 查询用户信息
+ ///
+ ///
+ [HttpGet("user")]
+ public async Task> GetUsers([FromQuery] string uidStr)
+ {
+ var uid = uidStr.Split(',', StringSplitOptions.RemoveEmptyEntries);
+ if (uid.IsNullOrEmpty())
+ {
+ throw Oops.Oh("请输入需要查询的用户id,多id请用英文逗号,分割");
+ }
+
+ var users = await _sqlSugar.Queryable()
+ .Where(x => uid.Contains(x.Id))
+ .ToListAsync();
+
+ return users.Adapt>();
+ }
+
+ ///
+ /// 检验房间是否存在
+ ///
+ ///
+ [HttpGet("checkout"), AllowAnonymous]
+ public async Task ExistsRoom([FromQuery] string roomNum)
+ {
+ return await _sqlSugar.Queryable().AnyAsync(x => x.RoomNum == roomNum && x.IsDelete == false);
+ }
+
+ ///
+ /// 获取单个会议室信息
+ ///
+ ///
+ [HttpGet("{roomNum}")]
+ public async Task GetRoom([FromRoute] string roomNum)
+ {
+ var room = await _sqlSugar.Queryable().FirstAsync(x => x.RoomNum == roomNum && x.IsDelete == false);
+ if (room == null)
+ {
+ throw Oops.Oh("会议号无效");
+ }
+ return room.Adapt();
+ }
+
+ ///
+ /// 分享上传文件
+ ///
+ ///
+ ///
+ [HttpPost("file")]
+ public async Task AddFile([FromBody] ShareFileInputDTO inputDTO)
+ {
+ var entity = inputDTO.Adapt();
+ entity.Id = YitIdHelper.NextId().ToString();
+ entity.UserId = UId;
+
+ return await _sqlSugar.Insertable(entity).ExecuteCommandAsync() > 0;
+ }
+
+ ///
+ /// 删除文件
+ ///
+ ///
+ [HttpDelete("file")]
+ public async Task DeleteFile([FromBody] List ids)
+ {
+ return await _sqlSugar.Updateable()
+ .SetColumns(x => x.IsDelete == true)
+ .Where(x => ids.Contains(x.Id))
+ .ExecuteCommandHasChangeAsync();
+ }
+
+ ///
+ /// 获取分享文件列表
+ ///
+ ///
+ [HttpGet("file")]
+ public async Task> GetFilesList([FromQuery] string roomId, [FromBody] PagedBaseDto pagedBaseDto)
+ {
+ RefAsync total = 0;
+ var list = await _sqlSugar.Queryable()
+ .LeftJoin((sf, u) => sf.UserId == u.Id)
+ .Where(x => x.IsDelete == false && x.RoomId == roomId)
+ .Select((sf, u) => new ShareFileOutputDTO
+ {
+ Id = sf.Id,
+ UserId = u.Id,
+ UserName = u.UserName,
+ FileName = sf.FileName,
+ FileUrl = sf.FileUrl,
+ RoomId = u.Id,
+ Size = sf.Size,
+ ModifyTime = sf.ModifyTime,
+ DownloadCount = sf.DownloadCount,
+ }).ToPageListAsync(pagedBaseDto.PageIndex, pagedBaseDto.PageSize, total);
+
+ return PagedResult.Create(list, total.Value);
+ }
+
+
+ }
+}
diff --git a/WGShare.API/Helpers/JwtHelper.cs b/WGShare.API/Helpers/JwtHelper.cs
new file mode 100644
index 0000000..6565f2a
--- /dev/null
+++ b/WGShare.API/Helpers/JwtHelper.cs
@@ -0,0 +1,53 @@
+using Masuit.Tools;
+using Microsoft.IdentityModel.Tokens;
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+using System.Text;
+
+namespace WGShare.API.Helpers
+{
+ public class JwtHelper
+ {
+ private readonly IConfiguration _configuration;
+
+ public JwtHelper(IConfiguration configuration)
+ {
+ _configuration = configuration;
+ }
+
+ public string CreateToken(string uid, List claims = null)
+ {
+ if (claims.IsNullOrEmpty())
+ claims = new();
+ claims.AddRange(new List
+ {
+ new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
+ new Claim("uid", uid),
+ });
+
+ // 2. 从 appsettings.json 中读取SecretKey
+ var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecretKey"]));
+
+ // 3. 选择加密算法
+ var algorithm = SecurityAlgorithms.HmacSha256;
+
+ // 4. 生成Credentials
+ var signingCredentials = new SigningCredentials(secretKey, algorithm);
+
+ // 5. 根据以上,生成token
+ var jwtSecurityToken = new JwtSecurityToken(
+ _configuration["Jwt:Issuer"], //Issuer
+ _configuration["Jwt:Audience"], //Audience
+ claims, //Claims,
+ DateTime.Now, //notBefore
+ DateTime.Now.AddSeconds(_configuration["Jwt:Expires"].ToDouble()), //expires
+ signingCredentials //Credentials
+ );
+
+ // 6. 将token变为string
+ var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
+
+ return token;
+ }
+ }
+}
diff --git a/WGShare.API/Helpers/RedisHelper.cs b/WGShare.API/Helpers/RedisHelper.cs
new file mode 100644
index 0000000..dc2fa57
--- /dev/null
+++ b/WGShare.API/Helpers/RedisHelper.cs
@@ -0,0 +1,47 @@
+using FreeRedis;
+
+namespace WGShare.API.Helpers
+{
+ ///
+ /// redis静态访问类
+ ///
+ public abstract class RedisHelper
+ {
+ private static RedisClient _instance;
+
+ ///
+ /// redis实例
+ ///
+ public static RedisClient Instance
+ {
+ get
+ {
+ if (_instance == null)
+ {
+ throw new Exception("使用前初始化redis静态访问类 RedisHelper.Initialization(new FreeRedis.RedisClient(\"127.0.0.1:6379,password=123,defaultDatabase=13,maxpoolsize=50,prefix=key前辍\"));");
+ }
+
+ return _instance;
+ }
+ }
+
+
+ ///
+ /// 初始化redis静态访问类 RedisHelper.Initialization(new FreeRedis.RedisClient(\"127.0.0.1:6379,password=123,defaultDatabase=13,maxpoolsize=50,prefix=key前辍\"))
+ ///
+ ///
+ internal static void Initialization(FreeRedis.RedisClient redisClient)
+ {
+ _instance = redisClient;
+ }
+
+ internal static ThreadLocal rnd = new ThreadLocal(() => new Random());
+ ///
+ /// 随机秒(防止所有key同一时间过期,雪崩)
+ ///
+ /// 最小秒数
+ /// 最大秒数
+ ///
+ public static int RandomExpired(int minTimeoutSeconds, int maxTimeoutSeconds) => rnd.Value.Next(minTimeoutSeconds, maxTimeoutSeconds);
+ }
+}
diff --git a/WGShare.API/Program.cs b/WGShare.API/Program.cs
new file mode 100644
index 0000000..eca2d6a
--- /dev/null
+++ b/WGShare.API/Program.cs
@@ -0,0 +1,81 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using Newtonsoft.Json.Serialization;
+using SqlSugar;
+using WGShare.API.Helpers;
+using WGShare.API.ServiceConfigs;
+using Yitter.IdGenerator;
+
+namespace WGShare.API
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ var builder = WebApplication.CreateBuilder(args);
+ var configuration = builder.Configuration;
+
+ // Add services to the container.
+
+ builder.Services.AddControllers(options =>
+ {
+ // ȫ쳣ڴ д try catch
+ options.Filters.Add();
+ // ȫģֵĬֵ ͳһظʽ
+ options.Filters.Add();
+ }).AddNewtonsoftJson(options =>
+ {
+ //ƵлʽĸСдշʽ
+ options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
+ //Ĭϸʽ ʽ1
+ options.SerializerSettings.Converters.Add(new IsoDateTimeConverter() { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" });
+ //ѭ
+ options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
+ //ֵ
+ //options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
+ });
+ // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
+ builder.Services.AddEndpointsApiExplorer();
+ builder.Services.AddSwaggerGen();
+ builder.Services.AddSingleton(new JwtHelper(configuration));
+ builder.Services.AddAuth(configuration["Jwt:Issuer"],
+ configuration["Jwt:Audience"],
+ configuration["Jwt:SecretKey"]);
+ builder.Services.AddSqlsugar(builder.Environment.EnvironmentName, new ConnectionConfig
+ {
+ DbType = DbType.MySql,
+ ConfigId = "metting",
+ ConnectionString = configuration.GetConnectionString("metting"),
+ IsAutoCloseConnection = true
+ },
+ new ConnectionConfig
+ {
+ DbType = DbType.MySql,
+ ConfigId = "usercenter",
+ ConnectionString = configuration.GetConnectionString("usercenter"),
+ IsAutoCloseConnection = true
+ });
+ YitIdHelper.SetIdGenerator(new IdGeneratorOptions(1));
+ RedisHelper.Initialization(new FreeRedis.RedisClient(configuration["Redis:master"]));
+
+ var app = builder.Build();
+
+ // Configure the HTTP request pipeline.
+ if (app.Environment.IsDevelopment())
+ {
+ app.UseSwagger();
+ app.UseSwaggerUI();
+ app.UseCustomCors();
+ }
+
+ //мUseAuthentication֤Ҫ֤мǰã UseAuthorizationȨ
+ app.UseAuthentication();
+ app.UseAuthorization();
+
+
+ app.MapControllers();
+
+ app.Run();
+ }
+ }
+}
diff --git a/WGShare.API/Properties/launchSettings.json b/WGShare.API/Properties/launchSettings.json
new file mode 100644
index 0000000..bb54335
--- /dev/null
+++ b/WGShare.API/Properties/launchSettings.json
@@ -0,0 +1,31 @@
+{
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:42628",
+ "sslPort": 0
+ }
+ },
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": false,
+ "launchUrl": "swagger",
+ "applicationUrl": "http://localhost:5192",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/WGShare.API/ServiceConfigs/AuthenticationServiceExtensions.cs b/WGShare.API/ServiceConfigs/AuthenticationServiceExtensions.cs
new file mode 100644
index 0000000..1066293
--- /dev/null
+++ b/WGShare.API/ServiceConfigs/AuthenticationServiceExtensions.cs
@@ -0,0 +1,52 @@
+using Mapster;
+using MapsterMapper;
+using Newtonsoft.Json.Converters;
+using Newtonsoft.Json.Serialization;
+using Newtonsoft.Json;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.IdentityModel.Tokens;
+using System.Text;
+using System.Reflection.Metadata;
+
+namespace WGShare.API.ServiceConfigs
+{
+ public static class AuthenticationServiceExtensions
+ {
+ ///
+ /// 添加认证和授权
+ ///
+ /// 服务集合
+ ///
+ public static IServiceCollection AddAuth(this IServiceCollection services, string issuer, string audience, string secretKey)
+ {
+ services.AddAuthentication(options =>
+ {
+ options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
+ }).AddJwtBearer(options =>
+ {
+ options.TokenValidationParameters = new TokenValidationParameters()
+ {
+ ValidateIssuer = true, //是否验证Issuer
+ ValidIssuer = issuer, //发行人Issuer
+ ValidateAudience = true, //是否验证Audience
+ ValidAudience = audience, //订阅人Audience
+ ValidateIssuerSigningKey = true, //是否验证SecurityKey
+ IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey)), //SecurityKey
+ ValidateLifetime = true, //是否验证失效时间
+ ClockSkew = TimeSpan.FromSeconds(30), //过期时间容错值,解决服务器端时间不同步问题(秒)
+ RequireExpirationTime = true,
+ };
+ });
+
+
+ return services;
+ //services.AddAuthorization(options =>
+ //{
+ // options.AddPolicy(Constant.Policy.FreePolicyName,
+ // policy => policy.RequireClaim(Constant.Auth.PermissionsKey, Constant.Auth.FreeClaimValue, Constant.Auth.VipClaimValue));
+ // options.AddPolicy(Constant.Policy.VipPolicyName,
+ // policy => policy.RequireClaim(Constant.Auth.PermissionsKey, Constant.Auth.VipClaimValue));
+ //});
+ }
+ }
+}
diff --git a/WGShare.API/ServiceConfigs/CorsAppBuilderExtensions.cs b/WGShare.API/ServiceConfigs/CorsAppBuilderExtensions.cs
new file mode 100644
index 0000000..b30e0a5
--- /dev/null
+++ b/WGShare.API/ServiceConfigs/CorsAppBuilderExtensions.cs
@@ -0,0 +1,30 @@
+using Microsoft.AspNetCore.Builder;
+
+namespace WGShare.API.ServiceConfigs
+{
+ ///
+ /// 跨域配置扩展服务
+ ///
+ public static class CorsAppBuilderExtensions
+ {
+ ///
+ /// 允许所有跨域请求 (属于基础服务,若已调用 UseBasicServices 则无需调用)
+ ///
+ ///
+ ///
+ public static IApplicationBuilder UseCustomCors(this IApplicationBuilder app)
+ {
+ //// 获取配置文件中的允许跨域的地址
+ //var hosts = AppSetting.AllowedHosts.Split("|", StringSplitOptions.RemoveEmptyEntries);
+ app.UseCors(options =>
+ {
+ options.WithOrigins("*") // 允许跨域请求的地址
+ .AllowAnyHeader() // 允许的请求标头
+ .AllowAnyMethod(); // 允许跨域请求的类型 (GET,POST等)
+
+ });
+
+ return app;
+ }
+ }
+}
diff --git a/WGShare.API/ServiceConfigs/GlobalExceptionFilter.cs b/WGShare.API/ServiceConfigs/GlobalExceptionFilter.cs
new file mode 100644
index 0000000..8701751
--- /dev/null
+++ b/WGShare.API/ServiceConfigs/GlobalExceptionFilter.cs
@@ -0,0 +1,68 @@
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.AspNetCore.Mvc;
+using WGShare.Domain.GeneralModel;
+using Masuit.Tools;
+using WGShare.Domain.FriendlyException.Exceptions;
+using WGShare.Domain.FriendlyException;
+
+namespace WGShare.API.ServiceConfigs
+{
+ ///
+ /// 全局异常捕获
+ ///
+ public class GlobalExceptionFilter : IAsyncExceptionFilter
+ {
+ private readonly ILogger _logger;
+
+ public GlobalExceptionFilter(ILogger logger)
+ {
+ _logger = logger; //在构造函数中注入日志处理实例
+ }
+
+ public async Task OnExceptionAsync(ExceptionContext context)
+ {
+ // 如果异常没有被处理则进行处理
+ if (context.ExceptionHandled == false)
+ {
+ // 定义返回类型
+ UniformResult