feat: 合并

This commit is contained in:
橙子
2024-02-26 22:16:16 +08:00
72 changed files with 1521 additions and 221 deletions

View File

@@ -107,13 +107,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.TenantManageme
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "code-gen", "code-gen", "{4FFE7212-21F2-476D-B628-3C65E6C5075E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.CodeGen.Application", "module\code-gen\Yi.Framework.Codegen.Application\Yi.Framework.CodeGen.Application.csproj", "{97EC40D7-DBFA-467A-98CB-221AF27B14F2}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.CodeGen.Application", "module\code-gen\Yi.Framework.CodeGen.Application\Yi.Framework.CodeGen.Application.csproj", "{97EC40D7-DBFA-467A-98CB-221AF27B14F2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.CodeGen.Application.Contracts", "module\code-gen\Yi.Framework.Codegen.Application.Contracts\Yi.Framework.CodeGen.Application.Contracts.csproj", "{882BC563-2F75-4B95-AC96-F4BF23F5E69D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.CodeGen.Application.Contracts", "module\code-gen\Yi.Framework.CodeGen.Application.Contracts\Yi.Framework.CodeGen.Application.Contracts.csproj", "{882BC563-2F75-4B95-AC96-F4BF23F5E69D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.CodeGen.Domain", "module\code-gen\Yi.Framework.Codegen.Domain\Yi.Framework.CodeGen.Domain.csproj", "{85CB8517-2B80-42D8-B954-081079AC9BA0}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.CodeGen.Domain", "module\code-gen\Yi.Framework.CodeGen.Domain\Yi.Framework.CodeGen.Domain.csproj", "{85CB8517-2B80-42D8-B954-081079AC9BA0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.CodeGen.Domain.Shared", "module\code-gen\Yi.Framework.Codegen.Domain.Shared\Yi.Framework.CodeGen.Domain.Shared.csproj", "{EEFF0F05-2709-4151-A8CE-667935CEAE0B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.CodeGen.Domain.Shared", "module\code-gen\Yi.Framework.CodeGen.Domain.Shared\Yi.Framework.CodeGen.Domain.Shared.csproj", "{EEFF0F05-2709-4151-A8CE-667935CEAE0B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Caching.FreeRedis", "framework\Yi.Framework.Caching.FreeRedis\Yi.Framework.Caching.FreeRedis.csproj", "{862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.CodeGen.SqlSugarCore", "module\code-gen\Yi.Framework.CodeGen.SqlSugarCore\Yi.Framework.CodeGen.SqlSugarCore.csproj", "{FB09ACC2-A27D-4D87-8D85-1435FDED4D04}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -289,6 +293,14 @@ Global
{EEFF0F05-2709-4151-A8CE-667935CEAE0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EEFF0F05-2709-4151-A8CE-667935CEAE0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EEFF0F05-2709-4151-A8CE-667935CEAE0B}.Release|Any CPU.Build.0 = Release|Any CPU
{862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{862BB0EF-3D4E-44FF-AB15-0EB74CE553D3}.Release|Any CPU.Build.0 = Release|Any CPU
{FB09ACC2-A27D-4D87-8D85-1435FDED4D04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FB09ACC2-A27D-4D87-8D85-1435FDED4D04}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FB09ACC2-A27D-4D87-8D85-1435FDED4D04}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FB09ACC2-A27D-4D87-8D85-1435FDED4D04}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -341,6 +353,8 @@ Global
{882BC563-2F75-4B95-AC96-F4BF23F5E69D} = {4FFE7212-21F2-476D-B628-3C65E6C5075E}
{85CB8517-2B80-42D8-B954-081079AC9BA0} = {4FFE7212-21F2-476D-B628-3C65E6C5075E}
{EEFF0F05-2709-4151-A8CE-667935CEAE0B} = {4FFE7212-21F2-476D-B628-3C65E6C5075E}
{862BB0EF-3D4E-44FF-AB15-0EB74CE553D3} = {77B949E9-530E-45A5-9657-20F7D5C6875C}
{FB09ACC2-A27D-4D87-8D85-1435FDED4D04} = {4FFE7212-21F2-476D-B628-3C65E6C5075E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {23D6FBC9-C970-4641-BC1E-2AEA59F51C18}

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FreeRedis;
namespace Yi.Framework.Caching.FreeRedis
{
/// <summary>
/// 便于转到定义
/// </summary>
public class FreeSqlOptions: ConnectionStringBuilder
{
}
}

View File

@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FreeRedis" Version="1.2.13" />
<PackageReference Include="FreeRedis.DistributedCache" Version="1.2.5" />
<PackageReference Include="Volo.Abp.Caching" Version="8.0.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
namespace Yi.Framework.Caching.FreeRedis
{
[Dependency(ReplaceServices =true)]
public class YiDistributedCacheKeyNormalizer : IDistributedCacheKeyNormalizer, ITransientDependency
{
protected ICurrentTenant CurrentTenant { get; }
protected AbpDistributedCacheOptions DistributedCacheOptions { get; }
public YiDistributedCacheKeyNormalizer(
ICurrentTenant currentTenant,
IOptions<AbpDistributedCacheOptions> distributedCacheOptions)
{
CurrentTenant = currentTenant;
DistributedCacheOptions = distributedCacheOptions.Value;
}
public virtual string NormalizeKey(DistributedCacheKeyNormalizeArgs args)
{
var normalizedKey = $"{DistributedCacheOptions.KeyPrefix}{args.Key}";
//if (!args.IgnoreMultiTenancy && CurrentTenant.Id.HasValue)
//{
// normalizedKey = $"t:{CurrentTenant.Id.Value},{normalizedKey}";
//}
return normalizedKey;
}
}
}

View File

@@ -0,0 +1,32 @@
using FreeRedis;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Caching;
namespace Yi.Framework.Caching.FreeRedis
{
/// <summary>
/// 此模块得益于FreeRedis作者支持IDistributedCache使用湿滑
/// </summary>
[DependsOn(typeof(AbpCachingModule))]
public class YiFrameworkCachingFreeRedisModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
var redisEnabled = configuration["Redis:IsEnabled"];
if (redisEnabled.IsNullOrEmpty() || bool.Parse(redisEnabled))
{
var redisConfiguration = configuration["Redis:Configuration"];
RedisClient redisClient = new RedisClient(redisConfiguration);
context.Services.AddSingleton<IRedisClient>(redisClient);
context.Services.Replace(ServiceDescriptor.Singleton<IDistributedCache>(new
DistributedCache(redisClient)));
}
}
}
}

View File

@@ -60,6 +60,7 @@ namespace Yi.Framework.SqlSugarCore
{
options.ConnectionString = currentConnection;
}));
connectionCreator.SetDbAop(SqlSugarClient);
}
/// <summary>
@@ -242,6 +243,11 @@ namespace Yi.Framework.SqlSugarCore
/// <param name="pars"></param>
protected virtual void OnLogExecuted(string sql, SugarParameter[] pars)
{
if (Options.EnabledSqlLog)
{
var sqllog = $"=========Yi-SQL耗时{SqlSugarClient.Ado.SqlExecutionTime.TotalMilliseconds}毫秒=====";
Logger.CreateLogger<SqlSugarDbContext>().LogDebug(sqllog.ToString());
}
}
/// <summary>
@@ -251,7 +257,14 @@ namespace Yi.Framework.SqlSugarCore
/// <param name="column"></param>
protected virtual void EntityService(PropertyInfo property, EntityColumnInfo column)
{
if (property.PropertyType == typeof(ExtraPropertyDictionary))
{
column.IsIgnore = true;
}
if (property.Name == nameof(Entity<object>.Id))
{
column.IsPrimarykey = true;
}
}
public void BackupDataBase()

View File

@@ -6,9 +6,11 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Application.Services;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Users;
using Yi.Framework.Bbs.Application.Contracts.Dtos.Integral;
using Yi.Framework.Bbs.Domain.Managers;
using Yi.Framework.Bbs.Domain.Shared.Etos;
using Yi.Framework.Rbac.Domain.Authorization;
namespace Yi.Framework.Bbs.Application.Services.Integral
@@ -17,10 +19,12 @@ namespace Yi.Framework.Bbs.Application.Services.Integral
{
private IntegralManager _integralManager;
private ICurrentUser _currentUser;
public IntegralService(IntegralManager integralManager, ICurrentUser currentUser)
private ILocalEventBus _localEventBus;
public IntegralService(IntegralManager integralManager, ICurrentUser currentUser, ILocalEventBus localEventBus)
{
_integralManager = integralManager;
_currentUser = currentUser;
_localEventBus = localEventBus;
}

View File

@@ -8,7 +8,7 @@ namespace Yi.Framework.Bbs.Domain.Shared.Consts
{
public class LevelConst
{
public const string LevelCacheKey=nameof(LevelCacheKey);
public const string LevelCacheKey="Level:All";
public const string Level_Low_Zero = "经验提升等级低于或等于0";
}

View File

@@ -26,6 +26,9 @@ namespace Yi.Framework.CodeGen.Domain.Shared.Enums
[Display(Name = "DateTime", Description = "DateTime")]
DateTime,
[Display(Name = "Guid", Description = "Guid")]
Guid
}
}

View File

@@ -1,4 +1,5 @@
using SqlSugar;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
namespace Yi.Framework.CodeGen.Domain.Entities
@@ -24,5 +25,8 @@ namespace Yi.Framework.CodeGen.Domain.Entities
/// </summary>
[Navigate(NavigateType.OneToMany, nameof(FieldEntity.TableId))]
public List<FieldEntity> Fields { get; set; }
[SugarColumn(IsIgnore =true)]
public override ExtraPropertyDictionary ExtraProperties { get; protected set; }
}
}

View File

@@ -0,0 +1,116 @@
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Yi.Framework.CodeGen.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.CodeGen.SqlSugarCore
{
public class TemplateDataSeed : IDataSeedContributor, ITransientDependency
{
private ISqlSugarRepository<TemplateEntity> _repository;
public TemplateDataSeed(ISqlSugarRepository<TemplateEntity> repository)
{
_repository = repository;
}
public async Task SeedAsync(DataSeedContext context)
{
if (!await _repository.IsAnyAsync(x => true))
{
await _repository.InsertManyAsync(GetSeedData());
}
}
public List<TemplateEntity> GetSeedData()
{
var entities = new List<TemplateEntity>();
TemplateEntity entityTemplate = new TemplateEntity()
{
Name = "Entity",
BuildPath = "D:\\code\\Entities\\@ModelEntity.cs",
Remarks = "实体",
TemplateStr = "using SqlSugar;\r\nusing lo.Abp;\r\nusing lo.Abp.Auditing;\r\nusing lo.Abp.Domain.Entities;\r\nusing Yi.Framework.Core.Data;\r\n\r\nnamespace Yi.Framework.Rbac.Domain.Entities\r\n{\r\n /// <summary>\r\n /// 实体\r\n /// </summary>\r\n [SugarTable(\"@Model\")]\r\n public class @ModelEntity : Entity<Guid>\r\n {\r\n@field\r\n }\r\n}\r\n"
};
entities.Add(entityTemplate);
TemplateEntity getListInputTemplate = new TemplateEntity()
{
Name = "GetListInput",
BuildPath = "D:\\code\\Dtos\\@Model\\@ModelGetListInput.cs",
Remarks = "列表查询参数",
TemplateStr = "using Yi.Framework.Ddd;\r\nusing Yi.Framework.Ddd.Application.Contracts;\r\n\r\nnamespace Yi.Framework.Rbac.Application.Contracts.Dtos.@Model\r\n{\r\n /// <summary>\r\n /// 查询参数\r\n /// </summary>\r\n public class @ModelGetListInput : PagedAllResultRequestDto\r\n {\r\n@field\r\n }\r\n}\r\n"
};
entities.Add(getListInputTemplate);
TemplateEntity getListOutputDtoTemplate = new TemplateEntity()
{
Name = "GetListOutputDto",
BuildPath = "D:\\code\\Dtos\\@Model\\@ModelGetListOutputDto.cs",
Remarks = "列表返回dto",
TemplateStr = "using lo.Abp.Application.Dtos;\r\n\r\nnamespace Yi.Framework.Rbac.Application.Contracts.Dtos.@Model\r\n{\r\n public class @ModelGetListOutputDto : EntityDto<Guid>\r\n {\r\n@field\r\n }\r\n}\r\n"
};
entities.Add(getListOutputDtoTemplate);
TemplateEntity getOutputDtoTemplate = new TemplateEntity()
{
Name = "GetOutputDto",
BuildPath = "D:\\code\\Dtos\\@Model\\@ModelGetOutputDto.cs",
Remarks = "单返回dto",
TemplateStr = "using lo.Abp.Application.Dtos;\r\n\r\nnamespace Yi.Framework.Rbac.Application.Contracts.Dtos.@Model\r\n{\r\n public class @ModelGetOutputDto : EntityDto<Guid>\r\n {\r\n@field\r\n }\r\n}\r\n"
};
entities.Add(getOutputDtoTemplate);
TemplateEntity updateInputTemplate = new TemplateEntity()
{
Name = "UpdateInput",
BuildPath = "D:\\code\\Dtos\\@Model\\@ModelUpdateInput.cs",
Remarks = "更新输入",
TemplateStr = "namespace Yi.Framework.Rbac.Application.Contracts.Dtos.@Model\r\n{\r\n public class @ModelUpdateInput\r\n {\r\n@field\r\n }\r\n}\r\n"
};
entities.Add(updateInputTemplate);
TemplateEntity createInputTemplate = new TemplateEntity()
{
Name = "CreateInput",
BuildPath = "D:\\code\\Dtos\\@Model\\@ModelCreateInput.cs",
Remarks = "创建dto",
TemplateStr = "namespace Yi.Framework.Rbac.Application.Contracts.Dtos.@Model\r\n{\r\n /// <summary>\r\n /// @Model输入创建对象\r\n /// </summary>\r\n public class @ModelCreateInput\r\n {\r\n@field\r\n }\r\n}\r\n"
};
entities.Add(createInputTemplate);
TemplateEntity iServicesTemplate = new TemplateEntity()
{
Name = "IServices",
BuildPath = "D:\\code\\IServices\\I@ModelService.cs",
Remarks = "应用服务抽象",
TemplateStr = "using lo.Abp.Application.Services;\r\nusing Yi.Framework.Ddd.Application.Contracts;\r\nusing Yi.Framework.Rbac.Application.Contracts.Dtos.@Model;\r\n\r\nnamespace Yi.Framework.Rbac.Application.Contracts.IServices\r\n{\r\n /// <summary>\r\n /// @Model服务抽象\r\n /// </summary>\r\n public interface I@ModelService : IYiCrudAppService<@ModelGetOutputDto, @ModelGetListOutputDto, Guid, @ModelGetListInput, @ModelCreateInput, @ModelUpdateInput>\r\n {\r\n\r\n }\r\n}\r\n"
};
entities.Add(iServicesTemplate);
TemplateEntity servicesTemplate = new TemplateEntity()
{
Name = "Service",
BuildPath = "D:\\code\\Services\\@ModelService.cs",
Remarks = "应用服务",
TemplateStr = "using SqlSugar;\r\nusing lo.Abp.Application.Dtos;\r\nusing lo.Abp.Application.Services;\r\nusing Yi.Framework.Ddd.Application;\r\nusing Yi.Framework.Rbac.Application.Contracts.Dtos.@Model;\r\nusing Yi.Framework.Rbac.Application.Contracts.IServices;\r\nusing Yi.Framework.Rbac.Domain.Entities;\r\nusing Yi.Framework.SqlSugarCore.Abstractions;\r\n\r\nnamespace Yi.Framework.Rbac.Application.Services\r\n{\r\n /// <summary>\r\n /// @Model服务实现\r\n /// </summary>\r\n public class @ModelService : YiCrudAppService<@ModelEntity, @ModelGetOutputDto, @ModelGetListOutputDto, Guid, @ModelGetListInput, @ModelCreateInput, @ModelUpdateInput>,\r\n I@ModelService\r\n {\r\n private ISqlSugarRepository<@ModelEntity, Guid> _repository;\r\n public @ModelService(ISqlSugarRepository<@ModelEntity, Guid> repository) : base(repository)\r\n {\r\n _repository = repository;\r\n }\r\n\r\n /// <summary>\r\n /// 多查\r\n /// </summary>\r\n /// <param name=\"input\"></param>\r\n /// <returns></returns>\r\n public override async Task<PagedResultDto<@ModelGetListOutputDto>> GetListAsync(@ModelGetListInput input)\r\n {\r\n RefAsync<int> total = 0;\r\n\r\n var entities = await _repository._DbQueryable.WhereIF(!string.IsNullOrEmpty(input.@ModelKey), x => x.@ModelKey.Contains(input.@ModelKey!))\r\n .WhereIF(!string.IsNullOrEmpty(input.@ModelName), x => x.@ModelName!.Contains(input.@ModelName!))\r\n .WhereIF(input.StartTime is not null && input.EndTime is not null, x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime)\r\n .ToPageListAsync(input.SkipCount, input.MaxResultCount, total);\r\n return new PagedResultDto<@ModelGetListOutputDto>(total, await MapToGetListOutputDtosAsync(entities));\r\n }\r\n }\r\n}\r\n"
};
entities.Add(servicesTemplate);
TemplateEntity apiTemplate = new TemplateEntity()
{
TemplateStr = "import request from '@/utils/request'\r\n\r\n// 分页查询\r\nexport function listData(query) {\r\n return request({\r\n url: '/@model',\r\n method: 'get',\r\n params: query\r\n })\r\n}\r\n\r\n// id查询\r\nexport function getData(id) {\r\n return request({\r\n url: `/@model/${id}`,\r\n method: 'get'\r\n })\r\n}\r\n\r\n// 新增\r\nexport function addData(data) {\r\n return request({\r\n url: '/@model',\r\n method: 'post',\r\n data: data\r\n })\r\n}\r\n\r\n// 修改\r\nexport function updateData(id,data) {\r\n return request({\r\n url: `/@model/${id}`,\r\n method: 'put',\r\n data: data\r\n })\r\n}\r\n\r\n// 删除\r\nexport function delData(ids) {\r\n return request({\r\n url: `/@model`,\r\n method: 'delete',\r\n params:{id:ids}\r\n })\r\n}\r\n",
Name = "api",
BuildPath = "D:\\code\\Api\\@ModelApi.vue",
Remarks = "前端api"
};
entities.Add(apiTemplate);
return entities;
}
}
}

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\framework\Yi.Framework.SqlSugarCore\Yi.Framework.SqlSugarCore.csproj" />
<ProjectReference Include="..\Yi.Framework.CodeGen.Domain\Yi.Framework.CodeGen.Domain.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,10 @@
using Yi.Framework.SqlSugarCore;
namespace Yi.Framework.CodeGen.SqlSugarCore
{
[DependsOn(typeof(YiFrameworkSqlSugarCoreModule))]
public class YiFrameworkCodeGenSqlSugarCoreModule:AbpModule
{
}
}

View File

@@ -0,0 +1,17 @@
using Yi.Framework.Rbac.Domain.Shared.Enums;
namespace Yi.Framework.Rbac.Application.Contracts.Dtos.Notice
{
/// <summary>
/// Notice输入创建对象
/// </summary>
public class NoticeCreateInput
{
public string Title { get; set; }
public NoticeTypeEnum Type { get; set; }
public string Content { get; set; }
public int OrderNum { get; set; }
public bool State { get; set; }
}
}

View File

@@ -0,0 +1,14 @@
using Yi.Framework.Ddd.Application.Contracts;
using Yi.Framework.Rbac.Domain.Shared.Enums;
namespace Yi.Framework.Rbac.Application.Contracts.Dtos.Notice
{
/// <summary>
/// 查询参数
/// </summary>
public class NoticeGetListInput : PagedAllResultRequestDto
{
public string? Title { get; set; }
public NoticeTypeEnum? Type { get; set; }
}
}

View File

@@ -0,0 +1,20 @@
using Volo.Abp.Application.Dtos;
using Yi.Framework.Rbac.Domain.Shared.Enums;
namespace Yi.Framework.Rbac.Application.Contracts.Dtos.Notice
{
public class NoticeGetListOutputDto : EntityDto<Guid>
{
public string Title { get; set; }
public NoticeTypeEnum Type { get; set; }
public string Content { get; set; }
public bool IsDeleted { get; set; }
public DateTime CreationTime { get; set; }
public Guid? CreatorId { get; set; }
public Guid? LastModifierId { get; set; }
public DateTime? LastModificationTime { get; set; }
public int OrderNum { get; set; }
public bool State { get; set; }
}
}

View File

@@ -0,0 +1,20 @@
using Volo.Abp.Application.Dtos;
using Yi.Framework.Rbac.Domain.Shared.Enums;
namespace Yi.Framework.Rbac.Application.Contracts.Dtos.Notice
{
public class NoticeGetOutputDto : EntityDto<Guid>
{
public string Title { get; set; }
public NoticeTypeEnum Type { get; set; }
public string Content { get; set; }
public bool IsDeleted { get; set; }
public DateTime CreationTime { get; set; }
public Guid? CreatorId { get; set; }
public Guid? LastModifierId { get; set; }
public DateTime? LastModificationTime { get; set; }
public int OrderNum { get; set; }
public bool State { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
using Yi.Framework.Rbac.Domain.Shared.Enums;
namespace Yi.Framework.Rbac.Application.Contracts.Dtos.Notice
{
public class NoticeUpdateInput
{
public string? Title { get; set; }
public NoticeTypeEnum? Type { get; set; }
public string? Content { get; set; }
public int? OrderNum { get; set; }
public bool? State { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
using Yi.Framework.Ddd.Application.Contracts;
using Yi.Framework.Rbac.Application.Contracts.Dtos.Notice;
namespace Yi.Framework.Rbac.Application.Contracts.IServices
{
/// <summary>
/// Notice服务抽象
/// </summary>
public interface INoticeService : IYiCrudAppService<NoticeGetOutputDto, NoticeGetListOutputDto, Guid, NoticeGetListInput, NoticeCreateInput, NoticeUpdateInput>
{
}
}

View File

@@ -3,6 +3,7 @@ using Lazy.Captcha.Core;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using SqlSugar;
using Volo.Abp;
@@ -33,15 +34,17 @@ namespace Yi.Framework.Rbac.Application.Services
private readonly IGuidGenerator _guidGenerator;
private readonly RbacOptions _rbacOptions;
private readonly IAliyunManger _aliyunManger;
private IDistributedCache<UserInfoCacheItem, UserInfoCacheKey> _userCache;
public AccountService(IUserRepository userRepository,
ICurrentUser currentUser,
IAccountManager accountManager,
ISqlSugarRepository<MenuEntity> menuRepository,
IDistributedCache<CaptchaPhoneCacheItem, CaptchaPhoneCacheKey> phoneCache,
IDistributedCache<UserInfoCacheItem, UserInfoCacheKey> userCache,
ICaptcha captcha,
IGuidGenerator guidGenerator,
IOptions<RbacOptions> options,
IAliyunManger aliyunManger )
IAliyunManger aliyunManger)
{
_userRepository = userRepository;
_currentUser = currentUser;
@@ -52,6 +55,7 @@ namespace Yi.Framework.Rbac.Application.Services
_guidGenerator = guidGenerator;
_rbacOptions = options.Value;
_aliyunManger = aliyunManger;
_userCache = userCache;
}
@@ -113,10 +117,10 @@ namespace Yi.Framework.Rbac.Application.Services
[Authorize(AuthenticationSchemes = TokenTypeConst.Refresh)]
public async Task<object> PostRefreshAsync([FromQuery] string refresh_token)
{
var userId = CurrentUser.Id.Value;
var accessToken = await _accountManager.GetTokenByUserIdAsync(userId);
var refreshToken = _accountManager.CreateRefreshToken(userId);
return new { Token = accessToken, RefreshToken = refreshToken };
var userId = CurrentUser.Id.Value;
var accessToken = await _accountManager.GetTokenByUserIdAsync(userId);
var refreshToken = _accountManager.CreateRefreshToken(userId);
return new { Token = accessToken, RefreshToken = refreshToken };
}
/// <summary>
@@ -249,7 +253,7 @@ namespace Yi.Framework.Rbac.Application.Services
/// <summary>
/// 查询已登录的账户信息
/// 查询已登录的账户信息,已缓存
/// </summary>
/// <returns></returns>
[Route("account")]
@@ -263,16 +267,33 @@ namespace Yi.Framework.Rbac.Application.Services
{
throw new UserFriendlyException("用户未登录");
}
//此处从缓存中获取也行
//var data = _cacheManager.Get<UserRoleMenuDto>($"Yi:UserInfo:{userId}");
var data = await _userRepository.GetUserAllInfoAsync(userId ?? Guid.Empty);
//系统用户数据被重置,老前端访问重新授权
if (data is null)
//此处优先从缓存中获取
UserRoleMenuDto output = null;
var cacheData = await _userCache.GetAsync(new UserInfoCacheKey(userId.Value));
if (cacheData is not null)
{
throw new AbpAuthorizationException();
output = cacheData.Info;
}
data.Menus.Clear();
return data;
else
{
var data = await _userRepository.GetUserAllInfoAsync(userId.Value);
//系统用户数据被重置,老前端访问重新授权
if (data is null)
{
throw new AbpAuthorizationException();
}
data.Menus.Clear();
output = data;
var tokenExpiresMinuteTime = LazyServiceProvider.GetRequiredService<IOptions<JwtOptions>>().Value.ExpiresMinuteTime;
//将用户信息放入缓存下次获取直接从缓存中获取即可过期时间为token过期时间
await _userCache.SetAsync(new UserInfoCacheKey(userId.Value), new UserInfoCacheItem(data), new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(tokenExpiresMinuteTime) });
}
return output;
}
@@ -307,10 +328,18 @@ namespace Yi.Framework.Rbac.Application.Services
/// 退出登录
/// </summary>
/// <returns></returns>
public Task<bool> PostLogout()
public async Task<bool> PostLogout()
{
//通过鉴权jwt获取到用户的id
var userId = _currentUser.Id;
if (userId is null)
{
return false;
// throw new UserFriendlyException("用户已退出");
}
await _userCache.RemoveAsync(new UserInfoCacheKey(userId.Value));
//Jwt去中心化登出只需用记录日志即可
return Task.FromResult(true);
return true;
}
/// <summary>

View File

@@ -0,0 +1,127 @@
using FreeRedis;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Microsoft.VisualBasic;
using TencentCloud.Mna.V20210119.Models;
using Volo.Abp.Application.Services;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Yi.Framework.Rbac.Application.Contracts.Dtos.MonitorCache;
using Yi.Framework.Rbac.Application.Contracts.IServices;
namespace Yi.Framework.Rbac.Application.Services.Monitor
{
public class MonitorCacheService : ApplicationService, IMonitorCacheService
{
public IAbpLazyServiceProvider LazyServiceProvider { get; set; }
/// <summary>
/// 缓存前缀
/// </summary>
private string CacheKeyPrefix => LazyServiceProvider.LazyGetRequiredService<IOptions<AbpDistributedCacheOptions>>().Value.KeyPrefix;
private bool EnableRedisCache
{
get
{
var redisEnabled = LazyServiceProvider.LazyGetRequiredService<IConfiguration>()["Redis:IsEnabled"];
return redisEnabled.IsNullOrEmpty() || bool.Parse(redisEnabled);
}
}
/// <summary>
/// 使用懒加载防止报错
/// </summary>
private IRedisClient RedisClient => LazyServiceProvider.LazyGetRequiredService<IRedisClient>();
/// <summary>
/// 获取所有key并分组
/// </summary>
/// <returns></returns>
[HttpGet("monitor-cache/name")]
public List<MonitorCacheNameGetListOutputDto> GetName()
{
VerifyRedisCacheEnable();
var keys = RedisClient.Keys(CacheKeyPrefix + "*");
var result = GroupedKeys(keys.ToList());
var output = result.Select(x => new MonitorCacheNameGetListOutputDto { CacheName = x }).ToList();
return output;
}
private List<string> GroupedKeys(List<string> keys)
{
HashSet<string> resultSet = new HashSet<string>();
foreach (string str in keys)
{
string[] parts = str.Split(':');
// 如果字符串中包含冒号,则将第一部分和第二部分进行分组
if (parts.Length >= 2)
{
string group = $"{parts[0]}:{parts[1]}";
resultSet.Add(group);
}
// 如果字符串中不包含冒号,则直接进行分组
else
{
resultSet.Add(str);
}
}
return resultSet.ToList();
}
private void VerifyRedisCacheEnable()
{
if (!EnableRedisCache)
{
throw new UserFriendlyException("后端程序未使用Redis缓存无法对Redis进行监控可切换使用Redis");
}
}
[HttpGet("monitor-cache/key/{cacaheName}")]
public List<string> GetKey(string cacaheName)
{
VerifyRedisCacheEnable();
var output = RedisClient.Keys($"{cacaheName}:*").Select(x => x.RemovePreFix(cacaheName + ":"));
return output.ToList();
}
//全部不为空
[HttpGet("monitor-cache/value/{cacaheName}/{cacaheKey}")]
public MonitorCacheGetListOutputDto GetValue(string cacaheName, string cacaheKey)
{
var value = RedisClient.HGet($"{cacaheName}:{cacaheKey}", "data");
return new MonitorCacheGetListOutputDto() { CacheKey = cacaheKey, CacheName = cacaheName, CacheValue = value };
}
[HttpDelete("monitor-cache/key/{cacaheName}")]
public bool DeleteKey(string cacaheName)
{
VerifyRedisCacheEnable();
RedisClient.Del($"{cacaheName}:*");
return true;
}
[HttpDelete("monitor-cache/value/{cacaheName}/{cacaheKey}")]
public bool DeleteValue(string cacaheName, string cacaheKey)
{
RedisClient.Del($"{cacaheName}:{cacaheKey}");
return true;
}
[HttpDelete("monitor-cache/clear")]
public bool DeleteClear()
{
RedisClient.FlushDb();
return true;
}
}
}

View File

@@ -7,7 +7,7 @@ using Volo.Abp.Application.Services;
using Yi.Framework.Core.Helper;
using Yi.Framework.Rbac.Application.Contracts.IServices;
namespace Yi.Framework.Rbac.Application.Services
namespace Yi.Framework.Rbac.Application.Services.Monitor
{
public class MonitorServerService : ApplicationService, IMonitorServerService
{

View File

@@ -7,13 +7,13 @@ using Yi.Framework.Rbac.Application.Contracts.IServices;
using Yi.Framework.Rbac.Application.SignalRHubs;
using Yi.Framework.Rbac.Domain.Shared.Model;
namespace Yi.Framework.Rbac.Application.Services
namespace Yi.Framework.Rbac.Application.Services.Monitor
{
public class OnlineService : ApplicationService, IOnlineService
{
private ILogger<OnlineService> _logger;
private IHubContext<OnlineUserHub> _hub;
public OnlineService(ILogger<OnlineService> logger, IHubContext<OnlineUserHub> hub)
private IHubContext<OnlineHub> _hub;
public OnlineService(ILogger<OnlineService> logger, IHubContext<OnlineHub> hub)
{
_logger = logger;
_hub = hub;
@@ -26,7 +26,7 @@ namespace Yi.Framework.Rbac.Application.Services
/// <returns></returns>
public Task<PagedResultDto<OnlineUserModel>> GetListAsync([FromQuery] OnlineUserModel online)
{
var data = OnlineUserHub.clientUsers;
var data = OnlineHub.clientUsers;
IEnumerable<OnlineUserModel> dataWhere = data.AsEnumerable();
if (!string.IsNullOrEmpty(online.Ipaddr))
@@ -37,7 +37,7 @@ namespace Yi.Framework.Rbac.Application.Services
{
dataWhere = dataWhere.Where((u) => u.UserName!.Contains(online.UserName));
}
return Task.FromResult(new PagedResultDto<OnlineUserModel>() { TotalCount = data.Count, Items = dataWhere.ToList() }) ;
return Task.FromResult(new PagedResultDto<OnlineUserModel>() { TotalCount = data.Count, Items = dataWhere.ToList() });
}
@@ -50,7 +50,7 @@ namespace Yi.Framework.Rbac.Application.Services
[Route("online/{connnectionId}")]
public async Task<bool> ForceOut(string connnectionId)
{
if (OnlineUserHub.clientUsers.Exists(u => u.ConnnectionId == connnectionId))
if (OnlineHub.clientUsers.Exists(u => u.ConnnectionId == connnectionId))
{
//前端接受到这个事件后,触发前端自动退出
await _hub.Clients.Client(connnectionId).SendAsync("forceOut", "你已被强制退出!");

View File

@@ -11,7 +11,7 @@ using Yi.Framework.Rbac.Application.Contracts.Dtos.Task;
using Yi.Framework.Rbac.Application.Contracts.IServices;
using Yi.Framework.Rbac.Domain.Shared.Enums;
namespace Yi.Framework.Rbac.Application.Services
namespace Yi.Framework.Rbac.Application.Services.Monitor
{
public class TaskService : ApplicationService, ITaskService
{
@@ -19,7 +19,7 @@ namespace Yi.Framework.Rbac.Application.Services
private readonly IClock _clock;
public TaskService(ISchedulerFactory schedulerFactory, IClock clock)
{
_clock=clock;
_clock = clock;
_schedulerFactory = schedulerFactory;
}
@@ -39,7 +39,7 @@ namespace Yi.Framework.Rbac.Application.Services
//状态
var state = await scheduler.GetTriggerState(trigger.Key);
var output = new TaskGetOutput
{
JobId = jobDetail.Key.Name,
@@ -48,7 +48,7 @@ namespace Yi.Framework.Rbac.Application.Services
Properties = Newtonsoft.Json.JsonConvert.SerializeObject(jobDetail.JobDataMap),
Concurrent = !jobDetail.ConcurrentExecutionDisallowed,
Description = jobDetail.Description,
LastRunTime = _clock.Normalize( trigger.GetPreviousFireTimeUtc()?.DateTime??DateTime.MinValue),
LastRunTime = _clock.Normalize(trigger.GetPreviousFireTimeUtc()?.DateTime ?? DateTime.MinValue),
NextRunTime = _clock.Normalize(trigger.GetNextFireTimeUtc()?.DateTime ?? DateTime.MinValue),
AssemblyName = jobDetail.JobType.Assembly.GetName().Name,
Status = state.ToString()
@@ -56,7 +56,7 @@ namespace Yi.Framework.Rbac.Application.Services
if (trigger is ISimpleTrigger simple)
{
output.TriggerArgs =Math.Round(simple.RepeatInterval.TotalMinutes,2) .ToString() + "分钟";
output.TriggerArgs = Math.Round(simple.RepeatInterval.TotalMinutes, 2).ToString() + "分钟";
output.Type = JobTypeEnum.Millisecond;
output.Millisecond = simple.RepeatInterval.TotalMilliseconds;
}
@@ -64,7 +64,7 @@ namespace Yi.Framework.Rbac.Application.Services
{
output.TriggerArgs = cron.CronExpressionString!;
output.Type = JobTypeEnum.Cron;
output.Cron=cron.CronExpressionString;
output.Cron = cron.CronExpressionString;
}
return output;
}
@@ -159,7 +159,7 @@ namespace Yi.Framework.Rbac.Application.Services
/// <param name="id"></param>
/// <returns></returns>
public async Task DeleteAsync(IEnumerable<string> id)
{
{
var scheduler = await _schedulerFactory.GetScheduler();
await scheduler.DeleteJobs(id.Select(x => new JobKey(x)).ToList());
}

View File

@@ -1,45 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Application.Services;
using Yi.Framework.Rbac.Application.Contracts.Dtos.MonitorCache;
using Yi.Framework.Rbac.Application.Contracts.IServices;
namespace Yi.Framework.Rbac.Application.Services
{
public class MonitorCacheService : ApplicationService, IMonitorCacheService
{
private static List<MonitorCacheNameGetListOutputDto> monitorCacheNames => new List<MonitorCacheNameGetListOutputDto>()
{
new MonitorCacheNameGetListOutputDto{ CacheName="Yi:Login",Remark="登录验证码"},
new MonitorCacheNameGetListOutputDto{ CacheName="Yi:User",Remark="用户信息"}
};
private Dictionary<string, string> monitorCacheNamesDic => monitorCacheNames.ToDictionary(x => x.CacheName, x => x.Remark);
//private CSRedisClient _cacheClient;
public MonitorCacheService()
{
//_cacheClient = redisCacheClient.Client;
}
//cacheKey value为空只要name和备注
public List<MonitorCacheNameGetListOutputDto> GetName()
{
//固定的
return monitorCacheNames;
}
[HttpGet("key/{cacaheName}")]
public List<string> GetKey(string cacaheName)
{
//var output = _cacheClient.Keys($"{cacaheName}:*");
return new List<string>() { "1233124", "3124", "1231251", "12312412" };
}
//全部不为空
[HttpGet("value/{cacaheName}/{cacaheKey}")]
public MonitorCacheGetListOutputDto GetValue(string cacaheName, string cacaheKey)
{
//var value = _cacheClient.Get($"{cacaheName}:{cacaheKey}");
return new MonitorCacheGetListOutputDto() { CacheKey = cacaheKey, CacheName = cacaheName, CacheValue = "ttt", Remark = monitorCacheNamesDic[cacaheName] };
}
}
}

View File

@@ -0,0 +1,70 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using SqlSugar;
using Volo.Abp.Application.Dtos;
using Yi.Framework.Ddd.Application;
using Yi.Framework.Rbac.Application.Contracts.Dtos.Notice;
using Yi.Framework.Rbac.Application.Contracts.IServices;
using Yi.Framework.Rbac.Application.SignalRHubs;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Rbac.Application.Services
{
/// <summary>
/// Notice服务实现
/// </summary>
public class NoticeService : YiCrudAppService<NoticeEntity, NoticeGetOutputDto, NoticeGetListOutputDto, Guid, NoticeGetListInput, NoticeCreateInput, NoticeUpdateInput>,
INoticeService
{
private ISqlSugarRepository<NoticeEntity, Guid> _repository;
private IHubContext<NoticeHub> _hubContext;
public NoticeService(ISqlSugarRepository<NoticeEntity, Guid> repository, IHubContext<NoticeHub> hubContext) : base(repository)
{
_hubContext = hubContext;
_repository = repository;
}
/// <summary>
/// 多查
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public override async Task<PagedResultDto<NoticeGetListOutputDto>> GetListAsync(NoticeGetListInput input)
{
RefAsync<int> total = 0;
var entities = await _repository._DbQueryable.WhereIF(input.Type is not null, x => x.Type == input.Type)
.WhereIF(!string.IsNullOrEmpty(input.Title), x => x.Title!.Contains(input.Title!))
.WhereIF(input.StartTime is not null && input.EndTime is not null, x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
return new PagedResultDto<NoticeGetListOutputDto>(total, await MapToGetListOutputDtosAsync(entities));
}
/// <summary>
/// 发送在线消息
/// </summary>
/// <returns></returns>
[HttpPost("notice/online/{id}")]
public async Task SendOnlineAsync([FromRoute] Guid id)
{
var entity = await _repository._DbQueryable.FirstAsync(x => x.Id == id);
await _hubContext.Clients.All.SendAsync("ReceiveNotice", entity.Type.ToString(), entity.Title, entity.Content);
}
/// <summary>
/// 发送离线消息
/// </summary>
/// <returns></returns>
[HttpPost("notice/offline/{id}")]
public async Task SendOfflineAsync([FromRoute] Guid id)
{
//先发送一个在线
await SendOnlineAsync(id);
//然后将所有用户和通知id进行保留记录判断是否已读还是未读
//在首次请求返回全部未读的通知给前端即可
}
}
}

View File

@@ -7,7 +7,7 @@ using Yi.Framework.Rbac.Application.Contracts.Dtos.LoginLog;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Rbac.Application.Services
namespace Yi.Framework.Rbac.Application.Services.RecordLog
{
public class LoginLogService : YiCrudAppService<LoginLogEntity, LoginLogGetListOutputDto, Guid, LoginLogGetListInputVo>
{

View File

@@ -7,7 +7,7 @@ using Yi.Framework.Rbac.Application.Contracts.IServices;
using Yi.Framework.Rbac.Domain.Operlog;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Rbac.Application.Services
namespace Yi.Framework.Rbac.Application.Services.RecordLog
{
/// <summary>
/// OperationLog服务实现
@@ -18,7 +18,7 @@ namespace Yi.Framework.Rbac.Application.Services
private ISqlSugarRepository<OperationLogEntity, Guid> _repository;
public OperationLogService(ISqlSugarRepository<OperationLogEntity, Guid> repository) : base(repository)
{
_repository=repository;
_repository = repository;
}
public override async Task<PagedResultDto<OperationLogGetListOutputDto>> GetListAsync(OperationLogGetListInputVo input)

View File

@@ -0,0 +1,15 @@
using Volo.Abp.AspNetCore.SignalR;
namespace Yi.Framework.Rbac.Application.SignalRHubs
{
[HubRoute("/hub/notice")]
public class NoticeHub : AbpHub
{
/// <summary>
/// 由于发布功能,主要是服务端项客户端主动推送
/// </summary>
public NoticeHub()
{
}
}
}

View File

@@ -10,14 +10,14 @@ namespace Yi.Framework.Rbac.Application.SignalRHubs
{
[HubRoute("/hub/main")]
[Authorize]
public class OnlineUserHub : AbpHub
public class OnlineHub : AbpHub
{
public static readonly List<OnlineUserModel> clientUsers = new();
private readonly static object objLock = new object();
private HttpContext? _httpContext;
private ILogger<OnlineUserHub> _logger => LoggerFactory.CreateLogger<OnlineUserHub>();
public OnlineUserHub(IHttpContextAccessor httpContextAccessor)
private ILogger<OnlineHub> _logger => LoggerFactory.CreateLogger<OnlineHub>();
public OnlineHub(IHttpContextAccessor httpContextAccessor)
{
_httpContext = httpContextAccessor?.HttpContext;
}

View File

@@ -20,7 +20,7 @@ namespace Yi.Framework.Rbac.Domain.Shared.Caches
public override string ToString()
{
return $"Yi:Phone:{Phone}";
return $"Phone:{Phone}";
}
}
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Yi.Framework.Rbac.Domain.Shared.Dtos;
namespace Yi.Framework.Rbac.Domain.Shared.Caches
{
public class UserInfoCacheItem
{
public UserInfoCacheItem(UserRoleMenuDto info) { Info = info; }
/// <summary>
/// 存储的用户信息
/// </summary>
public UserRoleMenuDto Info { get; set; }
}
public class UserInfoCacheKey
{
public UserInfoCacheKey(Guid userId) { UserId = userId; }
public Guid UserId { get; set; }
public override string ToString()
{
return $"User:{UserId}";
}
}
}

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Yi.Framework.Rbac.Domain.Shared.Enums
{
public enum NoticeTypeEnum
{
[Description("走马灯")]
MerryGoRound = 0,
[Description("提示弹窗")]
Popup = 1
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
using Yi.Framework.Core.Data;
using Yi.Framework.Rbac.Domain.Shared.Enums;
namespace Yi.Framework.Rbac.Domain.Entities
{
[SugarTable("Notice")]
public class NoticeEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IState
{
[SugarColumn(IsPrimaryKey = true)]
public override Guid Id { get; protected set; }
/// <summary>
/// 公告标题
/// </summary>
public string Title { get; set; }
/// <summary>
/// 类型
/// </summary>
public NoticeTypeEnum Type { get; set; }
/// <summary>
/// 内容
/// </summary>
[SugarColumn(ColumnDataType = StaticConfig.CodeFirst_BigString)]
public string Content { get; set; }
public bool IsDeleted { get; set; }
public DateTime CreationTime { get; set; }
public Guid? CreatorId { get; set; }
public Guid? LastModifierId { get; set; }
public DateTime? LastModificationTime { get; set; }
public int OrderNum { get; set; }
public bool State { get; set; }
}
}

View File

@@ -10,7 +10,7 @@ namespace Yi.Framework.Rbac.Domain.Entities
/// 岗位表
///</summary>
[SugarTable("Post")]
public partial class PostEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IState
public class PostEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IState
{
/// <summary>

View File

@@ -7,7 +7,7 @@ namespace Yi.Framework.Rbac.Domain.Entities;
/// 角色部门关系表
///</summary>
[SugarTable("RoleDept")]
public partial class RoleDeptEntity : Entity<Guid>
public class RoleDeptEntity : Entity<Guid>
{
/// <summary>
/// 主键

View File

@@ -6,15 +6,15 @@ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using Volo.Abp;
using Volo.Abp.Caching;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Services;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Security.Claims;
using Volo.Abp.Users;
using Yi.Framework.Core.Helper;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.Rbac.Domain.Repositories;
using Yi.Framework.Rbac.Domain.Shared.Caches;
using Yi.Framework.Rbac.Domain.Shared.Consts;
using Yi.Framework.Rbac.Domain.Shared.Dtos;
using Yi.Framework.Rbac.Domain.Shared.Etos;
@@ -38,6 +38,7 @@ namespace Yi.Framework.Rbac.Domain.Managers
private UserManager _userManager;
private ISqlSugarRepository<RoleEntity> _roleRepository;
private RefreshJwtOptions _refreshJwtOptions;
public AccountManager(IUserRepository repository
, IHttpContextAccessor httpContextAccessor
, IOptions<JwtOptions> jwtOptions
@@ -87,9 +88,9 @@ namespace Yi.Framework.Rbac.Domain.Managers
loginEto.UserId = userInfo.User.Id;
await _localEventBus.PublishAsync(loginEto);
}
var accessToken = CreateToken(this.UserInfoToClaim(userInfo));
//将用户信息添加到缓存中,需要考虑的是更改了用户、角色、菜单等整个体系都需要将缓存进行刷新,看具体业务进行选择
var accessToken = CreateToken(this.UserInfoToClaim(userInfo));
return accessToken;
}
@@ -175,11 +176,12 @@ namespace Yi.Framework.Rbac.Domain.Managers
{
userAction.Invoke(user);
}
if (user == null)
//这里为了兼容解决数据库开启了大小写不敏感问题,还要将用户名进行二次效验
if (user != null&&user.UserName==userName)
{
return false;
return true;
}
return true;
return false;
}
/// <summary>

View File

@@ -21,6 +21,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\framework\Yi.Framework.Caching.FreeRedis\Yi.Framework.Caching.FreeRedis.csproj" />
<ProjectReference Include="..\..\..\framework\Yi.Framework.SqlSugarCore.Abstractions\Yi.Framework.SqlSugarCore.Abstractions.csproj" />
<ProjectReference Include="..\Yi.Framework.Rbac.Domain.Shared\Yi.Framework.Rbac.Domain.Shared.csproj" />
</ItemGroup>

View File

@@ -3,6 +3,7 @@ using Volo.Abp.AspNetCore.SignalR;
using Volo.Abp.Caching;
using Volo.Abp.Domain;
using Volo.Abp.Modularity;
using Yi.Framework.Caching.FreeRedis;
using Yi.Framework.Mapster;
using Yi.Framework.Rbac.Domain.Authorization;
using Yi.Framework.Rbac.Domain.Operlog;
@@ -13,6 +14,7 @@ namespace Yi.Framework.Rbac.Domain
{
[DependsOn(
typeof(YiFrameworkRbacDomainSharedModule),
typeof(YiFrameworkCachingFreeRedisModule),
typeof(AbpAspNetCoreSignalRModule),
typeof(AbpDddDomainModule),

View File

@@ -1125,6 +1125,75 @@ namespace Yi.Framework.Rbac.SqlSugarCore.DataSeeds
//通知公告
MenuEntity notice = new MenuEntity(_guidGenerator.Create())
{
MenuName = "通知公告",
PermissionCode = "system:notice:list",
MenuType = MenuTypeEnum.Menu,
Router = "notice",
IsShow = true,
IsLink = false,
IsCache = true,
Component = "system/notice/index",
MenuIcon = "message",
OrderNum = 93,
ParentId = system.Id,
IsDeleted = false
};
entities.Add(notice);
MenuEntity noticeQuery = new MenuEntity(_guidGenerator.Create())
{
MenuName = "通知查询",
PermissionCode = "system:notice:query",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = notice.Id,
IsDeleted = false
};
entities.Add(noticeQuery);
MenuEntity noticeAdd = new MenuEntity(_guidGenerator.Create())
{
MenuName = "通知新增",
PermissionCode = "system:notice:add",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = notice.Id,
IsDeleted = false
};
entities.Add(noticeAdd);
MenuEntity noticeEdit = new MenuEntity(_guidGenerator.Create())
{
MenuName = "通知修改",
PermissionCode = "system:notice:edit",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = notice.Id,
IsDeleted = false
};
entities.Add(noticeEdit);
MenuEntity noticeRemove = new MenuEntity(_guidGenerator.Create())
{
MenuName = "通知删除",
PermissionCode = "system:notice:remove",
MenuType = MenuTypeEnum.Component,
OrderNum = 100,
ParentId = notice.Id,
IsDeleted = false
};
entities.Add(noticeRemove);
//日志管理
MenuEntity log = new MenuEntity(_guidGenerator.Create())
{
@@ -1135,7 +1204,7 @@ namespace Yi.Framework.Rbac.SqlSugarCore.DataSeeds
IsShow = true,
IsLink = false,
MenuIcon = "log",
OrderNum = 93,
OrderNum = 92,
ParentId = system.Id,
IsDeleted = false
};

View File

@@ -1,5 +1,6 @@
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;
using Volo.Abp.Application.Services;
using Volo.Abp.Uow;
using Yi.Framework.Bbs.Application.Contracts.Dtos.Banner;
@@ -117,5 +118,19 @@ namespace Yi.Abp.Application.Services
var entity = new BannerEntity();
var dto = entity.Adapt<BannerGetListOutputDto>();
}
/// <summary>
/// 速率限制
/// </summary>
/// <returns></returns>
// [DisableRateLimiting]
//[EnableRateLimiting("sliding")]
public int GetRateLimiting()
{
RequestNumber++;
return RequestNumber;
}
private static int RequestNumber { get; set; } = 0;
}
}

View File

@@ -6,6 +6,7 @@
<ProjectReference Include="..\..\framework\Yi.Framework.SqlSugarCore\Yi.Framework.SqlSugarCore.csproj" />
<ProjectReference Include="..\..\module\audit-logging\Yi.Framework.AuditLogging.SqlSugarCore\Yi.Framework.AuditLogging.SqlSugarCore.csproj" />
<ProjectReference Include="..\..\module\bbs\Yi.Framework.Bbs.SqlSugarCore\Yi.Framework.Bbs.SqlSugarCore.csproj" />
<ProjectReference Include="..\..\module\code-gen\Yi.Framework.CodeGen.SqlSugarCore\Yi.Framework.CodeGen.SqlSugarCore.csproj" />
<ProjectReference Include="..\..\module\rbac\Yi.Framework.Rbac.SqlSugarCore\Yi.Framework.Rbac.SqlSugarCore.csproj" />
<ProjectReference Include="..\..\module\tenant-management\Yi.Framework.TenantManagement.SqlSugarCore\Yi.Framework.TenantManagement.SqlSugarCore.csproj" />
<ProjectReference Include="..\Yi.Abp.Domain\Yi.Abp.Domain.csproj" />

View File

@@ -4,6 +4,7 @@ using Yi.Abp.Domain;
using Yi.Abp.SqlSugarCore;
using Yi.Framework.AuditLogging.SqlSugarCore;
using Yi.Framework.Bbs.SqlSugarCore;
using Yi.Framework.CodeGen.SqlSugarCore;
using Yi.Framework.Mapster;
using Yi.Framework.Rbac.SqlSugarCore;
using Yi.Framework.SqlSugarCore;
@@ -17,6 +18,7 @@ namespace Yi.Abp.SqlsugarCore
typeof(YiFrameworkRbacSqlSugarCoreModule),
typeof(YiFrameworkBbsSqlSugarCoreModule),
typeof(YiFrameworkCodeGenSqlSugarCoreModule),
typeof(YiFrameworkAuditLoggingSqlSugarCoreModule),
typeof(YiFrameworkTenantManagementSqlSugarCoreModule),

View File

@@ -1,6 +1,8 @@
using SqlSugar;
using Microsoft.Extensions.Logging;
using SqlSugar;
using Volo.Abp.DependencyInjection;
using Yi.Framework.Rbac.SqlSugarCore;
using Yi.Framework.SqlSugarCore;
namespace Yi.Abp.SqlSugarCore
{

View File

@@ -1,6 +1,9 @@
using System.Text;
using System.Globalization;
using System.Text;
using System.Threading.RateLimiting;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.RateLimiting;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json.Converters;
@@ -84,10 +87,12 @@ namespace Yi.Abp.Web
});
//设置缓存不要过期默认滑动20分钟
Configure<AbpDistributedCacheOptions>(cacheOptions =>
{
cacheOptions.GlobalCacheEntryOptions.SlidingExpiration =null;
});
Configure<AbpDistributedCacheOptions>(cacheOptions =>
{
cacheOptions.GlobalCacheEntryOptions.SlidingExpiration = null;
//缓存key前缀
cacheOptions.KeyPrefix = "Yi:";
});
Configure<AbpAntiForgeryOptions>(options =>
@@ -133,24 +138,62 @@ namespace Yi.Abp.Web
//options.TenantResolvers.RemoveAll(x => x.Name == CookieTenantResolveContributor.ContributorName);
});
//速率限制
//每60秒限制100个请求滑块添加分6段
service.AddRateLimiter(_ =>
{
_.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
_.OnRejected = (context, _) =>
{
if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
{
context.HttpContext.Response.Headers.RetryAfter =
((int)retryAfter.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo);
}
context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
context.HttpContext.Response.WriteAsync("Too many requests. Please try again later.");
return new ValueTask();
};
//全局使用,链式表达式
_.GlobalLimiter = PartitionedRateLimiter.CreateChained(
PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
{
var userAgent = httpContext.Request.Headers.UserAgent.ToString();
return RateLimitPartition.GetSlidingWindowLimiter
(userAgent, _ =>
new SlidingWindowRateLimiterOptions
{
PermitLimit = 100,
Window = TimeSpan.FromSeconds(60),
SegmentsPerWindow = 6,
QueueProcessingOrder = QueueProcessingOrder.OldestFirst
});
}));
});
//jwt鉴权
var jwtOptions = configuration.GetSection(nameof(JwtOptions)).Get<JwtOptions>();
var refreshJwtOptions = configuration.GetSection(nameof(RefreshJwtOptions)).Get<RefreshJwtOptions>();
context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
.AddJwtBearer(options =>
{
ClockSkew = TimeSpan.Zero,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtOptions.Issuer,
ValidAudience = jwtOptions.Audience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOptions.SecurityKey))
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
options.TokenValidationParameters = new TokenValidationParameters
{
ClockSkew = TimeSpan.Zero,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtOptions.Issuer,
ValidAudience = jwtOptions.Audience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOptions.SecurityKey))
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
if (!string.IsNullOrEmpty(accessToken))
@@ -159,21 +202,21 @@ namespace Yi.Abp.Web
}
return Task.CompletedTask;
}
};
})
.AddJwtBearer(TokenTypeConst.Refresh, options =>
{
options.TokenValidationParameters = new TokenValidationParameters
};
})
.AddJwtBearer(TokenTypeConst.Refresh, options =>
{
ClockSkew = TimeSpan.Zero,
ValidateIssuerSigningKey = true,
ValidIssuer = refreshJwtOptions.Issuer,
ValidAudience = refreshJwtOptions.Audience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(refreshJwtOptions.SecurityKey))
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
options.TokenValidationParameters = new TokenValidationParameters
{
ClockSkew = TimeSpan.Zero,
ValidateIssuerSigningKey = true,
ValidIssuer = refreshJwtOptions.Issuer,
ValidAudience = refreshJwtOptions.Audience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(refreshJwtOptions.SecurityKey))
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var refresh_token = context.Request.Headers["refresh_token"];
if (!string.IsNullOrEmpty(refresh_token))
@@ -189,17 +232,17 @@ namespace Yi.Abp.Web
return Task.CompletedTask;
}
};
};
})
.AddQQ(options =>
{
configuration.GetSection("OAuth:QQ").Bind(options);
})
.AddGitee(options =>
{
configuration.GetSection("OAuth:Gitee").Bind(options);
});
})
.AddQQ(options =>
{
configuration.GetSection("OAuth:QQ").Bind(options);
})
.AddGitee(options =>
{
configuration.GetSection("OAuth:Gitee").Bind(options);
});
//授权
context.Services.AddAuthorization();
@@ -219,6 +262,9 @@ namespace Yi.Abp.Web
//跨域
app.UseCors(DefaultCorsPolicyName);
//速率限制
app.UseRateLimiter();
//无感token先刷新再鉴权
app.UseRefreshToken();

View File

@@ -23,7 +23,7 @@
"EnabledSqlLog": true,
"EnabledDbSeed": true,
//SAAS多租户
"EnabledSaasMultiTenancy":false
"EnabledSaasMultiTenancy": false
//读写分离地址
//"ReadUrl": [
// "DataSource=[xxxx]", //Sqlite
@@ -32,6 +32,12 @@
//]
},
//redis使用freeesql参数在“FreeSqlOptions的ConnectionStringBuilder中”
"Redis": {
"IsEnabled": false,
"Configuration": "127.0.0.1:6379,password=123,defaultDatabase=13"
},
//鉴权
"JwtOptions": {
"Issuer": "https://ccnetcore.com",
@@ -47,6 +53,7 @@
"ExpiresMinuteTime": 172800
},
//第三方登录
"OAuth": {
//QQ

View File

@@ -9,6 +9,7 @@
"version": "0.0.0",
"dependencies": {
"@element-plus/icons-vue": "^2.1.0",
"@lucky-canvas/vue": "^0.1.11",
"@microsoft/signalr": "^8.0.0",
"axios": "^1.3.4",
"dayjs": "^1.11.10",
@@ -855,6 +856,51 @@
"@jridgewell/sourcemap-codec": "1.4.14"
}
},
"node_modules/@lucky-canvas/vue": {
"version": "0.1.11",
"resolved": "https://registry.npmmirror.com/@lucky-canvas/vue/-/vue-0.1.11.tgz",
"integrity": "sha512-5vm0txSKRBtMgrE/HZEvw1joSTx9NTdAkc8tBp/aX0LxyhQtiTVBLsRgdYUK/OiURCL8bo+046BTGnV+Q4JFlg==",
"dependencies": {
"@vue/composition-api": "^1.0.0",
"lucky-canvas": "^1.7.23",
"vue-demi": "^0.7.4"
},
"peerDependencies": {
"vue": "^2.0.0 || >=3.0.0-rc.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/@lucky-canvas/vue/node_modules/@vue/composition-api": {
"version": "1.7.2",
"resolved": "https://registry.npmmirror.com/@vue/composition-api/-/composition-api-1.7.2.tgz",
"integrity": "sha512-M8jm9J/laYrYT02665HkZ5l2fWTK4dcVg3BsDHm/pfz+MjDYwX+9FUaZyGwEyXEDonQYRCo0H7aLgdklcIELjw==",
"peerDependencies": {
"vue": ">= 2.5 < 2.7"
}
},
"node_modules/@lucky-canvas/vue/node_modules/vue-demi": {
"version": "0.7.5",
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.7.5.tgz",
"integrity": "sha512-eFSQSvbQdY7C9ujOzvM6tn7XxwLjn0VQDXQsiYBLBwf28Na+2nTQR4BBBcomhmdP6mmHlBKAwarq6a0BPG87hQ==",
"hasInstallScript": true,
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-beta.1",
"vue": "^2.6.0 || >=3.0.0-rc.1"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/@microsoft/signalr": {
"version": "8.0.0",
"resolved": "https://registry.npmmirror.com/@microsoft/signalr/-/signalr-8.0.0.tgz",
@@ -2673,6 +2719,11 @@
"node": ">=10"
}
},
"node_modules/lucky-canvas": {
"version": "1.7.27",
"resolved": "https://registry.npmmirror.com/lucky-canvas/-/lucky-canvas-1.7.27.tgz",
"integrity": "sha512-Ftz6qD+863bI7xijBmZg3dw3cNEc7odPr70EZQcGA14y3TgTAzH65HPosOCd6kKUlMwhntBaHMx3onoj9MtJRQ=="
},
"node_modules/magic-string": {
"version": "0.25.9",
"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz",
@@ -4617,6 +4668,30 @@
"@jridgewell/sourcemap-codec": "1.4.14"
}
},
"@lucky-canvas/vue": {
"version": "0.1.11",
"resolved": "https://registry.npmmirror.com/@lucky-canvas/vue/-/vue-0.1.11.tgz",
"integrity": "sha512-5vm0txSKRBtMgrE/HZEvw1joSTx9NTdAkc8tBp/aX0LxyhQtiTVBLsRgdYUK/OiURCL8bo+046BTGnV+Q4JFlg==",
"requires": {
"@vue/composition-api": "^1.0.0",
"lucky-canvas": "^1.7.23",
"vue-demi": "^0.7.4"
},
"dependencies": {
"@vue/composition-api": {
"version": "1.7.2",
"resolved": "https://registry.npmmirror.com/@vue/composition-api/-/composition-api-1.7.2.tgz",
"integrity": "sha512-M8jm9J/laYrYT02665HkZ5l2fWTK4dcVg3BsDHm/pfz+MjDYwX+9FUaZyGwEyXEDonQYRCo0H7aLgdklcIELjw==",
"requires": {}
},
"vue-demi": {
"version": "0.7.5",
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.7.5.tgz",
"integrity": "sha512-eFSQSvbQdY7C9ujOzvM6tn7XxwLjn0VQDXQsiYBLBwf28Na+2nTQR4BBBcomhmdP6mmHlBKAwarq6a0BPG87hQ==",
"requires": {}
}
}
},
"@microsoft/signalr": {
"version": "8.0.0",
"resolved": "https://registry.npmmirror.com/@microsoft/signalr/-/signalr-8.0.0.tgz",
@@ -6128,6 +6203,11 @@
"yallist": "^4.0.0"
}
},
"lucky-canvas": {
"version": "1.7.27",
"resolved": "https://registry.npmmirror.com/lucky-canvas/-/lucky-canvas-1.7.27.tgz",
"integrity": "sha512-Ftz6qD+863bI7xijBmZg3dw3cNEc7odPr70EZQcGA14y3TgTAzH65HPosOCd6kKUlMwhntBaHMx3onoj9MtJRQ=="
},
"magic-string": {
"version": "0.25.9",
"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz",

View File

@@ -10,6 +10,7 @@
},
"dependencies": {
"@element-plus/icons-vue": "^2.1.0",
"@lucky-canvas/vue": "^0.1.11",
"@microsoft/signalr": "^8.0.0",
"axios": "^1.3.4",
"dayjs": "^1.11.10",

View File

@@ -7,6 +7,7 @@
</template>
<script setup>
import signalR from "@/utils/signalR";
import noticeSignalR from "@/utils/noticeSignalR";
import useConfigStore from "@/stores/config";
import { ElConfigProvider } from "element-plus";
import useUserStore from "@/stores/user.js";
@@ -25,9 +26,8 @@ if (loading !== null) {
//加载全局信息
onMounted(async () => {
await configStore.getConfig();
// setInterval(() => {
// console.log("token的值"+tokenValue.value);
// }, 1000); // 1000毫秒即1秒
noticeSignalR.close();
noticeSignalR.init(`notice`);
});

View File

@@ -13,3 +13,10 @@ export function signInRecord() {
method: "get"
});
}
export function luckyWheel() {
return request({
url: "/lucky/wheel",
method: "post"
});
}

View File

@@ -18,7 +18,7 @@
<el-icon><Memo /></el-icon>
<span>任务列表</span>
</el-menu-item>
<el-menu-item index="4" :route="{path:'/activity/sign'}">
<el-menu-item index="4" :route="{path:'/activity/lucky'}">
<el-icon><HelpFilled /></el-icon>
<span>大转盘</span>
</el-menu-item>

View File

@@ -11,6 +11,7 @@ import "@/assets/styles/index.scss"; // global css
import * as ElementPlusIconsVue from "@element-plus/icons-vue";
import directive from "./directive"; // directive
import VueLuckyCanvas from '@lucky-canvas/vue'
import "./permission";
@@ -24,6 +25,7 @@ import "./permission";
app.use(pinia);
directive(app);
app.use(router);
app.use(VueLuckyCanvas);
await router.isReady();
app.mount("#app");
})();

View File

@@ -122,6 +122,14 @@ const router = createRouter({
title: "等级",
},
},
{
name: "lucky",
path: "lucky",
component: () => import("../views/lucky/Index.vue"),
meta: {
title: "大转盘",
},
},
],
},
{ path: "/:pathMatch(.*)*", name: "NotFound", component: NotFound },

View File

@@ -0,0 +1,107 @@
// 官方文档https://docs.microsoft.com/zh-cn/aspnet/core/signalr/javascript-client?view=aspnetcore-6.0&viewFallbackFrom=aspnetcore-2.2&tabs=visual-studio
import * as signalR from "@microsoft/signalr";
export default {
// signalR对象
SR: {},
// 失败连接重试次数
failNum: 4,
async init(url) {
const connection = new signalR.HubConnectionBuilder()
.withUrl(`${import.meta.env.VITE_APP_BASE_WS}/` + url)
.withAutomaticReconnect() //自动重新连接
.configureLogging(signalR.LogLevel.Information)
.build();
this.SR = connection;
// 断线重连
connection.onclose(async () => {
console.log("断开连接了");
console.assert(
connection.state === signalR.HubConnectionState.Disconnected
);
// 建议用户重新刷新浏览器
});
connection.onreconnected(() => {
console.log("断线重新连接成功");
});
this.receiveMsg(connection);
// 启动
await this.start();
},
/**
* 调用 this.signalR.start().then(async () => { await this.SR.invoke("method")})
* @returns
*/
async close() {
try {
var that = this;
await this.SR.stop();
this.SR = {};
} catch { }
},
async start() {
var that = this;
try {
//使用async和await 或 promise的then 和catch 处理来自服务端的异常
await this.SR.start();
//console.assert(this.SR.state === signalR.HubConnectionState.Connected);
//console.log('signalR 连接成功了', this.SR.state);
return true;
} catch (error) {
that.failNum--;
//console.log(`失败重试剩余次数${that.failNum}`, error)
if (that.failNum > 0) {
setTimeout(async () => {
await this.SR.start();
}, 5000);
}
return false;
}
},
// 接收消息处理
receiveMsg(connection) {
connection.on("receiveNotice", (type, title, content) => {
switch (type) {
case "MerryGoRound":
ElNotification({
title: title,
dangerouslyUseHTMLString: true,
message: content,
})
break;
case "Popup":
ElNotification({
title: title,
dangerouslyUseHTMLString: true,
message: content,
})
break;
}
});
// connection.on("onlineNum", (data) => {
// store.dispatch("socket/changeOnlineNum", data);
// });
// // 接收欢迎语
// connection.on("welcome", (data) => {
// console.log('welcome', data)
// Notification.info(data)
// });
// // 接收后台手动推送消息
// connection.on("receiveNotice", (title, data) => {
// Notification({
// type: 'info',
// title: title,
// message: data,
// dangerouslyUseHTMLString: true,
// duration: 0
// })
// })
},
};

View File

@@ -92,7 +92,7 @@ if(registerForm.password!=passwordConfirm.value)
});
//成功
if (response!=undefined) {
if (response.status==204) {
ElMessage({
message: `恭喜!${registerForm.userName},注册成功!请登录!`,
type: 'success',

View File

@@ -39,9 +39,10 @@ import {getList,upgrade} from '@/apis/levelApi.js'
import {getBbsUserProfile} from '@/apis/userApi.js'
import { ref,onMounted, reactive,computed } from 'vue'
import useAuths from '@/hooks/useAuths.js';
const { isLogin,currentUserInfo } = useAuths();
import useUserStore from "@/stores/user";
const { isLogin } = useAuths();
const userInfo=ref({});
const currentUserInfo=useUserStore();
const levelData =ref([]);
const moneyNum=ref(1);
@@ -73,7 +74,7 @@ const loadLevelData= async () => {
const loadUserInfoData=async()=>{
if(isLogin)
{
const {data}= await getBbsUserProfile(currentUserInfo.value.id);
const {data}= await getBbsUserProfile(currentUserInfo.id);
userInfo.value=data;
}
}

View File

@@ -0,0 +1,118 @@
<template>
<div>
<h2>谨记人生赌博不如理性规划</h2>
<h3>50钱钱一次,我的钱钱:{{ userInfo?.money ?? '未登录' }}</h3>
<LuckyWheel class="wheel" ref="myLucky" width="500px" height="500px" :prizes="prizes" :blocks="blocks"
:buttons="buttons" @start="startCallback" @end="endCallback" />
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import useAuths from '@/hooks/useAuths.js';
const { isLogin } = useAuths();
import { getBbsUserProfile } from '@/apis/userApi.js'
import { luckyWheel } from '@/apis/integralApi.js'
import useUserStore from "@/stores/user";
const userInfo = ref({});
const currentUserInfo=useUserStore();
onMounted(async () => {
await loadUserInfoData();
})
const loadUserInfoData = async () => {
if (isLogin) {
const { data } = await getBbsUserProfile(currentUserInfo.id);
userInfo.value = data;
}
}
const blocks = [{ padding: '13px', background: '#617df2' }];
const prizes = [
{ fonts: [{ text: '啥也没有', top: '10%' }], background: '#e9e8fe' },
{ fonts: [{ text: '10', top: '10%' }], background: '#b8c5f2' },
{ fonts: [{ text: '30', top: '10%' }], background: '#e9e8fe' },
{ fonts: [{ text: '50', top: '10%' }], background: '#b8c5f2' },
{ fonts: [{ text: '80', top: '10%' }], background: '#e9e8fe' },
{ fonts: [{ text: '100', top: '10%' }], background: '#b8c5f2' },
{ fonts: [{ text: '150', top: '10%' }], background: '#e9e8fe' },
{ fonts: [{ text: '200', top: '10%' }], background: '#b8c5f2' },
{ fonts: [{ text: '300', top: '10%' }], background: '#e9e8fe' },
{ fonts: [{ text: '666', top: '10%' }], background: '#FAD400' }
];
const buttons = [{
radius: '35%',
background: '#8a9bf3',
pointer: true,
fonts: [{ text: '启动', top: '-10px' }]
}];
const myLucky = ref(null);
// 点击抽奖按钮会触发star回调
const startCallback = () => {
var index =0;
// 调用抽奖组件的play方法开始游戏
if (!isLogin) {
ElMessage({
message: '请登录后启动!',
type: 'warning',
})
return;
}
ElMessageBox.confirm(
'每次启动需要消耗50钱钱确定要启动吗?',
'警告',
{
confirmButtonText: '启动',
cancelButtonText: '放弃',
type: 'warning',
}
)
.then(async () => {
myLucky.value.play()
//等待3秒
// 模拟调用接口异步抽奖
setTimeout(() => {
// 假设后端返回的中奖索引是0
// 调用stop停止旋转并传递中奖索引
}, 3000)
index= (await luckyWheel()).data;
myLucky.value.stop(index)
})
.catch(() => {
ElMessage({
type: 'info',
message: '成功克制',
})
})
};
// 抽奖结束会触发end回调
const endCallback = async (prize) => {
ElMessage({
type: 'success',
message: `恭喜你抽中了[${prize.fonts[0].text}]`,
})
await loadUserInfoData();
}
</script>
<style scoped>
.wheel {
margin: auto;
margin-top: 10%;
}
h2 {
text-align: center;
}
h3 {
text-align: center;
}
</style>

View File

@@ -34,6 +34,7 @@
import { onMounted, ref, reactive, computed, nextTick, watch } from "vue";
import { signIn, signInRecord } from "@/apis/integralApi.js";
import useAuths from "@/hooks/useAuths";
const { isLogin } = useAuths();
const number=ref(0);
const signInData=ref([]);

View File

@@ -286,6 +286,15 @@
"@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14"
"@lucky-canvas/vue@^0.1.11":
"integrity" "sha512-5vm0txSKRBtMgrE/HZEvw1joSTx9NTdAkc8tBp/aX0LxyhQtiTVBLsRgdYUK/OiURCL8bo+046BTGnV+Q4JFlg=="
"resolved" "https://registry.npmmirror.com/@lucky-canvas/vue/-/vue-0.1.11.tgz"
"version" "0.1.11"
dependencies:
"@vue/composition-api" "^1.0.0"
"lucky-canvas" "^1.7.23"
"vue-demi" "^0.7.4"
"@microsoft/signalr@^8.0.0":
"integrity" "sha512-K/wS/VmzRWePCGqGh8MU8OWbS1Zvu7DG7LSJS62fBB8rJUXwwj4axQtqrAAwKGUZHQF6CuteuQR9xMsVpM2JNA=="
"resolved" "https://registry.npmmirror.com/@microsoft/signalr/-/signalr-8.0.0.tgz"
@@ -475,6 +484,11 @@
"@vue/compiler-dom" "3.2.47"
"@vue/shared" "3.2.47"
"@vue/composition-api@^1.0.0", "@vue/composition-api@^1.0.0-beta.1":
"integrity" "sha512-M8jm9J/laYrYT02665HkZ5l2fWTK4dcVg3BsDHm/pfz+MjDYwX+9FUaZyGwEyXEDonQYRCo0H7aLgdklcIELjw=="
"resolved" "https://registry.npmmirror.com/@vue/composition-api/-/composition-api-1.7.2.tgz"
"version" "1.7.2"
"@vue/devtools-api@^6.4.5", "@vue/devtools-api@^6.5.0":
"integrity" "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
"resolved" "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.5.0.tgz"
@@ -1499,6 +1513,11 @@
dependencies:
"yallist" "^4.0.0"
"lucky-canvas@^1.7.23":
"integrity" "sha512-Ftz6qD+863bI7xijBmZg3dw3cNEc7odPr70EZQcGA14y3TgTAzH65HPosOCd6kKUlMwhntBaHMx3onoj9MtJRQ=="
"resolved" "https://registry.npmmirror.com/lucky-canvas/-/lucky-canvas-1.7.27.tgz"
"version" "1.7.27"
"magic-string@^0.25.7":
"integrity" "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ=="
"resolved" "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz"
@@ -2239,6 +2258,11 @@
"resolved" "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz"
"version" "0.13.11"
"vue-demi@^0.7.4":
"integrity" "sha512-eFSQSvbQdY7C9ujOzvM6tn7XxwLjn0VQDXQsiYBLBwf28Na+2nTQR4BBBcomhmdP6mmHlBKAwarq6a0BPG87hQ=="
"resolved" "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.7.5.tgz"
"version" "0.7.5"
"vue-router@^4.1.6":
"integrity" "sha512-DYWYwsG6xNPmLq/FmZn8Ip+qrhFEzA14EI12MsMgVxvHFDYvlr4NXpVF5hrRH1wVcDP8fGi5F4rxuJSl8/r+EQ=="
"resolved" "https://registry.npmmirror.com/vue-router/-/vue-router-4.1.6.tgz"
@@ -2246,7 +2270,7 @@
dependencies:
"@vue/devtools-api" "^6.4.5"
"vue@^2.6.14 || ^3.2.0", "vue@^3.0.0-0 || ^2.6.0", "vue@^3.2.0", "vue@^3.2.23", "vue@^3.2.25", "vue@^3.2.47", "vue@2 || 3", "vue@3.2.47":
"vue@^2.0.0 || >=3.0.0-rc.0", "vue@^2.6.0 || >=3.0.0-rc.1", "vue@^2.6.14 || ^3.2.0", "vue@^3.0.0-0 || ^2.6.0", "vue@^3.2.0", "vue@^3.2.23", "vue@^3.2.25", "vue@^3.2.47", "vue@>= 2.5 < 2.7", "vue@2 || 3", "vue@3.2.47":
"integrity" "sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ=="
"resolved" "https://registry.npmmirror.com/vue/-/vue-3.2.47.tgz"
"version" "3.2.47"

View File

@@ -0,0 +1,41 @@
## 模块与项目
意框架是一个基于Abp.VNext的框架完美使用模块化理念进行开发。既然是模块化开发就有`项目``项目`的概念。
很多人问到了我,我的代码应该放到哪里,其实是没有分清楚模块与项目,弄清楚这个,再下手撸码,才更有方向。
其实不必一堆概念,只需要心中思考一下
> 我接下来要开发的业务是否有必要被其他项目依赖使用?
例如内置的Rbac模块、Bbs模块单独具备权限管理和社区论坛的功能公开给大家复用引用这就可以当成一个模块
`但是`,有时候如果我不认为他有必要复用,就是公司的一个内部系统,想要快速开发并交付,要同时具备权限管理、商城、审批等各种功能,那就直接当一个项目开发即可,不一定需要将各个模块拆的太散
> 这个划分的界限,是由项目来决定
注意:模块的代码与项目的代码结构几乎没有区别
这意味,有一个最好的方式,先将业务全部写在项目中,等稳定之后,在复制抽象到模块中,完全没有问题。
### 模块的优缺点
- 优点:高度抽象复用
- 缺点:肉眼可见的程序集增加、维护成本更高
### 模块
![Alt text](image.png)
代码位置放在`module`目录下,单独建立一个自己的模块名称,可使用`脚手架使用`将默认代码生成在这里。
### 项目
![Alt text](image-1.png)
框架内置一个host项目代码位置存放在`src`中,直接使用即可,如果想更换命名,可以使用上一节的`脚手架使用`
## 总结
> 由于很多人询问我这个问题,所以单独写一篇,方便大家理解。
先区分模块还是项目,然后代码写到对应的位置即可
理论上,按这套规则意味着,只有在自己的`module``src`下才需要写代码,其他地方都是内置好了的,通过继承、实现等方式扩展即可
当然,你可以不按这套规则出发,目录结构按自己的舒服的方式去设计,当然也是可以的,不过这得花费一些时间了。

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,34 @@
## 简介
上节说到真正关注的代码其实只用在src中
![image.png](/prod-api/file/7f52e9a7-6b6d-cc80-64f6-3a10031e9214/true)
当我们要新建一个项目或者模块的时候,并不想取这个`Yi.Abp`的名字我们可以使用dotnet的脚手架进行模板的生成
**环境**只需要dotnet sdk即可
## 步骤
![image.png](/prod-api/file/2a8d18c4-4899-433d-3553-3a10032e9645/true)
在模板文件目录上,有一个`.template.config`文件夹,就是通过这个进行控制模板的生成及替换
进入该目录使用cmd执行以以下命令
``` shell
dotnet new install . --force
```
![image.png](/prod-api/file/0a92e464-13af-ef93-77b9-3a10032fe20c/true)
出现 `已安装以下模板` 及代表安装完成
>我们只需要安装一次即可,如果模板文件发生变化,我们生成出来的文件也是一样会变化的
当模板安装完成,接下来我们可以选择一个自己想要的空白文件夹,输入命令:
``` shell
dotnet new yi --name=Acme.BookStore
#Acme.BookStore可以替换成你想要生成的名称即可
```
![image.png](/prod-api/file/55536e5a-ef47-1593-3701-3a100333df31/true)
最后查看目录:
![image.png](/prod-api/file/20286d62-a9d1-6ab0-7fc0-3a1003348554/true)
这个便是我们想要的结果了

View File

@@ -35,15 +35,15 @@ export function getCacheValue(cacheName, cacheKey) {
// 清理指定名称缓存
export function clearCacheName(cacheName) {
return request({
url: '/monitor/cache/clearCacheName/' + cacheName,
url: '/monitor-cache/key/' + cacheName,
method: 'delete'
})
}
// 清理指定键名缓存
export function clearCacheKey(cacheKey) {
export function clearCacheKey(cacheName,cacheKey) {
return request({
url: '/monitor/cache/clearCacheKey/' + cacheKey,
url: '/monitor-cache/value/'+cacheName+'/' + cacheKey,
method: 'delete'
})
}
@@ -51,7 +51,7 @@ export function clearCacheKey(cacheKey) {
// 清理全部缓存
export function clearCacheAll() {
return request({
url: '/monitor/cache/clearCacheAll',
url: '/monitor-cache/clear',
method: 'delete'
})
}

View File

@@ -3,16 +3,16 @@ import request from '@/utils/request'
// 查询公告列表
export function listNotice(query) {
return request({
url: '/system/notice/list',
url: '/notice',
method: 'get',
params: query
})
}
// 查询公告详细
export function getNotice(noticeId) {
export function getNotice(id) {
return request({
url: '/system/notice/' + noticeId,
url: `/notice/${id}`,
method: 'get'
})
}
@@ -20,26 +20,41 @@ export function getNotice(noticeId) {
// 新增公告
export function addNotice(data) {
return request({
url: '/system/notice',
url: '/notice',
method: 'post',
data: data
})
}
// 修改公告
export function updateNotice(data) {
export function updateNotice(id,data) {
return request({
url: '/system/notice',
url: `/notice/${id}`,
method: 'put',
data: data
})
}
// 删除公告
export function delNotice(noticeId) {
export function delNotice(ids) {
return request({
url: '/system/notice',
url: `/notice`,
method: 'delete',
params:{id:noticeId}
params:{id:ids}
})
}
// 发送在线公告
export function sendOnlineNotice(id) {
return request({
url: '/notice/online/'+id,
method: 'post',
})
}
// 发送离线公告
export function sendOfflineNotice(id) {
return request({
url: '/notice/offline/'+id,
method: 'post',
})
}

View File

@@ -1,7 +1,7 @@
<template>
<section class="app-main">
<router-view v-slot="{ Component, route }">
<transition name="fade-transform" mode="out-in">
<transition name="slide-fade" mode="out-in">
<keep-alive :include="tagsViewStore.cachedViews">
<component v-if="!route.meta.link" :is="Component" :key="route.path"/>
</keep-alive>
@@ -19,6 +19,24 @@ const tagsViewStore = useTagsViewStore()
</script>
<style lang="scss" scoped>
/*
进入和离开动画可以使用不同
持续时间和速度曲线。
*/
.slide-fade-enter-active {
transition: all 0.3s ease-out;
}
.slide-fade-leave-active {
transition: all 0.3s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter-from,
.slide-fade-leave-to {
transform: translateX(20px);
opacity: 0;
}
.app-main {
/* 50= navbar 50 */
min-height: calc(100vh - 50px);

View File

@@ -374,7 +374,8 @@ const handleCodeToWeb = async () => {
/** CodeToWeb */
const handleWebToCode = async () => {
const response= await webToCode(ids.value);
if(response.statusCode==200)
console.log(response,"response");
if(response.status==200||response.status==204)
{
proxy.$modal.msgSuccess("代码生成成功");
}

View File

@@ -207,7 +207,7 @@ function refreshCacheKeys() {
/** 清理指定键名缓存 */
function handleClearCacheKey(cacheKey) {
clearCacheKey(cacheKey).then(response => {
clearCacheKey(nowCacheName.value,cacheKey).then(response => {
proxy.$modal.msgSuccess("清理缓存键名[" + cacheKey + "]成功");
getCacheKeys();
});

View File

@@ -1,24 +1,16 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="公告标题" prop="noticeTitle">
<el-form-item label="公告标题" prop="title">
<el-input
v-model="queryParams.noticeTitle"
v-model="queryParams.title"
placeholder="请输入公告标题"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="操作人员" prop="createBy">
<el-input
v-model="queryParams.createBy"
placeholder="请输入操作人员"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="类型" prop="noticeType">
<el-select v-model="queryParams.noticeType" placeholder="公告类型" clearable>
<el-form-item label="类型" prop="type">
<el-select v-model="queryParams.type" placeholder="公告类型" clearable>
<el-option
v-for="dict in sys_notice_type"
:key="dict.value"
@@ -68,31 +60,47 @@
<el-table v-loading="loading" :data="noticeList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" align="center" prop="noticeId" width="100" />
<el-table-column label="序号" align="center" prop="id" width="300" />
<el-table-column
label="公告标题"
align="center"
prop="noticeTitle"
prop="title"
:show-overflow-tooltip="true"
/>
<el-table-column label="公告类型" align="center" prop="noticeType" width="100">
<el-table-column label="公告类型" align="center" prop="type" width="100">
<template #default="scope">
<dict-tag :options="sys_notice_type" :value="scope.row.noticeType" />
<dict-tag :options="sys_notice_type" :value="scope.row.type" />
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status" width="100">
<el-table-column label="状态" align="center" prop="state" width="100">
<template #default="scope">
<dict-tag :options="sys_notice_status" :value="scope.row.status" />
<el-tag type="info">{{sys_notice_state.filter(x=>x.value==scope.row.state)[0]?.label}}</el-tag>
</template>
</el-table-column>
<el-table-column label="创建" align="center" prop="createBy" width="100" />
<el-table-column label="创建时间" align="center" prop="createTime" width="100">
<el-table-column label="创建时间" align="center" prop="creationTime" width="100">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
<span>{{ parseTime(scope.row.creationTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button
type="text"
icon="Promotion"
@click="handleOnlineSend(scope.row.id)"
v-hasPermi="['system:notice:edit']"
>在线发送</el-button>
<el-button
type="text"
icon="Promotion"
@click="handleOfflineSend(scope.row.id)"
v-hasPermi="['system:notice:edit']"
>离线发送</el-button>
<el-button
type="text"
icon="Edit"
@@ -122,13 +130,13 @@
<el-form ref="noticeRef" :model="form" :rules="rules" label-width="80px">
<el-row>
<el-col :span="12">
<el-form-item label="公告标题" prop="noticeTitle">
<el-input v-model="form.noticeTitle" placeholder="请输入公告标题" />
<el-form-item label="公告标题" prop="title">
<el-input v-model="form.title" placeholder="请输入公告标题" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="公告类型" prop="noticeType">
<el-select v-model="form.noticeType" placeholder="请选择">
<el-form-item label="公告类型" prop="type">
<el-select v-model="form.type" placeholder="请选择">
<el-option
v-for="dict in sys_notice_type"
:key="dict.value"
@@ -140,9 +148,9 @@
</el-col>
<el-col :span="24">
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio-group v-model="form.state">
<el-radio
v-for="dict in sys_notice_status"
v-for="dict in sys_notice_state"
:key="dict.value"
:label="dict.value"
>{{ dict.label }}</el-radio>
@@ -155,7 +163,7 @@
:rows="6"
type="textarea"
placeholder="请输入内容"
v-model="form.noticeContent"
v-model="form.content"
/>
</el-form-item>
</el-col>
@@ -172,11 +180,17 @@
</template>
<script setup name="Notice">
import { listNotice, getNotice, delNotice, addNotice, updateNotice } from "@/api/system/notice";
import { sendOnlineNotice,sendOfflineNotice,listNotice, getNotice, delNotice, addNotice, updateNotice } from "@/api/system/notice";
const { proxy } = getCurrentInstance();
const { sys_notice_status, sys_notice_type } = proxy.useDict("sys_notice_status", "sys_notice_type");
const sys_notice_state=[
{label:'启用',value:true},
{label:'停用',value:false}
];
const sys_notice_type=[
{label:'走马灯',value:'MerryGoRound'},
{label:'提示弹窗',value:'Popup'}
];
const noticeList = ref([]);
const open = ref(false);
const loading = ref(true);
@@ -192,13 +206,13 @@ const data = reactive({
queryParams: {
skipCount: 1,
maxResultCount: 10,
noticeTitle: undefined,
title: undefined,
createBy: undefined,
status: undefined
state: undefined
},
rules: {
noticeTitle: [{ required: true, message: "公告标题不能为空", trigger: "blur" }],
noticeType: [{ required: true, message: "公告类型不能为空", trigger: "change" }]
title: [{ required: true, message: "公告标题不能为空", trigger: "blur" }],
type: [{ required: true, message: "公告类型不能为空", trigger: "change" }]
},
});
@@ -208,8 +222,8 @@ const { queryParams, form, rules } = toRefs(data);
function getList() {
loading.value = true;
listNotice(queryParams.value).then(response => {
noticeList.value = response.rows;
total.value = response.total;
noticeList.value = response.data.items;
total.value = response.data.totalCount;
loading.value = false;
});
}
@@ -221,11 +235,11 @@ function cancel() {
/** 表单重置 */
function reset() {
form.value = {
noticeId: undefined,
noticeTitle: undefined,
noticeType: undefined,
noticeContent: undefined,
status: "0"
id: undefined,
title: undefined,
type: undefined,
content: undefined,
state: true
};
proxy.resetForm("noticeRef");
}
@@ -241,7 +255,7 @@ function resetQuery() {
}
/** 多选框选中数据 */
function handleSelectionChange(selection) {
ids.value = selection.map(item => item.noticeId);
ids.value = selection.map(item => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
@@ -254,8 +268,8 @@ function handleAdd() {
/**修改按钮操作 */
function handleUpdate(row) {
reset();
const noticeId = row.noticeId || ids.value;
getNotice(noticeId).then(response => {
const id = row.id || ids.value;
getNotice(id).then(response => {
form.value = response.data;
open.value = true;
title.value = "修改公告";
@@ -265,12 +279,13 @@ function handleUpdate(row) {
function submitForm() {
proxy.$refs["noticeRef"].validate(valid => {
if (valid) {
if (form.value.noticeId != undefined) {
updateNotice(form.value).then(response => {
if (form.value.id != undefined) {
updateNotice(form.value.id,form.value).then(response => {
proxy.$modal.msgSuccess("修改成功");
open.value = false;
getList();
});
} else {
addNotice(form.value).then(response => {
proxy.$modal.msgSuccess("新增成功");
@@ -283,14 +298,23 @@ function submitForm() {
}
/** 删除按钮操作 */
function handleDelete(row) {
const noticeIds = row.noticeId || ids.value
proxy.$modal.confirm('是否确认删除公告编号为"' + noticeIds + '"的数据项?').then(function() {
return delNotice(noticeIds);
const ids2 = row.id || ids.value
proxy.$modal.confirm('是否确认删除公告编号为"' + ids2 + '"的数据项?').then(function() {
return delNotice(ids2);
}).then(() => {
getList();
proxy.$modal.msgSuccess("删除成功");
}).catch(() => {});
}
const handleOnlineSend=async (id)=>{
await sendOnlineNotice(id);
proxy.$modal.msgSuccess("在线消息发送成功");
}
const handleOfflineSend=async (id)=>{
await sendOfflineNotice(id);
proxy.$modal.msgSuccess("离线消息发送成功");
}
getList();
</script>

View File

@@ -211,11 +211,11 @@ const deptRef = ref(null);
/** 数据范围选项*/
const dataScopeOptions = ref([
{ value: 0, label: "全部数据权限" },
{ value: 1, label: "自定数据权限" },
{ value: 2, label: "本部门数据权限" },
{ value: 3, label: "本部门及以下数据权限" },
{ value: 4, label: "仅本人数据权限" },
{ value: 'ALL', label: "全部数据权限" },
{ value: 'CUSTOM', label: "自定数据权限" },
{ value: 'DEPT', label: "本部门数据权限" },
{ value: 'DEPT_FOLLOW', label: "本部门及以下数据权限" },
{ value: 'USER', label: "仅本人数据权限" },
]);
const data = reactive({