网站首页 网站源码
using Dpz.Core.Public.Entity.Option;
using Dpz.Core.Public.ViewModel.Option;
using ZiggyCreatures.Caching.Fusion;
namespace Dpz.Core.Service.RepositoryServiceImpl;
public class AppOptionService(
IRepository<AppOption> repository,
IFusionCache fusionCache,
IMapper mapper
) : AbstractCacheService(fusionCache), IAppOptionService
{
/// <summary>
/// 友情链接 Option name
/// </summary>
private const string FriendOptionName = "friends";
/// <summary>
/// 页脚内容 Option name
/// </summary>
private const string FooterContentOptionName = "footer";
/// <summary>
/// 账号锁定 Option name
/// </summary>
private const string AccountLockOptionName = "AccountLoginFailLock";
public async Task<IList<VmFriends>> GetFriendsAsync()
{
return await GetOrSetCacheAsync<List<VmFriends>>(
nameof(GetFriendsAsync),
async (_, cancellationToken) =>
{
var friends = await repository
.SearchFor(x => x.OptionName == FriendOptionName)
.ToListAsync(cancellationToken);
if (friends == null || friends.Count == 0)
{
return [];
}
var list = friends.Cast<Friends>().ToList();
return mapper.Map<List<VmFriends>>(list);
}
);
}
public async Task AddFriendAsync(VmFriends friend)
{
var entity = mapper.Map<Friends>(friend);
entity.CreateTime = DateTime.Now;
entity.LastUpdateTime = DateTime.Now;
entity.OptionName = FriendOptionName;
await repository.InsertAsync(entity);
await RemoveByMethodAsync(nameof(GetFriendsAsync));
}
public async Task EditFriendAsync(VmFriends? friend)
{
if (string.IsNullOrEmpty(friend?.Id) || !ObjectId.TryParse(friend.Id, out var oid))
{
return;
}
var entity = await repository.FindAsync(oid);
if (entity == null || entity is not Friends entityFriend)
{
return;
}
entityFriend.Avatar = friend.Avatar;
entityFriend.Description = friend.Description;
entityFriend.Link = friend.Link;
entityFriend.Name = friend.Name;
entityFriend.LastUpdateTime = DateTime.Now;
var updateResult = await repository.UpdateAsync(entityFriend);
if (updateResult is { IsAcknowledged: true, ModifiedCount: > 0 })
{
await RemoveByMethodAsync(nameof(GetFriendsAsync));
}
}
public async Task<VmAppOption?> FindAsync(string id)
{
var entity = await repository.TryGetAsync(id);
if (entity == null)
{
return null;
}
return mapper.Map<VmAppOption>(entity);
}
public async Task<VmSaltOption> GetSaltAsync(string account)
{
var entity = await repository
.Collection.Aggregate()
.Match(new BsonDocument { { "OptionName", "AccountSalt" } })
.Match(new BsonDocument { { "Account", account } })
.Match(
new BsonDocument
{
{
"Expire",
new BsonDocument { { "$gt", BsonDateTime.Create(DateTime.Now) } }
},
}
)
.FirstOrDefaultAsync();
if (entity is SaltOption saltOption)
{
return mapper.Map<VmSaltOption>(saltOption);
}
var salt = Guid.NewGuid().ToString("N");
var saltEntity = new SaltOption
{
Account = account,
CreateTime = DateTime.Now,
Expire = DateTime.Now.AddDays(7),
LastUpdateTime = DateTime.Now,
OptionName = "AccountSalt",
Salt = salt,
};
await repository.InsertAsync(saltEntity);
return mapper.Map<VmSaltOption>(saltEntity);
}
public async Task SaveFooterContentAsync(string content)
{
var footer = await repository
.SearchFor(x => x.OptionName == FooterContentOptionName)
.FirstOrDefaultAsync();
if (footer != null)
{
var set = new BsonDocument
{
{
"$set",
new BsonDocument
{
{ "Content", new BsonString(content) },
{ "LastUpdateTime", new BsonDateTime(DateTime.Now) },
}
},
};
UpdateDefinition<AppOption> update = new BsonDocumentUpdateDefinition<AppOption>(set);
var updateResult = await repository.UpdateAsync(x => x.Id == footer.Id, update);
if (updateResult is { IsAcknowledged: true, ModifiedCount: > 0 })
{
await RemoveByMethodAsync(nameof(GetFooterContentAsync));
}
return;
}
var entity = new FooterContent
{
Content = content,
CreateTime = DateTime.Now,
LastUpdateTime = DateTime.Now,
OptionName = FooterContentOptionName,
};
await repository.InsertAsync(entity);
await RemoveByMethodAsync(nameof(GetFooterContentAsync));
}
public async Task<string> GetFooterContentAsync()
{
return await GetOrSetCacheAsync<string>(
nameof(GetFooterContentAsync),
async (_, cancellationToken) =>
{
var footer = await repository
.SearchFor(x => x.OptionName == FooterContentOptionName)
.FirstOrDefaultAsync(cancellationToken);
return footer is FooterContent entity ? entity.Content : "";
}
);
}
public async Task LoginFailAsync(string account)
{
if (
await repository
.Collection.Aggregate()
.Match(new BsonDocument { { "OptionName", AccountLockOptionName } })
.Match(new BsonDocument { { "Account", account } })
.FirstOrDefaultAsync()
is AccountLock accountLock
)
{
// 如果锁定时间过期,那么重置
if (
accountLock.Expired.HasValue
&& DateTime.Now > accountLock.Expired.Value.ToLocalTime()
)
{
accountLock.Expired = null;
accountLock.AttemptNumber = 1;
accountLock.LastUpdateTime = DateTime.Now;
await repository.UpdateAsync(accountLock);
return;
}
// 上次操作超过间隔时间,那么重置尝试次数
if (DateTime.Now > accountLock.LastUpdateTime.ToLocalTime().AddHours(1))
{
accountLock.AttemptNumber = 1;
}
// 没有超过间隔时间,尝试次数累加
else
{
accountLock.AttemptNumber++;
}
accountLock.LastUpdateTime = DateTime.Now;
// 超过尝试次数,则锁定,设定锁定时间
if (accountLock.AttemptNumber > 3)
{
accountLock.Expired = DateTime.Now.AddDays(1);
}
await repository.UpdateAsync(accountLock);
return;
}
var entity = new AccountLock
{
Account = account,
AttemptNumber = 1,
CreateTime = DateTime.Now,
LastUpdateTime = DateTime.Now,
OptionName = AccountLockOptionName,
};
await repository.InsertAsync(entity);
}
public async Task LoginSuccessfulAsync(string account)
{
if (
await repository
.Collection.Aggregate()
.Match(new BsonDocument { { "OptionName", AccountLockOptionName } })
.Match(new BsonDocument { { "Account", account } })
.FirstOrDefaultAsync()
is AccountLock accountLock
)
{
accountLock.AttemptNumber = 0;
accountLock.Expired = null;
accountLock.LastUpdateTime = DateTime.Now;
await repository.UpdateAsync(accountLock);
}
}
public async Task<bool> IsAccountLockAsync(string account)
{
var entity = await repository
.Collection.Aggregate()
.Match(new BsonDocument { { "OptionName", AccountLockOptionName } })
.Match(new BsonDocument { { "Account", account } })
.FirstOrDefaultAsync();
return entity is AccountLock { Expired: { } } accountLock
&& accountLock.Expired.Value.ToLocalTime() > DateTime.Now;
}
private const string SettingOptionName = "Application.Setting";
public async Task<VmSetting?> GetSettingAsync()
{
var option = await repository
.SearchFor(x => x.OptionName == SettingOptionName)
.SingleOrDefaultAsync();
if (option is Setting setting)
{
return mapper.Map<VmSetting>(setting);
}
return null;
}
public async Task SaveSettingAsync(VmSetting setting)
{
var option = await repository
.SearchFor(x => x.OptionName == SettingOptionName)
.SingleOrDefaultAsync();
if (option is Setting entitySetting)
{
entitySetting.Api = setting.Api;
entitySetting.Cdn = setting.Cdn;
entitySetting.Core = setting.Core;
entitySetting.Image = setting.Image;
entitySetting.Job = setting.Job;
entitySetting.Main = setting.Main;
entitySetting.Message = setting.Message;
entitySetting.Script = setting.Script;
entitySetting.Static = setting.Static;
entitySetting.LastUpdateTime = DateTime.Now;
await repository.UpdateAsync(entitySetting);
return;
}
var entity = mapper.Map<Setting>(setting);
entity.OptionName = SettingOptionName;
entity.CreateTime = DateTime.Now;
entity.LastUpdateTime = DateTime.Now;
await repository.InsertAsync(entity);
}
public async Task DeleteFriendAsync(string id)
{
if (ObjectId.TryParse(id, out var oid))
{
var deleteResult = await repository.DeleteAsync(x =>
x.OptionName == "Friends" && x.Id == oid
);
if (deleteResult is { IsAcknowledged: true, DeletedCount: > 0 })
{
await RemoveCacheAsync(nameof(GetFriendsAsync));
}
}
}
protected override string CachePrefixKey =>
"Dpz.Core.Service.RepositoryServiceImpl.AppOptionService";
protected override TimeSpan CacheDefaultExpiration => TimeSpan.FromDays(30);
}
上述代码定义了一个名为 AppOptionService
的服务类,主要用于管理应用程序的选项和配置。该类实现了 IAppOptionService
接口,并依赖于一个通用的 IRepository<AppOption>
接口来进行数据存取操作。以下是该类的主要功能和方法的详细解释:
管理友情链接:
GetFriendsAsync
:异步获取所有的友情链接,返回一个 VmFriends
的列表。AddFriendAsync
:异步添加一个新的友情链接。EditFriendAsync
:异步编辑现有的友情链接。管理应用程序设置:
GetSettingAsync
:异步获取应用程序的设置,返回一个 VmSetting
对象。SaveSettingAsync
:异步保存应用程序的设置。管理页脚内容:
GetFooterContentAsync
:异步获取页脚内容。SaveFooterContentAsync
:异步保存页脚内容,并将其缓存。账号锁定管理:
LoginFailAsync
:记录登录失败的尝试,并在超过最大尝试次数后锁定账号。LoginSuccessfulAsync
:在成功登录后重置账号的锁定状态。IsAccountLockAsync
:检查账号是否被锁定。盐值管理:
GetSaltAsync
:获取与特定账号相关的盐值,如果不存在则生成一个新的盐值并保存。删除操作:
DeleteAsync
:根据 ID 删除特定的选项。IRepository<AppOption>
、IHybridCachingProvider
和 IMapper
的实例,以便在类中使用。async
和 await
关键字,以提高性能和响应性。GetFriendsAsync
:从数据库中查找所有名称为 "friends" 的选项,并将其映射为 VmFriends
对象列表。AddFriendAsync
:将传入的 VmFriends
对象映射为 Friends
实体,并插入到数据库中。EditFriendAsync
:根据 ID 查找现有的 Friends
实体,并更新其属性。FindAsync
:根据 ID 查找特定的选项并返回映射后的 VmAppOption
对象。GetSaltAsync
:查找与账号相关的盐值,如果不存在则生成新的盐值并保存。SaveFooterContentAsync
:保存页脚内容到数据库,并设置缓存。GetFooterContentAsync
:获取页脚内容。LoginFailAsync
:记录登录失败的尝试,并在超过最大尝试次数后锁定账号。LoginSuccessfulAsync
:重置账号的锁定状态。IsAccountLockAsync
:检查账号是否被锁定。GetSettingAsync
:获取应用程序的设置。SaveSettingAsync
:保存应用程序的设置。DeleteAsync
:根据 ID 删除特定的选项。AppOptionService
类提供了一系列方法来管理应用程序的选项和配置,包括友情链接、页脚内容、账号锁定、盐值和应用程序设置等。通过使用异步编程模式,该类能够高效地处理数据库操作,并提供良好的用户体验。