chore:目录重构
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Module.DictionaryManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 常量定义
|
||||
/// </summary>
|
||||
|
||||
public class DictionaryConst
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using Furion.DependencyInjection;
|
||||
using Furion.DynamicApiController;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SqlSugar;
|
||||
using Yi.Framework.Infrastructure.Ddd.Dtos;
|
||||
using Yi.Framework.Infrastructure.Ddd.Services;
|
||||
using Yi.Framework.Module.DictionaryManager.Dtos.Dictionary;
|
||||
using Yi.Framework.Module.DictionaryManager.Entities;
|
||||
|
||||
namespace Yi.Framework.Module.DictionaryManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Dictionary服务实现
|
||||
/// </summary>
|
||||
public class DictionaryService : CrudAppService<DictionaryEntity, DictionaryGetOutputDto, DictionaryGetListOutputDto, long, DictionaryGetListInputVo, DictionaryCreateInputVo, DictionaryUpdateInputVo>,
|
||||
IDictionaryService,IDynamicApiController,ITransient
|
||||
{
|
||||
/// <summary>
|
||||
/// 查询
|
||||
/// </summary>
|
||||
|
||||
public override async Task<PagedResultDto<DictionaryGetListOutputDto>> GetListAsync(DictionaryGetListInputVo input)
|
||||
{
|
||||
RefAsync<int> total = 0;
|
||||
var entities = await _DbQueryable.WhereIF(input.DictType is not null, x => x.DictType == input.DictType)
|
||||
.WhereIF(input.DictLabel is not null, x => x.DictLabel!.Contains(input.DictLabel!))
|
||||
.WhereIF(input.State is not null, x => x.State == input.State)
|
||||
.ToPageListAsync(input.PageNum, input.PageSize, total);
|
||||
return new PagedResultDto<DictionaryGetListOutputDto>
|
||||
{
|
||||
Total = total,
|
||||
Items = await MapToGetListOutputDtosAsync(entities)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 根据字典类型获取字典列表
|
||||
/// </summary>
|
||||
/// <param name="dicType"></param>
|
||||
/// <returns></returns>
|
||||
[Route("/api/dictionary/dic-type/{dicType}")]
|
||||
public async Task<List<DictionaryGetListOutputDto>> GetDicType([FromRoute] string dicType)
|
||||
{
|
||||
var entities = await _repository.GetListAsync(u => u.DictType == dicType && u.State == true);
|
||||
var result = await MapToGetListOutputDtosAsync(entities);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Module.DictionaryManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 常量定义
|
||||
/// </summary>
|
||||
|
||||
public class DictionaryTypeConst
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using Furion.DependencyInjection;
|
||||
using Furion.DynamicApiController;
|
||||
using SqlSugar;
|
||||
using Yi.Framework.Infrastructure.Ddd.Dtos;
|
||||
using Yi.Framework.Infrastructure.Ddd.Services;
|
||||
using Yi.Framework.Module.DictionaryManager.Dtos.DictionaryType;
|
||||
using Yi.Framework.Module.DictionaryManager.Entities;
|
||||
|
||||
namespace Yi.Framework.Module.DictionaryManager
|
||||
{
|
||||
/// <summary>
|
||||
/// DictionaryType服务实现
|
||||
/// </summary>
|
||||
public class DictionaryTypeService : CrudAppService<DictionaryTypeEntity, DictionaryTypeGetOutputDto, DictionaryTypeGetListOutputDto, long, DictionaryTypeGetListInputVo, DictionaryTypeCreateInputVo, DictionaryTypeUpdateInputVo>,
|
||||
IDictionaryTypeService, IDynamicApiController, ITransient
|
||||
{
|
||||
|
||||
|
||||
public async override Task<PagedResultDto<DictionaryTypeGetListOutputDto>> GetListAsync(DictionaryTypeGetListInputVo input)
|
||||
{
|
||||
|
||||
RefAsync<int> total = 0;
|
||||
var entities = await _DbQueryable.WhereIF(input.DictName is not null, x => x.DictName.Contains(input.DictName!))
|
||||
.WhereIF(input.DictType is not null, x => x.DictType!.Contains(input.DictType!))
|
||||
.WhereIF(input.State is not null, x => x.State == input.State)
|
||||
.WhereIF(input.StartTime is not null && input.EndTime is not null, x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime)
|
||||
.ToPageListAsync(input.PageNum, input.PageSize, total);
|
||||
|
||||
return new PagedResultDto<DictionaryTypeGetListOutputDto>
|
||||
{
|
||||
Total = total,
|
||||
Items = await MapToGetListOutputDtosAsync(entities)
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Module.DictionaryManager.Dtos.Dictionary
|
||||
{
|
||||
/// <summary>
|
||||
/// Dictionary输入创建对象
|
||||
/// </summary>
|
||||
public class DictionaryCreateInputVo
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public DateTime CreationTime { get; set; } = DateTime.Now;
|
||||
public long? CreatorId { get; set; }
|
||||
public string? Remark { get; set; }
|
||||
public string? ListClass { get; set; }
|
||||
public string? CssClass { get; set; }
|
||||
public string DictType { get; set; } = string.Empty;
|
||||
public string? DictLabel { get; set; }
|
||||
public string DictValue { get; set; } = string.Empty;
|
||||
public bool IsDefault { get; set; }
|
||||
|
||||
public bool State { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Yi.Framework.Infrastructure.Ddd.Dtos;
|
||||
|
||||
namespace Yi.Framework.Module.DictionaryManager.Dtos.Dictionary
|
||||
{
|
||||
public class DictionaryGetListInputVo : PagedAndSortedResultRequestDto
|
||||
{
|
||||
public string? DictType { get; set; }
|
||||
public string? DictLabel { get; set; }
|
||||
public bool? State { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract;
|
||||
|
||||
namespace Yi.Framework.Module.DictionaryManager.Dtos.Dictionary
|
||||
{
|
||||
public class DictionaryGetListOutputDto : IEntityDto<long>
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public DateTime CreationTime { get; set; } = DateTime.Now;
|
||||
public long? CreatorId { get; set; }
|
||||
public string? Remark { get; set; }
|
||||
public string? ListClass { get; set; }
|
||||
public string? CssClass { get; set; }
|
||||
public string DictType { get; set; } = string.Empty;
|
||||
public string? DictLabel { get; set; }
|
||||
public string DictValue { get; set; } = string.Empty;
|
||||
public bool IsDefault { get; set; }
|
||||
public bool State { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract;
|
||||
|
||||
namespace Yi.Framework.Module.DictionaryManager.Dtos.Dictionary
|
||||
{
|
||||
public class DictionaryGetOutputDto : IEntityDto<long>
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public DateTime CreationTime { get; set; } = DateTime.Now;
|
||||
public long? CreatorId { get; set; }
|
||||
public string? Remark { get; set; }
|
||||
public string? ListClass { get; set; }
|
||||
public string? CssClass { get; set; }
|
||||
public string DictType { get; set; } = string.Empty;
|
||||
public string? DictLabel { get; set; }
|
||||
public string DictValue { get; set; } = string.Empty;
|
||||
public bool IsDefault { get; set; }
|
||||
|
||||
public bool State { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Module.DictionaryManager.Dtos.Dictionary
|
||||
{
|
||||
public class DictionaryUpdateInputVo
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public DateTime CreationTime { get; set; } = DateTime.Now;
|
||||
public long? CreatorId { get; set; }
|
||||
public string? Remark { get; set; }
|
||||
public string? ListClass { get; set; }
|
||||
public string? CssClass { get; set; }
|
||||
public string DictType { get; set; } = string.Empty;
|
||||
public string? DictLabel { get; set; }
|
||||
public string DictValue { get; set; } = string.Empty;
|
||||
public bool IsDefault { get; set; }
|
||||
|
||||
public bool State { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Module.DictionaryManager.Dtos.DictionaryType
|
||||
{
|
||||
/// <summary>
|
||||
/// DictionaryType输入创建对象
|
||||
/// </summary>
|
||||
public class DictionaryTypeCreateInputVo
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public DateTime CreationTime { get; set; } = DateTime.Now;
|
||||
public long? CreatorId { get; set; }
|
||||
public string DictName { get; set; } = string.Empty;
|
||||
public string DictType { get; set; } = string.Empty;
|
||||
public string? Remark { get; set; }
|
||||
|
||||
public bool State { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Yi.Framework.Infrastructure.Ddd.Dtos;
|
||||
|
||||
namespace Yi.Framework.Module.DictionaryManager.Dtos.DictionaryType
|
||||
{
|
||||
public class DictionaryTypeGetListInputVo : PagedAllResultRequestDto
|
||||
{
|
||||
public string? DictName { get; set; }
|
||||
public string? DictType { get; set; }
|
||||
public string? Remark { get; set; }
|
||||
|
||||
public bool? State { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract;
|
||||
|
||||
namespace Yi.Framework.Module.DictionaryManager.Dtos.DictionaryType
|
||||
{
|
||||
public class DictionaryTypeGetListOutputDto : IEntityDto<long>
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public DateTime CreationTime { get; set; } = DateTime.Now;
|
||||
public long? CreatorId { get; set; }
|
||||
public string DictName { get; set; } = string.Empty;
|
||||
public string DictType { get; set; } = string.Empty;
|
||||
public string? Remark { get; set; }
|
||||
|
||||
public bool State { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract;
|
||||
|
||||
namespace Yi.Framework.Module.DictionaryManager.Dtos.DictionaryType
|
||||
{
|
||||
public class DictionaryTypeGetOutputDto : IEntityDto<long>
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public DateTime CreationTime { get; set; } = DateTime.Now;
|
||||
public long? CreatorId { get; set; }
|
||||
public string DictName { get; set; } = string.Empty;
|
||||
public string DictType { get; set; } = string.Empty;
|
||||
public string? Remark { get; set; }
|
||||
|
||||
public bool State { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Yi.Framework.Module.DictionaryManager.Dtos.DictionaryType
|
||||
{
|
||||
public class DictionaryTypeUpdateInputVo
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public DateTime CreationTime { get; set; } = DateTime.Now;
|
||||
public long? CreatorId { get; set; }
|
||||
public string DictName { get; set; } = string.Empty;
|
||||
public string DictType { get; set; } = string.Empty;
|
||||
public string? Remark { get; set; }
|
||||
public bool State { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using SqlSugar;
|
||||
using Yi.Framework.Infrastructure.Data.Auditing;
|
||||
using Yi.Framework.Infrastructure.Data.Entities;
|
||||
using Yi.Framework.Infrastructure.Ddd.Entities;
|
||||
|
||||
namespace Yi.Framework.Module.DictionaryManager.Entities
|
||||
{
|
||||
[SugarTable("Dictionary")]
|
||||
public class DictionaryEntity : AuditedObject, IEntity<long>, ISoftDelete, IOrderNum, IState
|
||||
{
|
||||
/// <summary>
|
||||
/// 主键
|
||||
/// </summary>
|
||||
[SugarColumn(IsPrimaryKey = true)]
|
||||
public long Id { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 逻辑删除
|
||||
/// </summary>
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 排序
|
||||
/// </summary>
|
||||
public int OrderNum { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 状态
|
||||
/// </summary>
|
||||
public bool State { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 描述
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "Remark")]
|
||||
public string? Remark { get; set; }
|
||||
/// <summary>
|
||||
/// tag类型
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "ListClass")]
|
||||
public string? ListClass { get; set; }
|
||||
/// <summary>
|
||||
/// tagClass
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "CssClass")]
|
||||
public string? CssClass { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 字典类型
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "DictType")]
|
||||
public string DictType { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// 字典标签
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "DictLabel")]
|
||||
public string? DictLabel { get; set; }
|
||||
/// <summary>
|
||||
/// 字典值
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "DictValue")]
|
||||
public string DictValue { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// 是否为该类型的默认值
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "IsDefault")]
|
||||
public bool IsDefault { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using SqlSugar;
|
||||
using Yi.Framework.Infrastructure.Data.Auditing;
|
||||
using Yi.Framework.Infrastructure.Data.Entities;
|
||||
using Yi.Framework.Infrastructure.Ddd.Entities;
|
||||
|
||||
namespace Yi.Framework.Module.DictionaryManager.Entities
|
||||
{
|
||||
[SugarTable("DictionaryType")]
|
||||
public class DictionaryTypeEntity : AuditedObject, IEntity<long>, ISoftDelete, IOrderNum
|
||||
{
|
||||
/// <summary>
|
||||
/// 主键
|
||||
/// </summary>
|
||||
[SugarColumn(IsPrimaryKey = true)]
|
||||
public long Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 逻辑删除
|
||||
/// </summary>
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 排序
|
||||
/// </summary>
|
||||
public int OrderNum { get; set; } = 0;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 状态
|
||||
/// </summary>
|
||||
public bool? State { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 字典名称
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "DictName")]
|
||||
public string DictName { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// 字典类型
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "DictType")]
|
||||
public string DictType { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 描述
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "Remark")]
|
||||
public string? Remark { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Yi.Framework.Infrastructure.Ddd.Services.Abstract;
|
||||
using Yi.Framework.Module.DictionaryManager.Dtos.Dictionary;
|
||||
|
||||
namespace Yi.Framework.Module.DictionaryManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Dictionary服务抽象
|
||||
/// </summary>
|
||||
public interface IDictionaryService : ICrudAppService<DictionaryGetOutputDto, DictionaryGetListOutputDto, long, DictionaryGetListInputVo, DictionaryCreateInputVo, DictionaryUpdateInputVo>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Yi.Framework.Infrastructure.Ddd.Services.Abstract;
|
||||
using Yi.Framework.Module.DictionaryManager.Dtos.DictionaryType;
|
||||
|
||||
namespace Yi.Framework.Module.DictionaryManager
|
||||
{
|
||||
/// <summary>
|
||||
/// DictionaryType服务抽象
|
||||
/// </summary>
|
||||
public interface IDictionaryTypeService : ICrudAppService<DictionaryTypeGetOutputDto, DictionaryTypeGetListOutputDto, long, DictionaryTypeGetListInputVo, DictionaryTypeCreateInputVo, DictionaryTypeUpdateInputVo>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
43
Yi.Furion.Net6/Yi.Framework.Module/FileManager/FileEntity.cs
Normal file
43
Yi.Furion.Net6/Yi.Framework.Module/FileManager/FileEntity.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using SqlSugar;
|
||||
using Yi.Framework.Infrastructure.Data.Auditing;
|
||||
using Yi.Framework.Infrastructure.Ddd.Entities;
|
||||
|
||||
namespace Yi.Framework.Module.FileManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件表
|
||||
///</summary>
|
||||
[SugarTable("File")]
|
||||
public class FileEntity : IEntity<long>, IAuditedObject
|
||||
{
|
||||
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true)]
|
||||
public long Id { get; set; }
|
||||
/// <summary>
|
||||
/// 文件类型
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "FileContentType")]
|
||||
public string? FileContentType { get; set; }
|
||||
/// <summary>
|
||||
/// 文件大小
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "FileSize")]
|
||||
public decimal FileSize { get; set; }
|
||||
/// <summary>
|
||||
/// 文件名
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "FileName")]
|
||||
public string FileName { get; set; }
|
||||
/// <summary>
|
||||
/// 文件路径
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "FilePath")]
|
||||
public string FilePath { get; set; }
|
||||
|
||||
public DateTime CreationTime { get; set; }
|
||||
public long? CreatorId { get; set; }
|
||||
|
||||
public long? LastModifierId { get; set; }
|
||||
|
||||
public DateTime? LastModificationTime { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract;
|
||||
|
||||
namespace Yi.Framework.Module.FileManager
|
||||
{
|
||||
public class FileGetListOutputDto : IEntityDto
|
||||
{
|
||||
public long Id { get; set; }
|
||||
}
|
||||
}
|
||||
143
Yi.Furion.Net6/Yi.Framework.Module/FileManager/FileService.cs
Normal file
143
Yi.Furion.Net6/Yi.Framework.Module/FileManager/FileService.cs
Normal file
@@ -0,0 +1,143 @@
|
||||
using Furion.DependencyInjection;
|
||||
using Furion.DynamicApiController;
|
||||
using Mapster;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Yi.Framework.Infrastructure.AspNetCore;
|
||||
using Yi.Framework.Infrastructure.Const;
|
||||
using Yi.Framework.Infrastructure.Ddd.Repositories;
|
||||
using Yi.Framework.Infrastructure.Ddd.Services;
|
||||
using Yi.Framework.Infrastructure.Enums;
|
||||
using Yi.Framework.Infrastructure.Helper;
|
||||
using Yi.Framework.Module.ImageSharp;
|
||||
|
||||
namespace Yi.Framework.Module.FileManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件处理
|
||||
/// </summary>
|
||||
public class FileService : ApplicationService, IFileService, IDynamicApiController, ITransient
|
||||
{
|
||||
private readonly IRepository<FileEntity> _repository;
|
||||
private readonly ImageSharpManager _imageSharpManager;
|
||||
private readonly HttpContext _httpContext;
|
||||
public FileService(IRepository<FileEntity> repository, ImageSharpManager imageSharpManager, IHttpContextAccessor httpContextAccessor
|
||||
)
|
||||
{
|
||||
_repository = repository;
|
||||
_imageSharpManager = imageSharpManager;
|
||||
if (httpContextAccessor.HttpContext is null)
|
||||
{
|
||||
throw new ApplicationException("HttpContext为空");
|
||||
}
|
||||
_httpContext = httpContextAccessor.HttpContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 下载文件,是否缩略图
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Route("/api/file/{code}/{isThumbnail?}")]
|
||||
public async Task<IActionResult> Get([FromRoute] long code, [FromRoute] bool? isThumbnail)
|
||||
{
|
||||
var file = await _repository.GetByIdAsync(code);
|
||||
if (file is null)
|
||||
{
|
||||
return new NotFoundResult();
|
||||
}
|
||||
|
||||
var path = file.FilePath;
|
||||
//如果为缩略图,需要修改路径
|
||||
if (isThumbnail is true)
|
||||
{
|
||||
path = $"{PathConst.wwwroot}/{FileTypeEnum.Thumbnail}/{file.Id}{Path.GetExtension(file.FileName)}";
|
||||
}
|
||||
//路径为: 文件路径/文件id+文件扩展名
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
return new NotFoundResult();
|
||||
}
|
||||
|
||||
var steam = await File.ReadAllBytesAsync(path);
|
||||
|
||||
//设置附件下载,下载名称
|
||||
_httpContext.FileAttachmentHandle(file.FileName);
|
||||
return new FileContentResult(steam, file.FileContentType ?? @"text/plain");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上传文件
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<List<FileGetListOutputDto>> Post([FromForm] IFormFileCollection file)
|
||||
{
|
||||
if (file.Count() == 0)
|
||||
{
|
||||
throw new ArgumentException("文件上传为空!");
|
||||
}
|
||||
//批量插入
|
||||
List<FileEntity> entities = new();
|
||||
|
||||
foreach (var f in file)
|
||||
{
|
||||
FileEntity data = new();
|
||||
data.Id = SnowflakeHelper.NextId;
|
||||
data.FileSize = (decimal)f.Length / 1024;
|
||||
data.FileName = f.FileName;
|
||||
|
||||
|
||||
data.FileContentType = MimeHelper.GetMimeMapping(f.FileName);
|
||||
|
||||
|
||||
var type = MimeHelper.GetFileType(f.FileName);
|
||||
|
||||
//落盘文件,文件名为雪花id+自己的扩展名
|
||||
string filename = data.Id.ToString() + Path.GetExtension(f.FileName);
|
||||
string typePath = $"{PathConst.wwwroot}/{type}";
|
||||
if (!Directory.Exists(typePath))
|
||||
{
|
||||
Directory.CreateDirectory(typePath);
|
||||
}
|
||||
|
||||
var filePath = Path.Combine(typePath, filename);
|
||||
data.FilePath = filePath;
|
||||
|
||||
|
||||
//生成文件
|
||||
using (var stream = new FileStream(filePath, FileMode.CreateNew, FileAccess.ReadWrite))
|
||||
{
|
||||
await f.CopyToAsync(stream);
|
||||
|
||||
//如果是图片类型,还需要生成缩略图,当然,如果图片很小,直接复制过去即可
|
||||
if (FileTypeEnum.Image.Equals(type))
|
||||
{
|
||||
string thumbnailPath = $"{PathConst.wwwroot}/{FileTypeEnum.Thumbnail}";
|
||||
if (!Directory.Exists(thumbnailPath))
|
||||
{
|
||||
Directory.CreateDirectory(thumbnailPath);
|
||||
}
|
||||
string thumbnailFilePath = Path.Combine(thumbnailPath, filename);
|
||||
try
|
||||
{
|
||||
_imageSharpManager.ImageCompress(f.FileName, f.OpenReadStream(), thumbnailFilePath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
var result = new byte[stream.Length];
|
||||
await stream.ReadAsync(result, 0, result.Length);
|
||||
await File.WriteAllBytesAsync(thumbnailFilePath, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
entities.Add(data);
|
||||
}
|
||||
await _repository.InsertRangeAsync(entities);
|
||||
return entities.Adapt<List<FileGetListOutputDto>>();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Yi.Framework.Module.FileManager
|
||||
{
|
||||
public interface IFileService
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Yi.Framework.Module.ImageSharp.HeiCaptcha
|
||||
{
|
||||
public static class HeiCaptchaExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// 启用HeiCaptcha
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection AddHeiCaptcha(this IServiceCollection services)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
services.AddScoped<SecurityCodeHelper>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Formats.Gif;
|
||||
using SixLabors.ImageSharp.Formats.Png;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Yi.Framework.Module.ImageSharp.HeiCaptcha
|
||||
{
|
||||
public static class ImageRgba32Extension
|
||||
{
|
||||
public static byte[] ToPngArray<TPixel>(this Image<TPixel> img) where TPixel : unmanaged, IPixel<TPixel>
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
img.Save(ms, PngFormat.Instance);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] ToGifArray<TPixel>(this Image<TPixel> img) where TPixel : unmanaged, IPixel<TPixel>
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
img.Save(ms, new GifEncoder());
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
//using SixLabors.Fonts;
|
||||
//using SixLabors.ImageSharp;
|
||||
//using SixLabors.ImageSharp.PixelFormats;
|
||||
//using SixLabors.ImageSharp.Processing;
|
||||
//using System;
|
||||
//using System.Collections.Generic;
|
||||
|
||||
//namespace Hei.Captcha
|
||||
//{
|
||||
// public static class ImageSharpExtension
|
||||
// {
|
||||
|
||||
// / <summary>
|
||||
// / 绘制中文字符(可以绘制字母数字,但样式可能需要改)
|
||||
// / </summary>
|
||||
// / <typeparam name = "TPixel" ></ typeparam >
|
||||
// / < param name="processingContext"></param>
|
||||
// / <param name = "containerWidth" ></ param >
|
||||
// / < param name="containerHeight"></param>
|
||||
// / <param name = "text" ></ param >
|
||||
// / < param name="color"></param>
|
||||
// / <param name = "font" ></ param >
|
||||
// / < returns ></ returns >
|
||||
// public static IImageProcessingContext<TPixel> DrawingCnText<TPixel>(this IImageProcessingContext<TPixel> processingContext, int containerWidth, int containerHeight, string text, Rgba32 color, Font font)
|
||||
// where TPixel : struct, IPixel<TPixel>
|
||||
// {
|
||||
// return processingContext.Apply(img =>
|
||||
// {
|
||||
// if (string.IsNullOrEmpty(text) == false)
|
||||
// {
|
||||
// Random random = new Random();
|
||||
// var textWidth = (img.Width / text.Length);
|
||||
// var img2Size = Math.Min(textWidth, img.Height);
|
||||
// var fontMiniSize = (int)(img2Size * 0.6);
|
||||
// var fontMaxSize = (int)(img2Size * 0.95);
|
||||
|
||||
// for (int i = 0; i < text.Length; i++)
|
||||
// {
|
||||
// using (Image<Rgba32> img2 = new Image<Rgba32>(img2Size, img2Size))
|
||||
// {
|
||||
// Font scaledFont = new Font(font, random.Next(fontMiniSize, fontMaxSize));
|
||||
// var point = new Point(i * textWidth, (containerHeight - img2.Height) / 2);
|
||||
// var textGraphicsOptions = new TextGraphicsOptions(true)
|
||||
// {
|
||||
// HorizontalAlignment = HorizontalAlignment.Left,
|
||||
// VerticalAlignment = VerticalAlignment.Top
|
||||
// };
|
||||
|
||||
// img2.Mutate(ctx => ctx
|
||||
// .DrawText(textGraphicsOptions, text[i].ToString(), scaledFont, color, new Point(0, 0))
|
||||
// .Rotate(random.Next(-45, 45))
|
||||
// );
|
||||
// img.Mutate(ctx => ctx.DrawImage(img2, point, 1));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// public static IImageProcessingContext<TPixel> DrawingEnText<TPixel>(this IImageProcessingContext<TPixel> processingContext, int containerWidth, int containerHeight, string text, string[] colorHexArr, Font[] fonts)
|
||||
// where TPixel : struct, IPixel<TPixel>
|
||||
// {
|
||||
// return processingContext.Apply(img =>
|
||||
// {
|
||||
// if (string.IsNullOrEmpty(text) == false)
|
||||
// {
|
||||
// Random random = new Random();
|
||||
// var textWidth = (img.Width / text.Length);
|
||||
// var img2Size = Math.Min(textWidth, img.Height);
|
||||
// var fontMiniSize = (int)(img2Size * 0.9);
|
||||
// var fontMaxSize = (int)(img2Size * 1.37);
|
||||
// Array fontStyleArr = Enum.GetValues(typeof(FontStyle));
|
||||
|
||||
// for (int i = 0; i < text.Length; i++)
|
||||
// {
|
||||
// using (Image<Rgba32> img2 = new Image<Rgba32>(img2Size, img2Size))
|
||||
// {
|
||||
// Font scaledFont = new Font(fonts[random.Next(0, fonts.Length)], random.Next(fontMiniSize, fontMaxSize), (FontStyle)fontStyleArr.GetValue(random.Next(fontStyleArr.Length)));
|
||||
// var point = new Point(i * textWidth, (containerHeight - img2.Height) / 2);
|
||||
// var colorHex = colorHexArr[random.Next(0, colorHexArr.Length)];
|
||||
// var textGraphicsOptions = new TextGraphicsOptions(true)
|
||||
// {
|
||||
// HorizontalAlignment = HorizontalAlignment.Left,
|
||||
// VerticalAlignment = VerticalAlignment.Top
|
||||
// };
|
||||
|
||||
// img2.Mutate(ctx => ctx
|
||||
// .DrawText(textGraphicsOptions, text[i].ToString(), scaledFont, Rgba32.FromHex(colorHex), new Point(0, 0))
|
||||
// .DrawingGrid(containerWidth, containerHeight, Rgba32.FromHex(colorHex), 6, 1)
|
||||
// .Rotate(random.Next(-45, 45))
|
||||
// );
|
||||
// img.Mutate(ctx => ctx.DrawImage(img2, point, 1));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// / <summary>
|
||||
// / 画圆圈(泡泡)
|
||||
// / </summary>
|
||||
// / <typeparam name = "TPixel" ></ typeparam >
|
||||
// / < param name="processingContext"></param>
|
||||
// / <param name = "containerWidth" ></ param >
|
||||
// / < param name="containerHeight"></param>
|
||||
// / <param name = "count" ></ param >
|
||||
// / < param name="miniR"></param>
|
||||
// / <param name = "maxR" ></ param >
|
||||
// / < param name="color"></param>
|
||||
// / <param name = "canOverlap" ></ param >
|
||||
// / < returns ></ returns >
|
||||
// public static IImageProcessingContext<TPixel> DrawingCircles<TPixel>(this IImageProcessingContext<TPixel> processingContext, int containerWidth, int containerHeight, int count, int miniR, int maxR, TPixel color, bool canOverlap = false)
|
||||
// where TPixel : struct, IPixel<TPixel>
|
||||
// {
|
||||
// return processingContext.Apply(img =>
|
||||
// {
|
||||
// EllipsePolygon ep = null;
|
||||
// Random random = new Random();
|
||||
// PointF tempPoint = new PointF();
|
||||
// List<PointF> points = new List<PointF>();
|
||||
|
||||
// if (count > 0)
|
||||
// {
|
||||
// for (int i = 0; i < count; i++)
|
||||
// {
|
||||
// if (canOverlap)
|
||||
// {
|
||||
// tempPoint = new PointF(random.Next(0, containerWidth), random.Next(0, containerHeight));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// tempPoint = getCirclePoginF(containerWidth, containerHeight, (miniR + maxR), ref points);
|
||||
// }
|
||||
// ep = new EllipsePolygon(tempPoint, random.Next(miniR, maxR));
|
||||
|
||||
// img.Mutate(ctx => ctx
|
||||
// .Draw(color, (float)(random.Next(94, 145) / 100.0), ep.Clip())
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// / <summary>
|
||||
// / 画杂线
|
||||
// / </summary>
|
||||
// / <typeparam name = "TPixel" ></ typeparam >
|
||||
// / < param name="processingContext"></param>
|
||||
// / <param name = "containerWidth" ></ param >
|
||||
// / < param name="containerHeight"></param>
|
||||
// / <param name = "color" ></ param >
|
||||
// / < param name="count"></param>
|
||||
// / <param name = "thickness" ></ param >
|
||||
// / < returns ></ returns >
|
||||
// public static IImageProcessingContext<TPixel> DrawingGrid<TPixel>(this IImageProcessingContext<TPixel> processingContext, int containerWidth, int containerHeight, TPixel color, int count, float thickness)
|
||||
// where TPixel : struct, IPixel<TPixel>
|
||||
// {
|
||||
// return processingContext.Apply(img =>
|
||||
// {
|
||||
// var points = new List<PointF> { new PointF(0, 0) };
|
||||
// for (int i = 0; i < count; i++)
|
||||
// {
|
||||
// getCirclePoginF(containerWidth, containerHeight, 9, ref points);
|
||||
// }
|
||||
// points.Add(new PointF(containerWidth, containerHeight));
|
||||
// img.Mutate(ctx => ctx
|
||||
// .DrawLines(color, thickness, points.ToArray())
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
|
||||
// / <summary>
|
||||
// / 散 随机点
|
||||
// / </summary>
|
||||
// / <param name = "containerWidth" ></ param >
|
||||
// / < param name="containerHeight"></param>
|
||||
// / <param name = "lapR" ></ param >
|
||||
// / < param name="list"></param>
|
||||
// / <returns></returns>
|
||||
// private static PointF getCirclePoginF(int containerWidth, int containerHeight, double lapR, ref List<PointF> list)
|
||||
// {
|
||||
// Random random = new Random();
|
||||
// PointF newPoint = new PointF();
|
||||
// int retryTimes = 10;
|
||||
// double tempDistance = 0;
|
||||
|
||||
// do
|
||||
// {
|
||||
// newPoint.X = random.Next(0, containerWidth);
|
||||
// newPoint.Y = random.Next(0, containerHeight);
|
||||
// bool tooClose = false;
|
||||
// foreach (var p in list)
|
||||
// {
|
||||
// tooClose = false;
|
||||
// tempDistance = Math.Sqrt((Math.Pow((p.X - newPoint.X), 2) + Math.Pow((p.Y - newPoint.Y), 2)));
|
||||
// if (tempDistance < lapR)
|
||||
// {
|
||||
// tooClose = true;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// if (tooClose == false)
|
||||
// {
|
||||
// list.Add(newPoint);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// while (retryTimes-- > 0);
|
||||
|
||||
// if (retryTimes <= 0)
|
||||
// {
|
||||
// list.Add(newPoint);
|
||||
// }
|
||||
// return newPoint;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,182 @@
|
||||
using SixLabors.Fonts;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Drawing.Processing;
|
||||
using SixLabors.ImageSharp.Formats.Gif;
|
||||
using SixLabors.ImageSharp.Formats.Png;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace Yi.Framework.Module.ImageSharp.HeiCaptcha
|
||||
{
|
||||
/// <summary>
|
||||
/// 验证码配置和绘制逻辑
|
||||
/// </summary>
|
||||
public class SecurityCodeHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 验证码文本池
|
||||
/// </summary>
|
||||
private static readonly string[] _cnTextArr = new string[] { "的", "一", "国", "在", "人", "了", "有", "中", "是", "年", "和", "大", "业", "不", "为", "发", "会", "工", "经", "上", "地", "市", "要", "个", "产", "这", "出", "行", "作", "生", "家", "以", "成", "到", "日", "民", "来", "我", "部", "对", "进", "多", "全", "建", "他", "公", "开", "们", "场", "展", "时", "理", "新", "方", "主", "企", "资", "实", "学", "报", "制", "政", "济", "用", "同", "于", "法", "高", "长", "现", "本", "月", "定", "化", "加", "动", "合", "品", "重", "关", "机", "分", "力", "自", "外", "者", "区", "能", "设", "后", "就", "等", "体", "下", "万", "元", "社", "过", "前", "面", "农", "也", "得", "与", "说", "之", "员", "而", "务", "利", "电", "文", "事", "可", "种", "总", "改", "三", "各", "好", "金", "第", "司", "其", "从", "平", "代", "当", "天", "水", "省", "提", "商", "十", "管", "内", "小", "技", "位", "目", "起", "海", "所", "立", "已", "通", "入", "量", "子", "问", "度", "北", "保", "心", "还", "科", "委", "都", "术", "使", "明", "着", "次", "将", "增", "基", "名", "向", "门", "应", "里", "美", "由", "规", "今", "题", "记", "点", "计", "去", "强", "两", "些", "表", "系", "办", "教 正", "条", "最", "达", "特", "革", "收", "二", "期", "并", "程", "厂", "如", "道", "际 及", "西", "口", "京", "华", "任", "调", "性", "导", "组", "东", "路", "活", "广", "意", "比", "投", "决", "交", "统", "党", "南", "安", "此", "领", "结", "营", "项", "情", "解", "议", "义", "山", "先", "车", "然", "价", "放", "世", "间", "因", "共", "院", "步", "物", "界", "集", "把", "持", "无", "但", "城", "相", "书", "村", "求", "治", "取", "原", "处", "府", "研", "质", "信", "四", "运", "县", "军", "件", "育", "局", "干", "队", "团", "又", "造", "形", "级", "标", "联", "专", "少", "费", "效", "据", "手", "施", "权", "江", "近", "深", "更", "认", "果", "格", "几", "看", "没", "职", "服", "台", "式", "益", "想", "数", "单", "样", "只", "被", "亿", "老", "受", "优", "常", "销", "志", "战", "流", "很", "接", "乡", "头", "给", "至", "难", "观", "指", "创", "证", "织", "论", "别", "五", "协", "变", "风", "批", "见", "究", "支", "那", "查", "张", "精", "每", "林", "转", "划", "准", "做", "需", "传", "争", "税", "构", "具", "百", "或", "才", "积", "势", "举", "必", "型", "易", "视", "快", "李", "参", "回", "引", "镇", "首", "推", "思", "完", "消", "值", "该", "走", "装", "众", "责", "备", "州", "供", "包", "副", "极", "整", "确", "知", "贸", "己", "环", "话", "反", "身", "选", "亚", "么", "带", "采", "王", "策", "真", "女", "谈", "严", "斯", "况", "色", "打", "德", "告", "仅", "它", "气", "料", "神", "率", "识", "劳", "境", "源", "青", "护", "列", "兴", "许", "户", "马", "港", "则", "节", "款", "拉", "直", "案", "股", "光", "较", "河", "花", "根", "布", "线", "土", "克", "再", "群", "医", "清", "速", "律", "她", "族", "历", "非", "感", "占", "续", "师", "何", "影", "功", "负", "验", "望", "财", "类", "货", "约", "艺", "售", "连", "纪", "按", "讯", "史", "示", "象", "养", "获", "石", "食", "抓", "富", "模", "始", "住", "赛", "客", "越", "闻", "央", "席", "坚" };
|
||||
|
||||
private static readonly string[] _enTextArr = new string[] { "a", "b", "c", "d", "e", "f", "g", "h", "k", "m", "n", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
|
||||
|
||||
/// <summary>
|
||||
/// 验证码图片宽高
|
||||
/// </summary>
|
||||
private readonly int _imageWidth = 120;
|
||||
|
||||
private readonly int _imageHeight = 50;
|
||||
|
||||
/// <summary>
|
||||
/// 泡泡数量
|
||||
/// </summary>
|
||||
private int _circleCount = 14;
|
||||
|
||||
/// <summary>
|
||||
/// 泡泡半径范围
|
||||
/// </summary>
|
||||
private readonly int _miniCircleR = 2;
|
||||
|
||||
private readonly int _maxCircleR = 8;
|
||||
|
||||
/// <summary>
|
||||
/// 颜色池,较深的颜色
|
||||
/// https://tool.oschina.net/commons?type=3
|
||||
/// </summary>
|
||||
private static readonly string[] _colorHexArr = new string[] { "#00E5EE", "#000000", "#2F4F4F", "#000000", "#43CD80", "#191970", "#006400", "#458B00", "#8B7765", "#CD5B45" };
|
||||
|
||||
///较浅的颜色
|
||||
private static readonly string[] _lightColorHexArr = new string[] { "#FFFACD", "#FDF5E6", "#F0FFFF", "#BBFFFF", "#FAFAD2", "#FFE4E1", "#DCDCDC", "#F0E68C" };
|
||||
|
||||
private static readonly Random _random = new Random();
|
||||
|
||||
/// <summary>
|
||||
/// 字体池
|
||||
/// </summary>
|
||||
private static Font[] _fontArr;
|
||||
|
||||
public SecurityCodeHelper()
|
||||
{
|
||||
initFonts(_imageHeight);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成随机中文字符串
|
||||
/// </summary>
|
||||
/// <param name="length"></param>
|
||||
/// <returns></returns>
|
||||
public string GetRandomCnText(int length)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (length > 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
sb.Append(_cnTextArr[_random.Next(0, _cnTextArr.Length)]);
|
||||
}
|
||||
while (--length > 0);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成随机英文字母/数字组合字符串
|
||||
/// </summary>
|
||||
/// <param name="length"></param>
|
||||
/// <returns></returns>
|
||||
public string GetRandomEnDigitalText(int length)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (length > 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (_random.Next(0, 2) > 0)
|
||||
{
|
||||
sb.Append(_random.Next(2, 10));
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(_enTextArr[_random.Next(0, _enTextArr.Length)]);
|
||||
}
|
||||
}
|
||||
while (--length > 0);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 英文字母+数字组合验证码
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns>验证码图片字节数组</returns>
|
||||
public byte[] GetEnDigitalCodeByte(string text)
|
||||
{
|
||||
using (Image<Rgba32> img = getEnDigitalCodeImage(text))
|
||||
{
|
||||
return img.ToGifArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个数组组合验证码素材(Image)
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
private Image<Rgba32> getEnDigitalCodeImage(string text)
|
||||
{
|
||||
Image<Rgba32> img = new Image<Rgba32>(_imageWidth, _imageHeight);
|
||||
var colorTextHex = _colorHexArr[_random.Next(0, _colorHexArr.Length)];
|
||||
var lignthColorHex = _lightColorHexArr[_random.Next(0, _lightColorHexArr.Length)];
|
||||
|
||||
img.Mutate(ctx => ctx
|
||||
.Fill(Rgba32.ParseHex(_lightColorHexArr[_random.Next(0, _lightColorHexArr.Length)]))
|
||||
.Glow(Rgba32.ParseHex(lignthColorHex))
|
||||
//.DrawingGrid(_imageWidth, _imageHeight, Rgba32.ParseHex(lignthColorHex), 8, 1)
|
||||
.DrawText(text, _fontArr[0], Rgba32.ParseHex(_colorHexArr[0]), new PointF(0, 0))
|
||||
//.DrawingEnText(, text, _colorHexArr, _fontArr)
|
||||
.GaussianBlur(0.4f)
|
||||
//.DrawingCircles(_imageWidth, _imageHeight, 15, _miniCircleR, _maxCircleR, Color.White)
|
||||
);
|
||||
return img;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化字体池
|
||||
/// </summary>
|
||||
/// <param name="fontSize">一个初始大小</param>
|
||||
private void initFonts(int fontSize)
|
||||
{
|
||||
if (_fontArr == null)
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var names = assembly.GetManifestResourceNames();
|
||||
|
||||
if (names?.Length > 0 == true)
|
||||
{
|
||||
var fontList = new List<Font>();
|
||||
var fontCollection = new FontCollection();
|
||||
|
||||
foreach (var name in names)
|
||||
{
|
||||
fontList.Add(new Font(fontCollection.Add(assembly.GetManifestResourceStream(name)), fontSize));
|
||||
}
|
||||
|
||||
_fontArr = fontList.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"绘制验证码字体文件加载失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,65 @@
|
||||
using Furion.DependencyInjection;
|
||||
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||
using SixLabors.ImageSharp.Formats.Png;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
|
||||
namespace Yi.Framework.Module.ImageSharp;
|
||||
public class ImageSharpManager:ISingleton
|
||||
{
|
||||
public void ImageCompress(string fileName, Stream stream, string savePath)
|
||||
{
|
||||
var extensionName = Path.GetExtension(fileName).ToLower();
|
||||
if (extensionName == ".png")
|
||||
{
|
||||
PngImageCompress(stream, savePath);
|
||||
}
|
||||
else if (extensionName == ".jpg" || extensionName == ".jpeg")
|
||||
{
|
||||
JpgImageCompress(stream, savePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var fileStream = new FileStream(savePath, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
stream.CopyTo(fileStream);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void PngImageCompress(Stream stream, string savePath)
|
||||
{
|
||||
using (var image = Image.Load(stream))
|
||||
{
|
||||
var encoder = new PngEncoder()
|
||||
{
|
||||
CompressionLevel = PngCompressionLevel.Level6,
|
||||
|
||||
};
|
||||
if (image.Width > 300)
|
||||
{
|
||||
image.Mutate(a => a.Resize(image.Width / 2, image.Height / 2));
|
||||
}
|
||||
|
||||
image.Save(savePath, encoder);
|
||||
}
|
||||
}
|
||||
public void JpgImageCompress(Stream stream, string savePath)
|
||||
{
|
||||
using (var image = Image.Load(stream))
|
||||
{
|
||||
var encoder = new JpegEncoder()
|
||||
{
|
||||
Quality = 30
|
||||
};
|
||||
if (image.Width > 300)
|
||||
{
|
||||
image.Mutate(a => a.Resize(image.Width / 2, image.Height / 2));
|
||||
}
|
||||
|
||||
|
||||
image.Save(savePath, encoder);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using IPTools.Core;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Yi.Framework.Infrastructure.AspNetCore;
|
||||
using Yi.Framework.Infrastructure.CurrentUsers;
|
||||
using Yi.Framework.Infrastructure.Ddd.Repositories;
|
||||
using Yi.Framework.Infrastructure.Helper;
|
||||
|
||||
namespace Yi.Framework.Module.OperLogManager
|
||||
{
|
||||
public class GlobalOperLogAttribute : ActionFilterAttribute
|
||||
{
|
||||
private ILogger<GlobalOperLogAttribute> _logger;
|
||||
private IRepository<OperationLogEntity> _repository;
|
||||
private ICurrentUser _currentUser;
|
||||
//注入一个日志服务
|
||||
public GlobalOperLogAttribute(ILogger<GlobalOperLogAttribute> logger, IRepository<OperationLogEntity> repository, ICurrentUser currentUser)
|
||||
{
|
||||
_logger = logger;
|
||||
_repository = repository;
|
||||
_currentUser = currentUser;
|
||||
}
|
||||
|
||||
public override async void OnResultExecuted(ResultExecutedContext context)
|
||||
{
|
||||
//判断标签是在方法上
|
||||
if (context.ActionDescriptor is not ControllerActionDescriptor controllerActionDescriptor) return;
|
||||
|
||||
//查找标签,获取标签对象
|
||||
OperLogAttribute? operLogAttribute = controllerActionDescriptor.MethodInfo.GetCustomAttributes(inherit: true)
|
||||
.FirstOrDefault(a => a.GetType().Equals(typeof(OperLogAttribute))) as OperLogAttribute;
|
||||
//空对象直接返回
|
||||
if (operLogAttribute is null) return;
|
||||
|
||||
////获取控制器名
|
||||
//string controller = context.RouteData.Values["Controller"].ToString();
|
||||
|
||||
////获取方法名
|
||||
//string action = context.RouteData.Values["Action"].ToString();
|
||||
|
||||
//获取Ip
|
||||
string ip = context.HttpContext.GetClientIp();
|
||||
|
||||
//根据ip获取地址
|
||||
|
||||
var ipTool = IpTool.Search(ip);
|
||||
string location = ipTool.Province + " " + ipTool.City;
|
||||
|
||||
//日志服务插入一条操作记录即可
|
||||
|
||||
var logEntity = new OperationLogEntity();
|
||||
logEntity.Id = SnowflakeHelper.NextId;
|
||||
logEntity.OperIp = ip;
|
||||
//logEntity.OperLocation = location;
|
||||
logEntity.OperType = operLogAttribute.OperType;
|
||||
logEntity.Title = operLogAttribute.Title;
|
||||
logEntity.RequestMethod = context.HttpContext.Request.Method;
|
||||
logEntity.Method = context.HttpContext.Request.Path.Value;
|
||||
logEntity.OperLocation = location;
|
||||
|
||||
|
||||
logEntity.OperUser = _currentUser.UserName;
|
||||
if (operLogAttribute.IsSaveResponseData)
|
||||
{
|
||||
if (context.Result is ContentResult result && result.ContentType == "application/json")
|
||||
{
|
||||
logEntity.RequestResult = result.Content?.Replace("\r\n", "").Trim();
|
||||
}
|
||||
if (context.Result is JsonResult result2)
|
||||
{
|
||||
logEntity.RequestResult = result2.Value?.ToString();
|
||||
}
|
||||
|
||||
if (context.Result is ObjectResult result3)
|
||||
{
|
||||
logEntity.RequestResult = JsonHelper.ObjToStr(result3.Value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (operLogAttribute.IsSaveRequestData)
|
||||
{
|
||||
//logEntity.RequestParam = context.HttpContext.GetRequestValue(logEntity.RequestMethod);
|
||||
}
|
||||
|
||||
await _repository.InsertAsync(logEntity);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using Yi.Framework.Infrastructure.Ddd.Services.Abstract;
|
||||
|
||||
namespace Yi.Framework.Module.OperLogManager
|
||||
{
|
||||
/// <summary>
|
||||
/// OperationLog服务抽象
|
||||
/// </summary>
|
||||
public interface IOperationLogService : ICrudAppService<OperationLogGetListOutputDto, long, OperationLogGetListInputVo>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Module.OperLogManager
|
||||
{
|
||||
public enum OperEnum
|
||||
{
|
||||
Insert = 1,
|
||||
Update = 2,
|
||||
Delete = 3,
|
||||
Auth = 4,
|
||||
Export = 5,
|
||||
Import = 6,
|
||||
ForcedOut = 7,
|
||||
GenerateCode = 8,
|
||||
ClearData = 9
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Module.OperLogManager
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class OperLogAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// 操作类型
|
||||
/// </summary>
|
||||
public OperEnum OperType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 日志标题(模块)
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否保存请求数据
|
||||
/// </summary>
|
||||
public bool IsSaveRequestData { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 是否保存返回数据
|
||||
/// </summary>
|
||||
public bool IsSaveResponseData { get; set; } = true;
|
||||
|
||||
public OperLogAttribute(string title, OperEnum operationType)
|
||||
{
|
||||
Title = title;
|
||||
OperType = operationType;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using SqlSugar;
|
||||
using Yi.Framework.Infrastructure.Data.Auditing;
|
||||
using Yi.Framework.Infrastructure.Ddd.Entities;
|
||||
|
||||
namespace Yi.Framework.Module.OperLogManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 操作日志表
|
||||
///</summary>
|
||||
[SugarTable("OperationLog")]
|
||||
public class OperationLogEntity : IEntity<long>, ICreationAuditedObject
|
||||
{
|
||||
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true)]
|
||||
public long Id { get; set; }
|
||||
/// <summary>
|
||||
/// 操作模块
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "Title")]
|
||||
public string? Title { get; set; }
|
||||
/// <summary>
|
||||
/// 操作类型
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "OperType")]
|
||||
public OperEnum OperType { get; set; }
|
||||
/// <summary>
|
||||
/// 请求方法
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "RequestMethod")]
|
||||
public string? RequestMethod { get; set; }
|
||||
/// <summary>
|
||||
/// 操作人员
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "OperUser")]
|
||||
public string? OperUser { get; set; }
|
||||
/// <summary>
|
||||
/// 操作Ip
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "OperIp")]
|
||||
public string? OperIp { get; set; }
|
||||
/// <summary>
|
||||
/// 操作地点
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "OperLocation")]
|
||||
public string? OperLocation { get; set; }
|
||||
/// <summary>
|
||||
/// 操作方法
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "Method")]
|
||||
public string? Method { get; set; }
|
||||
/// <summary>
|
||||
/// 请求参数
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "RequestParam")]
|
||||
public string? RequestParam { get; set; }
|
||||
/// <summary>
|
||||
/// 请求结果
|
||||
///</summary>
|
||||
[SugarColumn(ColumnName = "RequestResult", Length = 9999)]
|
||||
public string? RequestResult { get; set; }
|
||||
|
||||
public DateTime CreationTime { get; set; }
|
||||
|
||||
public long? CreatorId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Yi.Framework.Infrastructure.Ddd.Dtos;
|
||||
|
||||
namespace Yi.Framework.Module.OperLogManager
|
||||
{
|
||||
public class OperationLogGetListInputVo : PagedAllResultRequestDto
|
||||
{
|
||||
public OperEnum? OperType { get; set; }
|
||||
public string? OperUser { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract;
|
||||
|
||||
namespace Yi.Framework.Module.OperLogManager
|
||||
{
|
||||
public class OperationLogGetListOutputDto : IEntityDto<long>
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public OperEnum OperType { get; set; }
|
||||
public string? RequestMethod { get; set; }
|
||||
public string? OperUser { get; set; }
|
||||
public string? OperIp { get; set; }
|
||||
public string? OperLocation { get; set; }
|
||||
public string? Method { get; set; }
|
||||
public string? RequestParam { get; set; }
|
||||
public string? RequestResult { get; set; }
|
||||
public DateTime CreationTime { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using Furion.DependencyInjection;
|
||||
using Furion.DynamicApiController;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SqlSugar;
|
||||
using Yi.Framework.Infrastructure.Ddd.Dtos;
|
||||
using Yi.Framework.Infrastructure.Ddd.Services;
|
||||
|
||||
namespace Yi.Framework.Module.OperLogManager
|
||||
{
|
||||
/// <summary>
|
||||
/// OperationLog服务实现
|
||||
/// </summary>
|
||||
//[AppService]
|
||||
public class OperationLogService : CrudAppService<OperationLogEntity, OperationLogGetListOutputDto, long, OperationLogGetListInputVo>,
|
||||
IOperationLogService, IDynamicApiController, ITransient
|
||||
{
|
||||
public override async Task<PagedResultDto<OperationLogGetListOutputDto>> GetListAsync(OperationLogGetListInputVo input)
|
||||
{
|
||||
var entity = await MapToEntityAsync(input);
|
||||
|
||||
RefAsync<int> total = 0;
|
||||
|
||||
var entities = await _DbQueryable.WhereIF(!string.IsNullOrEmpty(input.OperUser), x => x.OperUser.Contains(input.OperUser!))
|
||||
.WhereIF(input.OperType is not null, x => x.OperType == input.OperType)
|
||||
.WhereIF(input.StartTime is not null && input.EndTime is not null, x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime)
|
||||
.ToPageListAsync(input.PageNum, input.PageSize, total);
|
||||
return new PagedResultDto<OperationLogGetListOutputDto>(total, await MapToGetListOutputDtosAsync(entities));
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
public override Task<OperationLogGetListOutputDto> UpdateAsync(long id, OperationLogGetListOutputDto input)
|
||||
{
|
||||
return base.UpdateAsync(id, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using AlibabaCloud.SDK.Dysmsapi20170525;
|
||||
using Furion.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Yi.Framework.Module.Sms.Aliyun
|
||||
{
|
||||
public class SmsAliyunManager:ISingleton
|
||||
{
|
||||
public Client AliyunClient { get; set; }
|
||||
private ILogger<SmsAliyunManager> _logger;
|
||||
private SmsAliyunOptions Options { get; set; }
|
||||
public SmsAliyunManager(ILogger<SmsAliyunManager> logger, IOptions<SmsAliyunOptions> options)
|
||||
{
|
||||
Options = options.Value;
|
||||
if (Options.EnableFeature)
|
||||
{
|
||||
_logger = logger;
|
||||
AliyunClient = CreateClient(Options.AccessKeyId, Options.AccessKeySecret);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static Client CreateClient(string accessKeyId, string accessKeySecret)
|
||||
{
|
||||
AlibabaCloud.OpenApiClient.Models.Config config = new AlibabaCloud.OpenApiClient.Models.Config
|
||||
{
|
||||
// 必填,您的 AccessKey ID
|
||||
AccessKeyId = accessKeyId,
|
||||
// 必填,您的 AccessKey Secret
|
||||
AccessKeySecret = accessKeySecret,
|
||||
};
|
||||
// 访问的域名
|
||||
config.Endpoint = "dysmsapi.aliyuncs.com";
|
||||
return new Client(config);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 发送短信
|
||||
/// </summary>
|
||||
/// <param name="phoneNumbers"></param>
|
||||
/// <param name="code"></param>
|
||||
/// <returns></returns>
|
||||
public async Task Send(string phoneNumbers, string code)
|
||||
{
|
||||
try
|
||||
{
|
||||
AlibabaCloud.SDK.Dysmsapi20170525.Models.SendSmsRequest sendSmsRequest = new AlibabaCloud.SDK.Dysmsapi20170525.Models.SendSmsRequest
|
||||
{
|
||||
PhoneNumbers = phoneNumbers,
|
||||
SignName = Options.SignName,
|
||||
TemplateCode = Options.TemplateCode,
|
||||
TemplateParam = System.Text.Json.JsonSerializer.Serialize(new { code })
|
||||
};
|
||||
|
||||
var response = await AliyunClient.SendSmsAsync(sendSmsRequest);
|
||||
}
|
||||
|
||||
catch (Exception _error)
|
||||
{
|
||||
_logger.LogError(_error, _error.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Yi.Framework.Module.Sms.Aliyun
|
||||
{
|
||||
public class SmsAliyunOptions
|
||||
{
|
||||
public string AccessKeyId { get; set; }
|
||||
public string AccessKeySecret { get; set; }
|
||||
|
||||
public string SignName { get; set; }
|
||||
|
||||
|
||||
public string TemplateCode { get; set; }
|
||||
public bool EnableFeature { get; set; } = true;
|
||||
}
|
||||
}
|
||||
29
Yi.Furion.Net6/Yi.Framework.Module/Startup.cs
Normal file
29
Yi.Furion.Net6/Yi.Framework.Module/Startup.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Furion;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Yi.Framework.Infrastructure.AspNetCore;
|
||||
using Yi.Framework.Infrastructure.Sqlsugar;
|
||||
using Yi.Framework.Module.ImageSharp.HeiCaptcha;
|
||||
|
||||
namespace Yi.Framework.Module;
|
||||
|
||||
public class Startup : AppStartup
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddCurrentUserServer();
|
||||
|
||||
services.Configure<DbConnOptions>(App.Configuration.GetSection("DbConnOptions"));
|
||||
|
||||
services.AddDbSqlsugarContextServer();
|
||||
|
||||
services.AddHeiCaptcha();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="ImageSharp\HeiCaptcha\fonts\Candara.ttf" />
|
||||
<None Remove="ImageSharp\HeiCaptcha\fonts\impact.ttf" />
|
||||
<None Remove="ImageSharp\HeiCaptcha\fonts\monbaiti.ttf" />
|
||||
<None Remove="ImageSharp\HeiCaptcha\fonts\STCAIYUN.ttf" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AlibabaCloud.SDK.Dysmsapi20170525" Version="2.0.23" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
|
||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta15" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Yi.Framework.Infrastructure\Yi.Framework.Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="ImageSharp\HeiCaptcha\fonts\Candara.ttf" />
|
||||
<EmbeddedResource Include="ImageSharp\HeiCaptcha\fonts\impact.ttf" />
|
||||
<EmbeddedResource Include="ImageSharp\HeiCaptcha\fonts\monbaiti.ttf" />
|
||||
<EmbeddedResource Include="ImageSharp\HeiCaptcha\fonts\STCAIYUN.ttf" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="DictionaryManager\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user