diff --git a/README.md b/README.md index 3912ff6f..2537318f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -

Yi框架

+

Yi框架

一套以用户体验出发的.Net8 Web开源框架

支持Abp.vNext 版本原生版本、Furion版本,前端后台接入Ruoyi Vue3.0

集大成者,终究轮子

diff --git a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/Microsoft/Extensions/DependencyInjection/SwaggerAddExtensions.cs b/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/Microsoft/Extensions/DependencyInjection/SwaggerAddExtensions.cs index 8afd6878..dc7c5b99 100644 --- a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/Microsoft/Extensions/DependencyInjection/SwaggerAddExtensions.cs +++ b/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/Microsoft/Extensions/DependencyInjection/SwaggerAddExtensions.cs @@ -1,7 +1,11 @@ -using System.Diagnostics; +using System.ComponentModel; +using System.Diagnostics; +using System.Text; +using System.Xml.Linq; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; using Volo.Abp.AspNetCore.Mvc; @@ -75,12 +79,59 @@ namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection { [scheme] = new string[0] }); + + + options.SchemaFilter(); } ); - + return services; } } + + + /// + /// Swagger文档枚举字段显示枚举属性和枚举值,以及枚举描述 + /// + public class EnumSchemaFilter : ISchemaFilter + { + /// + /// 实现接口 + /// + /// + /// + + public void Apply(OpenApiSchema model, SchemaFilterContext context) + { + if (context.Type.IsEnum) + { + model.Enum.Clear(); + model.Type = "string"; + model.Format = null; + + + StringBuilder stringBuilder = new StringBuilder(); + Enum.GetNames(context.Type) + .ToList() + .ForEach(name => + { + Enum e = (Enum)Enum.Parse(context.Type, name); + var descrptionOrNull = GetEnumDescription(e); + model.Enum.Add(new OpenApiString(name)); + stringBuilder.Append($"【枚举:{name}{(descrptionOrNull is null ? string.Empty : $"({descrptionOrNull})")}={Convert.ToInt64(Enum.Parse(context.Type, name))}】
"); + }); + model.Description= stringBuilder.ToString(); + } + } + + private static string? GetEnumDescription(Enum value) + { + var fieldInfo = value.GetType().GetField(value.ToString()); + var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false); + return attributes.Length > 0 ? attributes[0].Description : null; + } + + } } diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Article/ArticleImprotDto.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Article/ArticleImprotDto.cs index cb078ddd..98e8ba3d 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Article/ArticleImprotDto.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Article/ArticleImprotDto.cs @@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text; using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; using Yi.Framework.Bbs.Domain.Shared.Enums; namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Article @@ -16,6 +17,8 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Article [Required] public Guid DiscussId { get; set; } + public Guid ArticleParentId { get; set; }= Guid.Empty; + public ArticleImportTypeEnum ImportType { get; set; } = ArticleImportTypeEnum.Defalut; - } +} } diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application/Services/ArticleService.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application/Services/ArticleService.cs index 5c9c245b..57de3e2b 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application/Services/ArticleService.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application/Services/ArticleService.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Text; using Mapster; using Microsoft.AspNetCore.Authorization; @@ -143,7 +144,7 @@ namespace Yi.Framework.Bbs.Application.Services /// 导入文章 /// /// - public async Task PostImportAsync(ArticleImprotDto input, [FromForm] IFormFileCollection file) + public async Task PostImportAsync([FromQuery] ArticleImprotDto input, [FromForm][Required] IFormFileCollection file) { var fileObjs = new List(); if (file.Count > 0) @@ -161,14 +162,18 @@ namespace Yi.Framework.Bbs.Application.Services // 将字节转换成字符串 var content = Encoding.UTF8.GetString(bytes); - fileObjs.Add(new FileObject() { FileName=item.FileName,Content=content}); + fileObjs.Add(new FileObject() { FileName = item.FileName, Content = content }); } } } } } + else + { + throw new UserFriendlyException("未选择文件"); + } //使用简单工厂根据传入的类型进行判断 - await _forumManager.PostImportAsync(input.DiscussId, fileObjs, input.ImportType); + await _forumManager.PostImportAsync(input.DiscussId, input.ArticleParentId, fileObjs, input.ImportType); } diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain.Shared/Enums/ArticleImportTypeEnum.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain.Shared/Enums/ArticleImportTypeEnum.cs index 6c6f2ac3..01686b76 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain.Shared/Enums/ArticleImportTypeEnum.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain.Shared/Enums/ArticleImportTypeEnum.cs @@ -1,17 +1,16 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.ComponentModel; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace Yi.Framework.Bbs.Domain.Shared.Enums { + [JsonConverter(typeof(StringEnumConverter))] public enum ArticleImportTypeEnum { - //默认导入方式 + [Description("默认导入方式")] Defalut, - //vuePresss方式 + [Description("vuePresss方式")] VuePress } } diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/ArticleImport/AbstractArticleImport.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/ArticleImport/AbstractArticleImport.cs new file mode 100644 index 00000000..c39b5dec --- /dev/null +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/ArticleImport/AbstractArticleImport.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Yi.Framework.Bbs.Domain.Entities; +using Yi.Framework.Bbs.Domain.Shared.Model; + +namespace Yi.Framework.Bbs.Domain.Managers.ArticleImport +{ + public abstract class AbstractArticleImport + { + public virtual List Import(Guid discussId,Guid articleParentId, List fileObjs) + { + var articles = Convert(fileObjs); + articles.ForEach(article => + { + article.DiscussId = discussId; + article.ParentId = articleParentId; + }); + return articles; + } + public abstract List Convert(List fileObjs); + } +} diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/ArticleImport/DefaultArticleImport.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/ArticleImport/DefaultArticleImport.cs new file mode 100644 index 00000000..8ca58332 --- /dev/null +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/ArticleImport/DefaultArticleImport.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Yi.Framework.Bbs.Domain.Entities; +using Yi.Framework.Bbs.Domain.Shared.Model; + +namespace Yi.Framework.Bbs.Domain.Managers.ArticleImport +{ + internal class DefaultArticleImport : AbstractArticleImport + { + public override List Convert(List fileObjs) + { + return fileObjs.OrderBy(x => x.FileName).Select(x => new ArticleEntity { Name = x.FileName, Content = x.Content }).ToList(); + } + } +} diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/ArticleImport/VuePressArticleImport.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/ArticleImport/VuePressArticleImport.cs new file mode 100644 index 00000000..6ebb0626 --- /dev/null +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/ArticleImport/VuePressArticleImport.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Yi.Framework.Bbs.Domain.Entities; +using Yi.Framework.Bbs.Domain.Shared.Model; + +namespace Yi.Framework.Bbs.Domain.Managers.ArticleImport +{ + internal class VuePressArticleImport : AbstractArticleImport + { + public override List Convert(List fileObjs) + { + //排序及处理目录名称 + var fileNameHandler = fileObjs.OrderBy(x => x.FileName).Select(x => + { + var f = new FileObject { Content = x.Content }; + + //除去数字 + f.FileName = x.FileName.Split('.')[1]; + return f; + }); + + + //处理内容 + var fileContentHandler= fileNameHandler.Select(x => + { + var f = new FileObject { FileName = x.FileName }; + var lines = x.Content.SplitToLines(); + + var num = 0; + var startIndex = 0; + for (int i = 0; i < lines.Length; i++) + { + if (lines[i] == "---") + { + num++; + if (num == 2) + { + startIndex = i; + + break; + } + + } + + } + var linesRef = lines.ToList(); + + linesRef.RemoveRange(0, startIndex+1); + var result = string.Join(Environment.NewLine, linesRef); + f.Content = result; + return f; + }); + + var output = fileContentHandler.Select(x => new ArticleEntity() { Content = x.Content, Name = x.FileName }).ToList(); + + return output; + } + } +} diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/ForumManager.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/ForumManager.cs index 43e82011..7ffd1225 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/ForumManager.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/ForumManager.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Mvc; using Volo.Abp.Domain.Services; using Yi.Framework.Bbs.Domain.Entities; +using Yi.Framework.Bbs.Domain.Managers.ArticleImport; using Yi.Framework.Bbs.Domain.Shared.Enums; using Yi.Framework.Bbs.Domain.Shared.Model; using Yi.Framework.SqlSugarCore.Abstractions; @@ -16,11 +17,13 @@ namespace Yi.Framework.Bbs.Domain.Managers public readonly ISqlSugarRepository _discussRepository; public readonly ISqlSugarRepository _plateEntityRepository; public readonly ISqlSugarRepository _commentRepository; - public ForumManager(ISqlSugarRepository discussRepository, ISqlSugarRepository plateEntityRepository, ISqlSugarRepository commentRepository) + public readonly ISqlSugarRepository _articleRepository; + public ForumManager(ISqlSugarRepository discussRepository, ISqlSugarRepository plateEntityRepository, ISqlSugarRepository commentRepository, ISqlSugarRepository articleRepository) { _discussRepository = discussRepository; _plateEntityRepository = plateEntityRepository; _commentRepository = commentRepository; + _articleRepository = articleRepository; } //主题是不能直接创建的,需要由领域服务统一创建 @@ -45,11 +48,30 @@ namespace Yi.Framework.Bbs.Domain.Managers /// 导入文章 /// /// + /// /// /// /// - public async Task PostImportAsync(Guid discussId, List fileObjs, ArticleImportTypeEnum importType) + public async Task PostImportAsync(Guid discussId,Guid articleParentId, List fileObjs, ArticleImportTypeEnum importType) { + AbstractArticleImport abstractArticleImport = default; + switch (importType) + { + case ArticleImportTypeEnum.Defalut: + abstractArticleImport = new DefaultArticleImport(); + + break; + case ArticleImportTypeEnum.VuePress: + abstractArticleImport = new VuePressArticleImport(); + break; + + default: abstractArticleImport = new DefaultArticleImport(); break; + } + + var articleHandled = abstractArticleImport.Import(discussId, articleParentId, fileObjs); + + //await _articleRepository.InsertManyAsync(articleHandled); + } } } diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/AccountService.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/AccountService.cs index 83675077..f6c9462c 100644 --- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/AccountService.cs +++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/AccountService.cs @@ -248,6 +248,17 @@ namespace Yi.Framework.Rbac.Application.Services throw new UserFriendlyException("验证码错误"); } + private void ValidateUserName(RegisterDto input) + { + // 正则表达式,匹配只包含数字和字母的字符串 + string pattern = @"^[a-zA-Z0-9]+$"; + + bool isMatch = Regex.IsMatch(input.UserName, pattern); + if (!isMatch) + { + throw new UserFriendlyException("用户名不能包含除【字母】与【数字】的其他字符"); + } + } /// /// 注册,需要验证码通过 @@ -276,6 +287,10 @@ namespace Yi.Framework.Rbac.Application.Services { throw new UserFriendlyException("密码需大于等于6位!"); } + + //效验用户名 + ValidateUserName(input); + //效验验证码,根据电话号码获取 value,比对验证码已经uuid await ValidationPhoneCaptchaAsync(input); @@ -283,7 +298,7 @@ namespace Yi.Framework.Rbac.Application.Services //输入的用户名与电话号码都不能在数据库中存在 UserEntity user = new(); - var isExist = await _userRepository.IsAnyAsync(x =>x.UserName == input.UserName|| x.Phone == input.Phone); + var isExist = await _userRepository.IsAnyAsync(x => x.UserName == input.UserName || x.Phone == input.Phone); if (isExist) { throw new UserFriendlyException("用户已存在,注册失败"); diff --git a/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs b/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs index 83fdecfb..13dc515b 100644 --- a/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs +++ b/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs @@ -1,9 +1,10 @@ using System.Text; +using System.Text.Json.Serialization; using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Cors; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; +using Newtonsoft.Json.Converters; using Volo.Abp; using Volo.Abp.AspNetCore.Authentication.JwtBearer; using Volo.Abp.AspNetCore.Mvc; @@ -65,6 +66,7 @@ namespace Yi.Abp.Web service.AddControllers().AddNewtonsoftJson(options => { options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; + options.SerializerSettings.Converters.Add(new StringEnumConverter()); }); Configure(options => diff --git a/Yi.Abp.Net8/src/Yi.Abp.Web/wwwroot/logo.png b/Yi.Abp.Net8/src/Yi.Abp.Web/wwwroot/logo.png new file mode 100644 index 00000000..226b005a Binary files /dev/null and b/Yi.Abp.Net8/src/Yi.Abp.Web/wwwroot/logo.png differ diff --git a/readme/1.png b/readme/1.png index 476268b4..901b0c1d 100644 Binary files a/readme/1.png and b/readme/1.png differ diff --git a/readme/1696760969217.jpg b/readme/1696760969217.jpg index fad52ebe..ebef5032 100644 Binary files a/readme/1696760969217.jpg and b/readme/1696760969217.jpg differ diff --git a/readme/1696761014270.jpg b/readme/1696761014270.jpg index 29e28e93..21b9db40 100644 Binary files a/readme/1696761014270.jpg and b/readme/1696761014270.jpg differ diff --git a/readme/2.png b/readme/2.png index 12a7c7ad..e3885a42 100644 Binary files a/readme/2.png and b/readme/2.png differ diff --git a/readme/3.png b/readme/3.png index 85ac88f2..60733fb2 100644 Binary files a/readme/3.png and b/readme/3.png differ diff --git a/readme/4.png b/readme/4.png index d7ac081f..1213c1e7 100644 Binary files a/readme/4.png and b/readme/4.png differ diff --git a/readme/5.png b/readme/5.png index ea25ace3..bdfea170 100644 Binary files a/readme/5.png and b/readme/5.png differ diff --git a/readme/6.png b/readme/6.png index c67669d6..481b493d 100644 Binary files a/readme/6.png and b/readme/6.png differ diff --git a/readme/7.png b/readme/7.png index 984c1297..3084d2c6 100644 Binary files a/readme/7.png and b/readme/7.png differ diff --git a/readme/8.png b/readme/8.png index 10788d56..3e602e31 100644 Binary files a/readme/8.png and b/readme/8.png differ diff --git a/readme/9.png b/readme/9.png index bb6b1df9..37bb1f9d 100644 Binary files a/readme/9.png and b/readme/9.png differ