修复 token无法校验问题
This commit is contained in:
parent
7075ec039d
commit
658b75e3ad
|
|
@ -17,7 +17,7 @@ namespace Learn.Archives.API.Controllers
|
|||
this.baseService = baseService;
|
||||
}
|
||||
/// <summary>
|
||||
/// 后台管理员登录
|
||||
/// 管理员登录
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
|
|
@ -36,7 +36,7 @@ namespace Learn.Archives.API.Controllers
|
|||
Oh.Error("登录失败,用户不存在!");
|
||||
if (!admin!.Enable)
|
||||
Oh.Error("登录失败,用户已锁定!");
|
||||
if (admin.Password != model.Password)
|
||||
if (admin.Password != model.Password.GetMD5())
|
||||
Oh.Error("登录失败,密码错误");
|
||||
// 获取租户信息
|
||||
|
||||
|
|
@ -48,5 +48,7 @@ namespace Learn.Archives.API.Controllers
|
|||
new Claim(ClaimEnum.Name, admin.Name),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
using Learn.Archives.Core.Model;
|
||||
|
||||
namespace Learn.Archives.API.Controllers.Dto
|
||||
{
|
||||
/// <summary>
|
||||
/// 菜单树
|
||||
/// </summary>
|
||||
public class MenuTree : Menu
|
||||
{
|
||||
/// <summary>
|
||||
/// 子菜单列表
|
||||
/// </summary>
|
||||
public MenuTree[] Children { get; set; } = Array.Empty<MenuTree>();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
using Learn.Archives.API.Controllers.Dto;
|
||||
using Learn.Archives.API.Expand;
|
||||
using Learn.Archives.Core.Common;
|
||||
using Learn.Archives.Core.Model;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Diagnostics;
|
||||
using System.Security.Claims;
|
||||
using UserCenter.Model;
|
||||
|
||||
namespace Learn.Archives.API.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// 路由菜单
|
||||
/// </summary>
|
||||
public class MenuController : BackController<Menu>
|
||||
{
|
||||
readonly Repository<Menu> baseService;
|
||||
readonly Repository<MenuRelation> menuRelationDB;
|
||||
readonly LiveUserInfo userInfo;
|
||||
public MenuController(Repository<Menu> baseService, LiveUserInfo userInfo, Repository<MenuRelation> menuRelationDB) : base(baseService)
|
||||
{
|
||||
this.baseService = baseService;
|
||||
this.userInfo = userInfo;
|
||||
this.menuRelationDB = menuRelationDB;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 管理员菜单
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public async Task<MenuTree[]> AdminMenu()
|
||||
{
|
||||
var rId = userInfo.RoleId;
|
||||
if (rId == 0) Oh.Error("登录了无效的用户");
|
||||
var menuArr = await menuRelationDB.AsQueryable()
|
||||
.LeftJoin<Menu>((mr, m) => mr.MenuId == m.Id)
|
||||
.Where((mr,m)=> mr.RoleId == userInfo.RoleId)
|
||||
.Select((mr, m) => m)
|
||||
.ToArrayAsync();
|
||||
return GetChildren(menuArr, menuArr.First().Id);
|
||||
}
|
||||
/// <summary>
|
||||
/// 递归获取子菜单
|
||||
/// </summary>
|
||||
[NonAction]
|
||||
private static MenuTree[] GetChildren(IEnumerable<Menu> menus, long parentId)
|
||||
{
|
||||
return menus
|
||||
.Where(m => m.ParentId == parentId)
|
||||
.OrderBy(m => m.Rank)
|
||||
.Select(m => new MenuTree
|
||||
{
|
||||
Id = m.Id,
|
||||
Name = m.Name,
|
||||
Title = m.Title,
|
||||
Path = m.Path,
|
||||
IsButton = m.IsButton,
|
||||
Icon = m.Icon,
|
||||
Auths = m.Auths,
|
||||
Rank = m.Rank,
|
||||
ShowLink = m.ShowLink,
|
||||
ParentId = m.ParentId,
|
||||
Children = GetChildren(menus, m.Id)
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ using UserCenter.Model.Interface;
|
|||
namespace Learn.Archives.API.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
public class BaseController : ControllerBase
|
||||
public class _BaseController : ControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ namespace Learn.Archives.API.Controllers
|
|||
/// </summary>
|
||||
[Authorize(AuthenticationSchemes = Authentication.Admin)]
|
||||
[Route("api/[controller]/[action]")]
|
||||
public abstract class BackBaseController : BaseController
|
||||
public abstract class BackBaseController : _BaseController
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -108,9 +108,7 @@ namespace Learn.Archives.API.Controllers
|
|||
[NonAction]
|
||||
public virtual ISugarQueryable<T> BaseQuery(QueryDto model)
|
||||
{
|
||||
List<IConditionalModel> where = new List<IConditionalModel>();
|
||||
foreach (var item in model.Conditions)
|
||||
where.Add(item);
|
||||
List<IConditionalModel> where = [.. model.Conditions];
|
||||
var d = _baseRepository.AsQueryable()
|
||||
.Where(where);
|
||||
if ((typeof(T)).GetProperty("DeleteState") != null)
|
||||
|
|
@ -143,11 +141,7 @@ namespace Learn.Archives.API.Controllers
|
|||
{
|
||||
if (string.IsNullOrEmpty(model.ValueName) || string.IsNullOrEmpty(model.TextName))
|
||||
Oh.ModelError("ValueName TextName 是必填项");
|
||||
List<IConditionalModel> where = [.. model.Conditions];
|
||||
var sqlquery = _baseRepository.AsQueryable().Where(where).Where("DeleteState=0");
|
||||
|
||||
if (!string.IsNullOrEmpty(model.OrderBy))
|
||||
sqlquery = sqlquery.OrderByPropertyName(model.OrderBy, model.OrderByType);
|
||||
var sqlquery = BaseQuery(model);
|
||||
var res = await sqlquery.Select<ComboModel>($"{model.TextName} as Text , {model.ValueName} as Value").ToListAsync();
|
||||
return res;
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ using System.Text;
|
|||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Learn.Archives.Core.Model.Dto;
|
||||
using Aliyun.OSS;
|
||||
using NetTaste;
|
||||
|
||||
namespace Learn.Archives.API.Expand
|
||||
{
|
||||
|
|
@ -18,6 +19,7 @@ namespace Learn.Archives.API.Expand
|
|||
.AddJwtBearer(Authentication.Admin, options =>
|
||||
{
|
||||
options.RequireHttpsMetadata = false;
|
||||
options.UseSecurityTokenValidators = true;
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
SaveSigninToken = false,//保存token,后台验证token是否生效(重要)
|
||||
|
|
@ -30,6 +32,19 @@ namespace Learn.Archives.API.Expand
|
|||
};
|
||||
options.Events = new JwtBearerEvents
|
||||
{
|
||||
OnMessageReceived = context =>
|
||||
{
|
||||
var token = context.Request.Headers["Authorization"].FirstOrDefault();
|
||||
// 3. 安全提取令牌
|
||||
if (!string.IsNullOrEmpty(token) && token.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// 移除"Bearer "前缀并清除两端空格
|
||||
token = token.Substring("Bearer ".Length).Trim();
|
||||
context.Token = token;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
},
|
||||
OnAuthenticationFailed = context =>
|
||||
{
|
||||
context.Response.Clear();
|
||||
|
|
@ -41,11 +56,13 @@ namespace Learn.Archives.API.Expand
|
|||
},
|
||||
OnChallenge = context =>
|
||||
{
|
||||
if(context.Response.StatusCode == 403 || context.Response.StatusCode == 401)
|
||||
return Task.CompletedTask;
|
||||
context.HandleResponse();
|
||||
context.Response.Clear();
|
||||
context.Response.ContentType = "application/json";
|
||||
context.Response.StatusCode = 403;
|
||||
var data = new BaseReturn() { Code = 403, Message = context.Error + context.AuthenticateFailure?.Message };
|
||||
context.Response.StatusCode = 401;
|
||||
var data = new BaseReturn() { Code = 401, Message = context.Error + context.AuthenticateFailure?.Message };
|
||||
context.Response.WriteAsync(data.ToJson());
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,10 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.18" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.13.0" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.13.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.3" />
|
||||
<PackageReference Include="Mapster.DependencyInjection" Version="1.0.2-pre01" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -28,18 +28,7 @@ builder.Services.AddControllers(options =>
|
|||
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;// 默认小驼峰 null 大驼峰
|
||||
});
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen(c =>
|
||||
{
|
||||
c.SwaggerDoc("v1", new OpenApiInfo
|
||||
{
|
||||
Version = "v1",
|
||||
Description = "学校档案系统v1"
|
||||
});
|
||||
var file = Path.Combine(AppContext.BaseDirectory, "Learn.Archives.API.xml"); // xml文档绝对路径
|
||||
c.IncludeXmlComments(file, true); // true : 显示控制器层注释
|
||||
c.OrderActionsBy(o => o.RelativePath); // 对action的名称进行排序,如果有多个,就可以看见效果了。
|
||||
});
|
||||
|
||||
builder.Services.AddSwaggerExpand("学校档案系统");
|
||||
builder.Configuration.AddAppConfig(args);
|
||||
builder.Services.AddPermissionAuthentication();
|
||||
builder.Services.AddSqlSugarExpand();
|
||||
|
|
@ -54,12 +43,16 @@ builder.Services.AddHttpContextAccessor();
|
|||
|
||||
var app = builder.Build();
|
||||
|
||||
AppCommon.Services = app.Services;
|
||||
|
||||
app.UseMiddleware<BasicAuthMiddleware>("Swagger");
|
||||
|
||||
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
//自定义 应用
|
||||
app.UseCorsExpand();
|
||||
app.UseSqlSugarExpand();
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using System.Linq;
|
|||
using System.Reflection;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using System.Runtime.Loader;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
|
|
@ -70,12 +71,16 @@ namespace Learn.Archives.Core.Common
|
|||
public static class ExpandFunction
|
||||
{
|
||||
|
||||
static Dictionary<string, string> FormulaData;
|
||||
static string FormulaDataKey;
|
||||
/// <summary>
|
||||
/// 帧文件名称
|
||||
/// </summary>
|
||||
public static string FrameName = "frame_";
|
||||
|
||||
|
||||
public static string GetMD5(this string input)
|
||||
{
|
||||
using (MD5 md5 = MD5.Create())
|
||||
{
|
||||
byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(input));
|
||||
return Convert.ToHexString(hashBytes).ToUpper(); // 或者保留大写
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 对象转化为JSON字符串
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Learn.Archives.Core.Model;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using OracleInternal.Secure.Network;
|
||||
using SqlSugar.IOC;
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -12,6 +14,8 @@ namespace Learn.Archives.Core.Common
|
|||
private readonly RequestDelegate _next;
|
||||
private readonly string _realm;
|
||||
|
||||
private Repository<Admin> baseservice;
|
||||
|
||||
public BasicAuthMiddleware(RequestDelegate next, string realm)
|
||||
{
|
||||
_next = next;
|
||||
|
|
@ -20,7 +24,8 @@ namespace Learn.Archives.Core.Common
|
|||
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
if (context.Request.Path.StartsWithSegments("/swagger"))
|
||||
if (context.Request.Path.StartsWithSegments("/swagger")
|
||||
&& (context.Request.Path.Value?.Contains("swagger.json") ?? true))
|
||||
{
|
||||
string authHeader = context.Request.Headers["Authorization"];
|
||||
if (authHeader != null && authHeader.StartsWith("Basic "))
|
||||
|
|
@ -31,6 +36,7 @@ namespace Learn.Archives.Core.Common
|
|||
|
||||
if (await IsAuthorized(usernamePassword[0], usernamePassword[1]))
|
||||
{
|
||||
|
||||
await _next(context);
|
||||
return;
|
||||
}
|
||||
|
|
@ -45,9 +51,19 @@ namespace Learn.Archives.Core.Common
|
|||
|
||||
private async Task<bool> IsAuthorized(string username, string password)
|
||||
{
|
||||
// 在这里验证用户名和密码
|
||||
return AppCommon.Config.Admin.Account == username
|
||||
&& AppCommon.Config.Admin.Password == password;
|
||||
//if (baseservice == null)
|
||||
//{
|
||||
// using var scope = AppCommon.Services?.CreateScope();
|
||||
// if (scope != null)
|
||||
// baseservice = scope.ServiceProvider.GetService<Repository<Admin>>();
|
||||
//}
|
||||
//if (baseservice == null) return false;
|
||||
|
||||
var admin = await DbScoped.Sugar.Queryable<Admin>()
|
||||
.FirstAsync(x => x.Account == username);
|
||||
if (admin == null || !admin!.Enable) return false;
|
||||
else if (admin.Password != password.GetMD5()) return false;
|
||||
else return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Learn.Archives.Core.Common.Expand
|
||||
{
|
||||
public static class SwaggerExpand
|
||||
{
|
||||
public static void AddSwaggerExpand(this IServiceCollection s,string name="")
|
||||
{
|
||||
s.AddSwaggerGen(c =>
|
||||
{
|
||||
c.SwaggerDoc("v1", new OpenApiInfo
|
||||
{
|
||||
Version = "v1",
|
||||
Description = name
|
||||
});
|
||||
c.OperationFilter<SwaggerFileUploadFilter>();
|
||||
//按Http类型排序
|
||||
c.OrderActionsBy(o => o.GroupName);
|
||||
|
||||
c.DocInclusionPredicate((docName, apiDesc) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!apiDesc.TryGetMethodInfo(out MethodInfo methodInfo)) return false;
|
||||
var versions = methodInfo.DeclaringType.GetCustomAttributes(true)
|
||||
.OfType<ApiExplorerSettingsAttribute>().Select(attr => attr.GroupName);
|
||||
if (docName.ToLower() == "v1" && versions.FirstOrDefault() == null)
|
||||
return true;
|
||||
return versions.Any(v => v.ToString() == docName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
//添加全局安全性需求
|
||||
c.AddSecurityRequirement(new OpenApiSecurityRequirement{
|
||||
{
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new OpenApiReference
|
||||
{
|
||||
Type = ReferenceType.SecurityScheme,
|
||||
Id = "bearerAuth"
|
||||
}
|
||||
}, Array.Empty<string>()
|
||||
}
|
||||
});
|
||||
|
||||
//添加一个必须的全局安全信息,和AddSecurityDefinition方法指定的方案名称要一致,这里是Bearer。
|
||||
c.AddSecurityDefinition("bearerAuth",
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
Description = "使用JWT授权头。示例:\"Authorization: Bearer {token}\"",
|
||||
Name = "Authorization",
|
||||
In = ParameterLocation.Header,
|
||||
Type = SecuritySchemeType.Http,
|
||||
//内容为以 bearer开头
|
||||
Scheme = "bearer",
|
||||
BearerFormat = "JWT"
|
||||
});
|
||||
|
||||
DirectoryInfo dirs = new DirectoryInfo(AppContext.BaseDirectory);
|
||||
FileInfo[] files = dirs.GetFiles("*.xml");
|
||||
foreach (var path in files)
|
||||
{
|
||||
c.IncludeXmlComments(path.FullName);
|
||||
}
|
||||
});
|
||||
//s.AddSwaggerGenNewtonsoftSupport();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SwaggerFileUploadFilter : IOperationFilter
|
||||
{
|
||||
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
||||
{
|
||||
if (context.ApiDescription.ActionDescriptor.Parameters.Any(w => w.ParameterType == typeof(IFormCollection)))
|
||||
{
|
||||
Dictionary<string, OpenApiSchema> schema = new Dictionary<string, OpenApiSchema>();
|
||||
schema["fileName"] = new OpenApiSchema { Description = "选择上传文件", Type = "string", Format = "binary" };
|
||||
Dictionary<string, OpenApiMediaType> content = new Dictionary<string, OpenApiMediaType>();
|
||||
content["multipart/form-data"] = new OpenApiMediaType { Schema = new OpenApiSchema { Type = "object", Properties = schema } };
|
||||
operation.RequestBody = new OpenApiRequestBody() { Content = content };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ namespace Learn.Archives.Core.Common
|
|||
//JWT ID 唯一标识符
|
||||
new Claim(JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString() ),
|
||||
//发布时间戳 issued timestamp
|
||||
new Claim(JwtRegisteredClaimNames.Iat, now.ToString()),
|
||||
new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64),
|
||||
};
|
||||
if (claims != null && claims.Length > 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ namespace Learn.Archives.Core.Common
|
|||
/// <summary>
|
||||
/// 管理员角色id
|
||||
/// </summary>
|
||||
public string? RoleId
|
||||
public long? RoleId
|
||||
{
|
||||
get => _httpContextAccessor.HttpContext?.User.FindFirst(ClaimEnum.Role)?.Value;
|
||||
get => long.Parse(_httpContextAccessor.HttpContext?.User.FindFirst(ClaimEnum.Role)?.Value ?? "0");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ using System.Threading.Channels;
|
|||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using UserCenter.Model.Enum;
|
||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||
|
||||
namespace Learn.Archives.Core.Common
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.7" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
<PackageReference Include="SqlSugar.IOC" Version="2.0.0" />
|
||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.170" />
|
||||
<PackageReference Include="UserCenter.Model" Version="1.3.9" />
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ namespace Learn.Archives.Core.Model
|
|||
/// <summary>
|
||||
/// 密码
|
||||
/// </summary>
|
||||
[SugarColumn(Length = 12)]
|
||||
[SugarColumn(Length = 32)]
|
||||
public string Password { get; set; }
|
||||
/// <summary>
|
||||
/// 启用
|
||||
|
|
|
|||
|
|
@ -19,15 +19,16 @@ namespace Learn.Archives.Core.Model
|
|||
/// 名称
|
||||
/// </summary>
|
||||
[SugarColumn(Length = 50)]
|
||||
public required string Name { get; set; }
|
||||
public string Name { get; set; }
|
||||
/// <summary>
|
||||
/// 标题
|
||||
/// </summary>
|
||||
[SugarColumn(Length = 20)]
|
||||
public required string Title { get; set; }
|
||||
public string Title { get; set; }
|
||||
/// <summary>
|
||||
/// 路径
|
||||
/// </summary>
|
||||
[SugarColumn(IsNullable = true)]
|
||||
public string? Path { get; set; }
|
||||
/// <summary>
|
||||
/// 是按钮权限
|
||||
|
|
@ -36,10 +37,12 @@ namespace Learn.Archives.Core.Model
|
|||
/// <summary>
|
||||
/// 图标
|
||||
/// </summary>
|
||||
[SugarColumn(IsNullable = true)]
|
||||
public string? Icon { get; set; }
|
||||
/// <summary>
|
||||
/// 需要的授权码
|
||||
/// </summary>
|
||||
[SugarColumn(IsNullable = true)]
|
||||
public string? Auths { get; set; }
|
||||
/// <summary>
|
||||
/// 排名
|
||||
|
|
@ -51,5 +54,12 @@ namespace Learn.Archives.Core.Model
|
|||
/// </summary>
|
||||
public bool ShowLink { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 父级菜单ID
|
||||
/// <para>属于<see cref="Menu.Id"/></para>
|
||||
/// </summary>
|
||||
[SugarColumn(IsNullable = true)]
|
||||
public long ParentId { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ namespace Learn.Archives.Core.Model
|
|||
/// <summary>
|
||||
/// 菜单id
|
||||
/// </summary>
|
||||
public required long MenuId { get; set; }
|
||||
public long MenuId { get; set; }
|
||||
/// <summary>
|
||||
/// 管理员id
|
||||
/// 角色id
|
||||
/// </summary>
|
||||
public required long AdminId { get; set; }
|
||||
public long RoleId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
|
|
|
|||
Loading…
Reference in New Issue