Merge branch 'refs/heads/abp-dev' into abp

This commit is contained in:
橙子
2024-08-15 21:39:29 +08:00
38 changed files with 1330 additions and 242 deletions

View File

@@ -0,0 +1,44 @@
using Volo.Abp.Application.Dtos;
using Yi.Framework.Bbs.Domain.Shared.Enums;
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Assignment;
public class AssignmentDefineGetListOutputDto : EntityDto<Guid>
{
/// <summary>
/// 任务名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Remarks { get; set; }
/// <summary>
/// 任务类型
/// </summary>
public AssignmentTypeEnum AssignmentType { get; set; }
/// <summary>
/// 任务需求类型
/// </summary>
public AssignmentRequirementTypeEnum AssignmentRequirementType{ get; set; }
/// <summary>
/// 总共步骤数
/// </summary>
public int TotalStepNumber { get; set; }
/// <summary>
/// 前置任务id
/// </summary>
public Guid? PreAssignmentId { get; set; }
/// <summary>
/// 任务奖励的钱钱数量
/// </summary>
public decimal RewardsMoneyNumber { get; set; }
public int OrderNum { get; set; }
}

View File

@@ -4,5 +4,21 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Assignment;
public class AssignmentGetListInput
{
/// <summary>
/// 任务查询条件
/// </summary>
public AssignmentQueryStateEnum AssignmentQueryState { get; set; } = AssignmentQueryStateEnum.Progress;
}
public enum AssignmentQueryStateEnum
{
/// <summary>
/// 正在进行
/// </summary>
Progress,
/// <summary>
/// 已结束
/// </summary>
End
}

View File

@@ -1,8 +1,54 @@
using Volo.Abp.Application.Dtos;
using Yi.Framework.Bbs.Domain.Shared.Enums;
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Assignment;
public class AssignmentGetListOutputDto:EntityDto<Guid>
{
/// <summary>
/// 任务名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Remarks { get; set; }
/// <summary>
/// 当前步骤数
/// </summary>
public int CurrentStepNumber { get; set; }
/// <summary>
/// 总共步骤数
/// </summary>
public int TotalStepNumber { get; set; }
/// <summary>
/// 任务类型
/// </summary>
public AssignmentTypeEnum AssignmentType { get; set; }
/// <summary>
/// 任务需求类型
/// </summary>
public AssignmentRequirementTypeEnum AssignmentRequirementType{ get; set; }
/// <summary>
/// 任务状态
/// </summary>
public AssignmentStateEnum AssignmentState { get; set; }
/// <summary>
/// 任务奖励的钱钱数量
/// </summary>
public decimal RewardsMoneyNumber { get; set; }
/// <summary>
/// 任务过期时间
/// </summary>
public DateTime? ExpireTime { get; set; }
public DateTime? CompleteTime { get; set; }
public DateTime CreationTime { get; set; }
public int OrderNum { get; set; }
}

View File

@@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Builder;
namespace Yi.Framework.Bbs.Application.Extensions;
public static class AccessLogExtensions
{
public static IApplicationBuilder UseAccessLog(this IApplicationBuilder app)
{
app.UseMiddleware<AccessLogMiddleware>();
return app;
}
}

View File

@@ -0,0 +1,51 @@
using FreeRedis;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Yi.Framework.Bbs.Domain.Shared.Caches;
namespace Yi.Framework.Bbs.Application.Extensions;
/// <summary>
/// 访问日志中间件
/// 并发最高采用缓存默认10分钟才会真正操作一次数据库
/// 需考虑一致性问题,又不能上锁影响性能
/// </summary>
public class AccessLogMiddleware : IMiddleware, ITransientDependency
{
/// <summary>
/// 缓存前缀
/// </summary>
private string CacheKeyPrefix => LazyServiceProvider.LazyGetRequiredService<IOptions<AbpDistributedCacheOptions>>()
.Value.KeyPrefix;
/// <summary>
/// 使用懒加载防止报错
/// </summary>
private IRedisClient RedisClient => LazyServiceProvider.LazyGetRequiredService<IRedisClient>();
/// <summary>
/// 属性注入
/// </summary>
public IAbpLazyServiceProvider LazyServiceProvider { get; set; }
private bool EnableRedisCache
{
get
{
var redisEnabled = LazyServiceProvider.LazyGetRequiredService<IConfiguration>()["Redis:IsEnabled"];
return redisEnabled.IsNullOrEmpty() || bool.Parse(redisEnabled);
}
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
await next(context);
if (EnableRedisCache)
{
await RedisClient.IncrByAsync($"{CacheKeyPrefix}:{AccessLogCacheConst.Key}:{DateTime.Now.Date}", 1);
}
}
}

View File

@@ -0,0 +1,87 @@
using FreeRedis;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Quartz;
using Volo.Abp.BackgroundWorkers.Quartz;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities;
using Yi.Framework.Bbs.Domain.Entities;
using Yi.Framework.Bbs.Domain.Shared.Caches;
using Yi.Framework.Bbs.Domain.Shared.Enums;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Bbs.Application.Jobs;
public class AccessLogStoreJob : QuartzBackgroundWorkerBase
{
private readonly ISqlSugarRepository<AccessLogAggregateRoot> _repository;
/// <summary>
/// 缓存前缀
/// </summary>
private string CacheKeyPrefix => LazyServiceProvider.LazyGetRequiredService<IOptions<AbpDistributedCacheOptions>>()
.Value.KeyPrefix;
/// <summary>
/// 使用懒加载防止报错
/// </summary>
private IRedisClient RedisClient => LazyServiceProvider.LazyGetRequiredService<IRedisClient>();
/// <summary>
/// 属性注入
/// </summary>
public IAbpLazyServiceProvider LazyServiceProvider { get; set; }
private bool EnableRedisCache
{
get
{
var redisEnabled = LazyServiceProvider.LazyGetRequiredService<IConfiguration>()["Redis:IsEnabled"];
return redisEnabled.IsNullOrEmpty() || bool.Parse(redisEnabled);
}
}
public AccessLogStoreJob(ISqlSugarRepository<AccessLogAggregateRoot> repository)
{
_repository = repository;
JobDetail = JobBuilder.Create<AccessLogStoreJob>().WithIdentity(nameof(AccessLogStoreJob))
.Build();
//每分钟执行一次
Trigger = TriggerBuilder.Create().WithIdentity(nameof(AccessLogStoreJob))
.WithCronSchedule("0 * * * * ?")
.Build();
}
public override async Task Execute(IJobExecutionContext context)
{
if (EnableRedisCache)
{
//当天的访问量
var number =
await RedisClient.GetAsync<long>($"{CacheKeyPrefix}:{AccessLogCacheConst.Key}:{DateTime.Now.Date}");
var entity = await _repository._DbQueryable.Where(x => x.AccessLogType == AccessLogTypeEnum.Request)
.Where(x => x.CreationTime.Date == DateTime.Today)
.FirstAsync();
if (entity is not null)
{
entity.Number = number+1;
await _repository.UpdateAsync(entity);
}
else
{
await _repository.InsertAsync((new AccessLogAggregateRoot() { Number = number,AccessLogType = AccessLogTypeEnum.Request}));
}
//删除前一天的缓存
await RedisClient.DelAsync($"{CacheKeyPrefix}:{AccessLogCacheConst.Key}:{DateTime.Now.Date.AddDays(-1)}");
}
}
}

View File

@@ -4,6 +4,7 @@ using Volo.Abp.Application.Services;
using Yi.Framework.Bbs.Application.Contracts.Dtos.AccessLog;
using Yi.Framework.Bbs.Application.Contracts.IServices;
using Yi.Framework.Bbs.Domain.Entities;
using Yi.Framework.Bbs.Domain.Shared.Enums;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Bbs.Application.Services
@@ -11,7 +12,11 @@ namespace Yi.Framework.Bbs.Application.Services
public class AccessLogService : ApplicationService, IAccessLogService
{
private readonly ISqlSugarRepository<AccessLogAggregateRoot> _repository;
public AccessLogService(ISqlSugarRepository<AccessLogAggregateRoot> repository) { _repository = repository; }
public AccessLogService(ISqlSugarRepository<AccessLogAggregateRoot> repository)
{
_repository = repository;
}
public DateTime GetWeekFirst()
{
@@ -45,16 +50,15 @@ namespace Yi.Framework.Bbs.Application.Services
}
/// <summary>
/// 获取全部访问流量(3个月)
/// </summary>
/// <param name="AccessLogType"></param>
/// <returns></returns>
public async Task<List<AccessLogDto>> Get()
public async Task<List<AccessLogDto>> GetListAsync([FromQuery] AccessLogTypeEnum accessLogType)
{
var entities = await _repository._DbQueryable
.Where(x=>x.CreationTime>=DateTime.Now.AddMonths(-3))
var entities = await _repository._DbQueryable.Where(x => x.AccessLogType == accessLogType)
.Where(x => x.CreationTime >= DateTime.Now.AddMonths(-3))
.OrderBy(x => x.CreationTime).ToListAsync();
var output = entities.Adapt<List<AccessLogDto>>();
output?.ForEach(x => x.CreationTime = x.CreationTime.Date);
@@ -62,32 +66,35 @@ namespace Yi.Framework.Bbs.Application.Services
}
/// <summary>
/// 触发
/// 首页点击触发
/// </summary>
/// <returns></returns>
[HttpPost("access-log")]
public async Task AccessAsync()
{
//可判断http重复防止同一ip多次访问
var last = await _repository._DbQueryable.OrderByDescending(x => x.CreationTime).FirstAsync();
var last = await _repository._DbQueryable.Where(x=>x.AccessLogType==AccessLogTypeEnum.HomeClick).OrderByDescending(x => x.CreationTime).FirstAsync();
if (last is null || last.CreationTime.Date != DateTime.Today)
{
await _repository.InsertAsync(new AccessLogAggregateRoot());
await _repository.InsertAsync(new AccessLogAggregateRoot(){AccessLogType=AccessLogTypeEnum.HomeClick});
}
else
{
await _repository._Db.Updateable<AccessLogAggregateRoot>().SetColumns(it => it.Number == it.Number + 1).Where(it => it.Id == last.Id).ExecuteCommandAsync();
await _repository._Db.Updateable<AccessLogAggregateRoot>().SetColumns(it => it.Number == it.Number + 1)
.Where(it => it.Id == last.Id).ExecuteCommandAsync();
}
}
/// <summary>
/// 获取当前周数据
/// 获取当前周首页点击数据
/// </summary>
/// <returns></returns>
public async Task<AccessLogDto[]> GetWeekAsync()
public async Task<AccessLogDto[]> GetWeekAsync([FromQuery] AccessLogTypeEnum accessLogType)
{
var lastSeven = await _repository._DbQueryable.OrderByDescending(x => x.CreationTime).ToPageListAsync(1, 7);
var lastSeven = await _repository._DbQueryable
.Where(x => x.AccessLogType == accessLogType)
.OrderByDescending(x => x.CreationTime).ToPageListAsync(1, 7);
return WeekTimeHandler(lastSeven.ToArray());
}
@@ -99,7 +106,8 @@ namespace Yi.Framework.Bbs.Application.Services
/// <returns></returns>
private AccessLogDto[] WeekTimeHandler(AccessLogAggregateRoot[] data)
{
data = data.Where(x => x.CreationTime >= GetWeekFirst()).OrderByDescending(x => x.CreationTime).DistinctBy(x => x.CreationTime.DayOfWeek).ToArray();
data = data.Where(x => x.CreationTime >= GetWeekFirst()).OrderByDescending(x => x.CreationTime)
.DistinctBy(x => x.CreationTime.DayOfWeek).ToArray();
Dictionary<DayOfWeek, AccessLogDto> processedData = new Dictionary<DayOfWeek, AccessLogDto>();
@@ -117,8 +125,8 @@ namespace Yi.Framework.Bbs.Application.Services
// 如果当天有数据则更新字典中的值为对应的Number
var sss = data.Adapt<AccessLogDto>();
processedData[dayOfWeek] = item.Adapt<AccessLogDto>();
}
var result = processedData.Values.ToList();
//此时的时间是周日-周一-周二,需要处理
@@ -128,8 +136,5 @@ namespace Yi.Framework.Bbs.Application.Services
return result.ToArray();
}
}
}
}

View File

@@ -1,10 +1,12 @@
using Microsoft.AspNetCore.Authorization;
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Users;
using Yi.Framework.Bbs.Application.Contracts.Dtos.Assignment;
using Yi.Framework.Bbs.Domain.Entities.Assignment;
using Yi.Framework.Bbs.Domain.Managers;
using Yi.Framework.Bbs.Domain.Shared.Enums;
namespace Yi.Framework.Bbs.Application.Services;
@@ -26,26 +28,55 @@ public class AssignmentService : ApplicationService
/// </summary>
/// <param name="id"></param>
[HttpPost("assignment/accept/{id}")]
public async Task AcceptAsync(Guid id)
public async Task AcceptAsync([FromRoute] Guid id)
{
await _assignmentManager.AcceptAsync(CurrentUser.GetId(), id);
}
/// <summary>
/// 接收任务奖励
/// 领取任务奖励
/// </summary>
/// <param name="id"></param>
[HttpPost("assignment/receive-rewards/{id}")]
public async Task ReceiveRewardsAsync(Guid id)
[HttpPost("assignment/complete/{id}")]
public async Task ReceiveRewardsAsync([FromRoute] Guid id)
{
await _assignmentManager.ReceiveRewardsAsync(id);
}
/// <summary>
/// 查任务
/// 查看可接受的任务
/// </summary>
public async Task<PagedResultDto<AssignmentGetListOutputDto>> GetListAsync(AssignmentGetListInput input)
/// <returns></returns>
[HttpGet("assignment/receive")]
public async Task<List<AssignmentDefineGetListOutputDto>> GetCanReceiveListAsync()
{
throw new NotImplementedException();
var entities = await _assignmentManager.GetCanReceiveListAsync(CurrentUser.GetId());
var output = entities.Adapt<List<AssignmentDefineGetListOutputDto>>();
return output;
}
/// <summary>
/// 查询接受的任务
/// </summary>
[HttpGet("assignment")]
public async Task<List<AssignmentGetListOutputDto>> GetListAsync([FromQuery] AssignmentGetListInput input)
{
var output = await _assignmentManager._assignmentRepository._DbQueryable
.Where(x => x.UserId == CurrentUser.GetId())
.WhereIF(input.AssignmentQueryState == AssignmentQueryStateEnum.Progress,
x => x.AssignmentState == AssignmentStateEnum.Progress||
x.AssignmentState == AssignmentStateEnum.Completed)
.WhereIF(input.AssignmentQueryState == AssignmentQueryStateEnum.End,
x => x.AssignmentState == AssignmentStateEnum.End ||
x.AssignmentState == AssignmentStateEnum.Expired)
.OrderBy(x=>x.CreationTime)
.LeftJoin<AssignmentDefineAggregateRoot>((x, define) => x.AssignmentDefineId==define.Id)
.Select((x, define) => new AssignmentGetListOutputDto
{
Id = x.Id
},true)
.ToListAsync();
return output;
}
}

View File

@@ -0,0 +1,20 @@
namespace Yi.Framework.Bbs.Domain.Shared.Caches;
public class AccessLogCacheItem
{
public AccessLogCacheItem(long number)
{
Number = number;
}
public long Number { get; set; }
public DateTime LastModificationTime { get; set; }=DateTime.Now;
public DateTime LastInsertTime { get; set; }=DateTime.Now;
}
public class AccessLogCacheConst
{
public const string Key = "AccessLog";
}

View File

@@ -0,0 +1,14 @@
namespace Yi.Framework.Bbs.Domain.Shared.Enums;
public enum AccessLogTypeEnum
{
/// <summary>
/// 首页点击量
/// </summary>
HomeClick,
/// <summary>
/// 接口请求量
/// </summary>
Request
}

View File

@@ -0,0 +1,28 @@
namespace Yi.Framework.Bbs.Domain.Shared.Enums;
public enum AssignmentRequirementTypeEnum
{
/// <summary>
/// 主题
/// </summary>
Discuss=2,
/// <summary>
/// 评论
/// </summary>
Comment=4,
/// <summary>
/// 点赞
/// </summary>
Agree=8,
/// <summary>
/// 更新个人中心
/// </summary>
UpdateProfile=16
}

View File

@@ -15,5 +15,10 @@ public enum AssignmentStateEnum
/// <summary>
/// 已过期
/// </summary>
Expired
Expired,
/// <summary>
/// 已结束
/// </summary>
End
}

View File

@@ -0,0 +1,30 @@
using Yi.Framework.Bbs.Domain.Shared.Enums;
namespace Yi.Framework.Bbs.Domain.Shared.Etos;
public class AssignmentEventArgs
{
public AssignmentEventArgs(AssignmentRequirementTypeEnum requirementType, Guid currentUserId,object? args=null)
{
RequirementType = requirementType;
Args = args;
CurrentUserId = currentUserId;
}
/// <summary>
/// 任务需求类型
/// </summary>
public AssignmentRequirementTypeEnum RequirementType { get; set; }
/// <summary>
/// 任务参数,可空,只需要一个触发点即可
/// </summary>
public object? Args { get; set; }
/// <summary>
/// 当前用户id
/// </summary>
public Guid CurrentUserId { get; set; }
}

View File

@@ -1,16 +1,19 @@
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
using Yi.Framework.Bbs.Domain.Shared.Enums;
namespace Yi.Framework.Bbs.Domain.Entities
{
[SugarTable("AccessLog")]
public class AccessLogAggregateRoot : AggregateRoot<Guid>, IHasModificationTime, IHasCreationTime
public class AccessLogAggregateRoot : AggregateRoot<Guid>, IHasCreationTime,IHasModificationTime
{
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true)]
public override Guid Id { get; protected set; }
public long Number { get; set; }
public DateTime? LastModificationTime { get; set; }
public AccessLogTypeEnum AccessLogType { get; set; }
public DateTime CreationTime { get; set; }
public DateTime? LastModificationTime { get; set; }
}
}

View File

@@ -44,32 +44,45 @@ public class AssignmentAggregateRoot : AggregateRoot<Guid>, IHasCreationTime, IO
/// 任务奖励的钱钱数量
/// </summary>
public decimal RewardsMoneyNumber { get; set; }
/// <summary>
/// 任务过期时间
/// </summary>
public DateTime? ExpireTime { get; set; }
public DateTime? CompleteTime { get; set; }
public DateTime CreationTime { get; }
/// <summary>
/// 任务需求类型
/// </summary>
public AssignmentRequirementTypeEnum AssignmentRequirementType { get; set; }
public DateTime? EndTime { get; set; }
public DateTime CreationTime { get; set; }
public int OrderNum { get; set; }
public DateTime? LastModificationTime { get; }
public DateTime? LastModificationTime { get; set; }
public bool IsAllowCompleted()
{
return AssignmentState == AssignmentStateEnum.Progress && this.CurrentStepNumber == this.TotalStepNumber;
return AssignmentState == AssignmentStateEnum.Completed && this.CurrentStepNumber == this.TotalStepNumber;
}
public bool TrySetExpire()
{
if (ExpireTime<=DateTime.Now)
if (ExpireTime <= DateTime.Now)
{
//现在时间已经大于过期时间
AssignmentState = AssignmentStateEnum.Expired;
return false;
return true;
}
return true;
return false;
}
public void SetEnd()
{
this.AssignmentState = AssignmentStateEnum.End;
this.EndTime = DateTime.Now;
}
}

View File

@@ -31,6 +31,11 @@ public class AssignmentDefineAggregateRoot: AggregateRoot<Guid>, IHasCreationTim
/// </summary>
public AssignmentTypeEnum AssignmentType{ get; set; }
/// <summary>
/// 任务需求类型
/// </summary>
public AssignmentRequirementTypeEnum AssignmentRequirementType{ get; set; }
/// <summary>
/// 总共步骤数
/// </summary>
@@ -46,6 +51,6 @@ public class AssignmentDefineAggregateRoot: AggregateRoot<Guid>, IHasCreationTim
/// </summary>
public decimal RewardsMoneyNumber { get; set; }
public DateTime CreationTime { get; }
public DateTime CreationTime{ get; set; }
public int OrderNum { get; set; }
}

View File

@@ -5,6 +5,7 @@ using Volo.Abp.EventBus.Local;
using Yi.Framework.Bbs.Domain.Entities;
using Yi.Framework.Bbs.Domain.Entities.Forum;
using Yi.Framework.Bbs.Domain.Shared.Consts;
using Yi.Framework.Bbs.Domain.Shared.Enums;
using Yi.Framework.Bbs.Domain.Shared.Etos;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions;
@@ -14,20 +15,24 @@ namespace Yi.Framework.Bbs.Domain.EventHandlers
/// <summary>
/// 被点赞
/// </summary>
public class AgreeCreatedEventHandler : ILocalEventHandler<EntityCreatedEventData<AgreeEntity>>,
ITransientDependency
public class AgreeChangeEventHandler : ILocalEventHandler<EntityCreatedEventData<AgreeEntity>>,
ITransientDependency
{
private ISqlSugarRepository<UserAggregateRoot> _userRepository;
private ISqlSugarRepository<BbsUserExtraInfoEntity> _userInfoRepository;
private ISqlSugarRepository<AgreeEntity> _agreeRepository;
private ILocalEventBus _localEventBus;
public AgreeCreatedEventHandler(ISqlSugarRepository<BbsUserExtraInfoEntity> userInfoRepository, ISqlSugarRepository<AgreeEntity> agreeRepository, ILocalEventBus localEventBus, ISqlSugarRepository<UserAggregateRoot> userRepository)
public AgreeChangeEventHandler(ISqlSugarRepository<BbsUserExtraInfoEntity> userInfoRepository,
ISqlSugarRepository<AgreeEntity> agreeRepository, ILocalEventBus localEventBus,
ISqlSugarRepository<UserAggregateRoot> userRepository)
{
_userInfoRepository = userInfoRepository;
_agreeRepository = agreeRepository;
_localEventBus = localEventBus;
_userRepository = userRepository;
}
public async Task HandleEventAsync(EntityCreatedEventData<AgreeEntity> eventData)
{
var agreeEntity = eventData.Entity;
@@ -35,55 +40,66 @@ namespace Yi.Framework.Bbs.Domain.EventHandlers
//查询主题的信息
var discussAndAgreeDto = await _agreeRepository._DbQueryable
.LeftJoin<DiscussAggregateRoot>((agree, discuss) => agree.DiscussId == discuss.Id)
.Select((agree, discuss) =>
new
{
DiscussId=discuss.Id,
DiscussTitle = discuss.Title,
DiscussCreatorId = discuss.CreatorId,
})
.FirstAsync();
.Select((agree, discuss) =>
new
{
DiscussId = discuss.Id,
DiscussTitle = discuss.Title,
DiscussCreatorId = discuss.CreatorId,
})
.FirstAsync();
//查询点赞者用户
var agreeUser = await _userRepository.GetFirstAsync(x => x.Id == agreeEntity.CreatorId);
//给创建者点赞数量+1
await _userInfoRepository._Db.Updateable<BbsUserExtraInfoEntity>()
.SetColumns(it => it.AgreeNumber == it.AgreeNumber + 1)
.Where(it => it.UserId == discussAndAgreeDto.DiscussCreatorId)
.ExecuteCommandAsync();
.SetColumns(it => it.AgreeNumber == it.AgreeNumber + 1)
.Where(it => it.UserId == discussAndAgreeDto.DiscussCreatorId)
.ExecuteCommandAsync();
//通知主题作者,有人点赞
await _localEventBus.PublishAsync(new BbsNoticeEventArgs(discussAndAgreeDto.DiscussCreatorId!.Value, string.Format(DiscussConst.AgreeNotice, discussAndAgreeDto.DiscussTitle, agreeUser.UserName,discussAndAgreeDto.DiscussId)), false);
await _localEventBus.PublishAsync(
new BbsNoticeEventArgs(discussAndAgreeDto.DiscussCreatorId!.Value,
string.Format(DiscussConst.AgreeNotice, discussAndAgreeDto.DiscussTitle, agreeUser.UserName,
discussAndAgreeDto.DiscussId)), false);
//最后发布任务触发事件
await _localEventBus.PublishAsync(
new AssignmentEventArgs(AssignmentRequirementTypeEnum.Agree, agreeUser.Id),false);
}
}
/// <summary>
/// 取消点赞
/// </summary>
public class AgreeDeletedEventHandler : ILocalEventHandler<EntityDeletedEventData<AgreeEntity>>,
ITransientDependency
ITransientDependency
{
private ISqlSugarRepository<BbsUserExtraInfoEntity> _userRepository;
private ISqlSugarRepository<AgreeEntity> _agreeRepository;
private ILocalEventBus _localEventBus;
public AgreeDeletedEventHandler(ISqlSugarRepository<BbsUserExtraInfoEntity> userRepository, ISqlSugarRepository<AgreeEntity> agreeRepository, ILocalEventBus localEventBus)
public AgreeDeletedEventHandler(ISqlSugarRepository<BbsUserExtraInfoEntity> userRepository,
ISqlSugarRepository<AgreeEntity> agreeRepository, ILocalEventBus localEventBus)
{
_userRepository = userRepository;
_agreeRepository = agreeRepository;
_localEventBus = localEventBus;
}
public async Task HandleEventAsync(EntityDeletedEventData<AgreeEntity> eventData)
{
var agreeEntity = eventData.Entity;
var userId = await _agreeRepository._DbQueryable.LeftJoin<DiscussAggregateRoot>((agree, discuss) => agree.DiscussId == discuss.Id)
.Select((agree, discuss) => discuss.CreatorId).FirstAsync();
var userId = await _agreeRepository._DbQueryable
.LeftJoin<DiscussAggregateRoot>((agree, discuss) => agree.DiscussId == discuss.Id)
.Select((agree, discuss) => discuss.CreatorId).FirstAsync();
//给创建者点赞数量-1
await _userRepository._Db.Updateable<BbsUserExtraInfoEntity>()
.SetColumns(it => it.AgreeNumber == it.AgreeNumber - 1)
.Where(it => it.UserId == userId)
.ExecuteCommandAsync();
.SetColumns(it => it.AgreeNumber == it.AgreeNumber - 1)
.Where(it => it.UserId == userId)
.ExecuteCommandAsync();
}
}
}
}

View File

@@ -0,0 +1,82 @@
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus;
using Yi.Framework.Bbs.Domain.Entities.Assignment;
using Yi.Framework.Bbs.Domain.Shared.Enums;
using Yi.Framework.Bbs.Domain.Shared.Etos;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Bbs.Domain.EventHandlers;
/// <summary>
/// 任务系统的领域事件,处理不同任务触发变化
/// </summary>
public class AssignmentEventHandler : ILocalEventHandler<AssignmentEventArgs>, ITransientDependency
{
private readonly ISqlSugarRepository<AssignmentAggregateRoot> _repository;
public AssignmentEventHandler(ISqlSugarRepository<AssignmentAggregateRoot> repository)
{
_repository = repository;
}
public async Task HandleEventAsync(AssignmentEventArgs eventData)
{
var currentAssignmentList = await _repository.GetListAsync(x =>
x.AssignmentState == AssignmentStateEnum.Progress && x.UserId == eventData.CurrentUserId);
//如果有接收的任务
if (currentAssignmentList.Count > 0)
{
switch (eventData.RequirementType)
{
//发表主题
case AssignmentRequirementTypeEnum.Discuss:
SetCurrentStepNumber(AssignmentRequirementTypeEnum.Discuss, currentAssignmentList);
break;
//发表评论
case AssignmentRequirementTypeEnum.Comment:
SetCurrentStepNumber(AssignmentRequirementTypeEnum.Comment, currentAssignmentList);
break;
//点赞
case AssignmentRequirementTypeEnum.Agree:
SetCurrentStepNumber(AssignmentRequirementTypeEnum.Agree, currentAssignmentList);
break;
//更新个人信息
case AssignmentRequirementTypeEnum.UpdateProfile:
//这里还需判断是否更新了
break;
default:
throw new ArgumentOutOfRangeException();
}
//更新
await _repository.UpdateRangeAsync(currentAssignmentList);
}
}
/// <summary>
/// 设置当前进度
/// </summary>
/// <param name="requirementType"></param>
/// <param name="currentAssignmentList"></param>
private void SetCurrentStepNumber(AssignmentRequirementTypeEnum requirementType,
List<AssignmentAggregateRoot> currentAssignmentList)
{
currentAssignmentList.ForEach(x =>
{
if (x.AssignmentRequirementType == requirementType &&
x.CurrentStepNumber < x.TotalStepNumber)
{
x.CurrentStepNumber += 1;
if (x.CurrentStepNumber==x.TotalStepNumber)
{
x.AssignmentState = AssignmentStateEnum.Completed;
}
}
});
}
}

View File

@@ -6,6 +6,7 @@ using Volo.Abp.EventBus.Local;
using Yi.Framework.Bbs.Domain.Entities;
using Yi.Framework.Bbs.Domain.Entities.Forum;
using Yi.Framework.Bbs.Domain.Shared.Consts;
using Yi.Framework.Bbs.Domain.Shared.Enums;
using Yi.Framework.Bbs.Domain.Shared.Etos;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions;
@@ -62,6 +63,10 @@ namespace Yi.Framework.Bbs.Domain.EventHandlers
await _localEventBus.PublishAsync(new BbsNoticeEventArgs(commentEntity.ParentId, string.Format(DiscussConst.CommentNoticeToReply, disucssDto.DiscussTitle, commentUser.UserName, content,commentEntity.DiscussId)), false);
}
//最后发布任务触发事件
await _localEventBus.PublishAsync(
new AssignmentEventArgs(AssignmentRequirementTypeEnum.Comment, commentUser.Id),false);
}
}

View File

@@ -1,8 +1,11 @@
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus;
using Volo.Abp.EventBus.Local;
using Yi.Framework.Bbs.Domain.Entities;
using Yi.Framework.Bbs.Domain.Entities.Forum;
using Yi.Framework.Bbs.Domain.Shared.Enums;
using Yi.Framework.Bbs.Domain.Shared.Etos;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Bbs.Domain.EventHandlers
@@ -11,22 +14,31 @@ namespace Yi.Framework.Bbs.Domain.EventHandlers
/// 主题创建的领域事件
/// </summary>
public class DiscussCreatedEventHandler : ILocalEventHandler<EntityCreatedEventData<DiscussAggregateRoot>>,
ITransientDependency
ITransientDependency
{
private ISqlSugarRepository<BbsUserExtraInfoEntity> _userRepository;
public DiscussCreatedEventHandler(ISqlSugarRepository<BbsUserExtraInfoEntity> userRepository)
private ILocalEventBus _localEventBus;
public DiscussCreatedEventHandler(ISqlSugarRepository<BbsUserExtraInfoEntity> userRepository,
ILocalEventBus localEventBus)
{
_userRepository = userRepository;
_localEventBus = localEventBus;
}
public async Task HandleEventAsync(EntityCreatedEventData<DiscussAggregateRoot> eventData)
{
var disucussEntity = eventData.Entity;
//给创建者发布数量+1
await _userRepository._Db.Updateable<BbsUserExtraInfoEntity>()
.SetColumns(it => it.DiscussNumber == it.DiscussNumber + 1)
.Where(it => it.UserId == disucussEntity.CreatorId)
.ExecuteCommandAsync();
.SetColumns(it => it.DiscussNumber == it.DiscussNumber + 1)
.Where(it => it.UserId == disucussEntity.CreatorId)
.ExecuteCommandAsync();
//最后发布任务触发事件
await _localEventBus.PublishAsync(
new AssignmentEventArgs(AssignmentRequirementTypeEnum.Discuss, disucussEntity.CreatorId!.Value), false);
}
}
}
}

View File

@@ -16,8 +16,8 @@ namespace Yi.Framework.Bbs.Domain.Managers;
public class AssignmentManager : DomainService
{
private readonly IEnumerable<IAssignmentProvider> _assignmentProviders;
private readonly ISqlSugarRepository<AssignmentAggregateRoot> _assignmentRepository;
private readonly ISqlSugarRepository<AssignmentDefineAggregateRoot> _assignmentDefineRepository;
public readonly ISqlSugarRepository<AssignmentAggregateRoot> _assignmentRepository;
public readonly ISqlSugarRepository<AssignmentDefineAggregateRoot> _assignmentDefineRepository;
private readonly ILocalEventBus _localEventBus;
public AssignmentManager(IEnumerable<IAssignmentProvider> assignmentProviders,
@@ -52,6 +52,7 @@ public class AssignmentManager : DomainService
entity.CurrentStepNumber = 0;
entity.TotalStepNumber = assignmentDefine.TotalStepNumber;
entity.RewardsMoneyNumber = assignmentDefine.RewardsMoneyNumber;
entity.AssignmentRequirementType = assignmentDefine.AssignmentRequirementType;
entity.ExpireTime = assignmentDefine.AssignmentType.GetExpireTime();
await _assignmentRepository.InsertAsync(entity);
}
@@ -68,11 +69,18 @@ public class AssignmentManager : DomainService
var assignment = await _assignmentRepository.GetByIdAsync(asignmentId);
if (assignment.IsAllowCompleted())
{
//设置已完成,并领取奖励,钱钱
assignment.AssignmentState = AssignmentStateEnum.Completed;
//加钱加钱
await _localEventBus.PublishAsync(
new MoneyChangeEventArgs { UserId = assignment.UserId, Number = assignment.RewardsMoneyNumber }, false);
//设置已完成,并领取奖励,钱钱
assignment.SetEnd();
await _assignmentRepository.UpdateAsync(assignment);
}
else
{
//不能领取
throw new UserFriendlyException("该任务没有满足领取条件,请检查任务详情");
}
}
@@ -91,8 +99,8 @@ public class AssignmentManager : DomainService
output.AddRange(await assignmentProvider.GetCanReceiveListAsync(context));
}
output.DistinctBy(x => x.Id);
throw new NotImplementedException();
output = output.DistinctBy(x => x.Id).OrderBy(x => x.OrderNum).ToList();
return output;
}
@@ -134,6 +142,5 @@ public class AssignmentManager : DomainService
{
await _assignmentRepository._Db.Updateable(needUpdateEntities).ExecuteCommandAsync();
}
}
}

View File

@@ -1,10 +1,12 @@
using Yi.Framework.Bbs.Domain.Shared.Enums;
using Volo.Abp.DependencyInjection;
using Yi.Framework.Bbs.Domain.Shared.Enums;
namespace Yi.Framework.Bbs.Domain.Managers.AssignmentProviders;
/// <summary>
/// 每日任务提供者
/// </summary>
[ExposeServices(typeof(IAssignmentProvider))]
public class DailyProvider : TimerProvider
{
protected override AssignmentTypeEnum AssignmentType => AssignmentTypeEnum.Daily;

View File

@@ -5,11 +5,12 @@ namespace Yi.Framework.Bbs.Domain.Managers.AssignmentProviders;
/// <summary>
/// 新手任务提供者
/// </summary>
public class NoviceProvider : IAssignmentProvider
public class NoviceProvider : IAssignmentProvider
{
public Task<List<AssignmentDefineAggregateRoot>> GetCanReceiveListAsync(AssignmentContext context)
public async Task<List<AssignmentDefineAggregateRoot>> GetCanReceiveListAsync(AssignmentContext context)
{
//新手任务是要有前置依赖关系的,链表类型依赖
throw new NotImplementedException();
throw new NotImplementedException();
}
}

View File

@@ -1,10 +1,12 @@
using Yi.Framework.Bbs.Domain.Shared.Enums;
using Volo.Abp.DependencyInjection;
using Yi.Framework.Bbs.Domain.Shared.Enums;
namespace Yi.Framework.Bbs.Domain.Managers.AssignmentProviders;
/// <summary>
/// 每周任务提供者
/// </summary>
[ExposeServices(typeof(IAssignmentProvider))]
public class WeeklyProvider : TimerProvider
{
protected override AssignmentTypeEnum AssignmentType => AssignmentTypeEnum.Weekly;

View File

@@ -24,10 +24,11 @@ public abstract class TimerProvider : IAssignmentProvider
//2: 存在已完成,但是完成时间是过期的
var assignmentFilterIds = context.CurrentUserAssignments
.Where(x =>
//正在进行的,要去掉
//正在进行的,已经完成,要去掉
x.AssignmentState == AssignmentStateEnum.Progress||
//已完成,但是还没过期,要去掉
(x.AssignmentState == AssignmentStateEnum.Completed&&!AssignmentType.IsExpire(x.CompleteTime!.Value)))
x.AssignmentState==AssignmentStateEnum.Completed||
//已结束,但是还没过期,要去掉
(x.AssignmentState == AssignmentStateEnum.End&&!AssignmentType.IsExpire(x.EndTime!.Value)))
.Select(x => x.AssignmentDefineId)
.ToList();

View File

@@ -15,7 +15,7 @@ namespace Yi.Framework.Bbs.Domain.Managers
/// </summary>
public class BankManager : DomainService
{
private const decimal DefalutRate = 1.3m;
private const decimal DefalutRate = 1.2m;
private ISqlSugarRepository<BankCardAggregateRoot> _repository;
private ILocalEventBus _localEventBus;
private ISqlSugarRepository<InterestRecordsAggregateRoot> _interestRepository;

View File

@@ -19,8 +19,10 @@ namespace Yi.Framework.Bbs.Domain.Managers
private IDistributedCache<List<LevelCacheItem>> _levelCache;
private IRepository<LevelAggregateRoot> _repository;
private ISqlSugarRepository<BbsUserExtraInfoEntity> _bbsUserRepository;
public LevelManager( ILocalEventBus localEventBus,
IDistributedCache<List<LevelCacheItem>> levelCache, IRepository<LevelAggregateRoot> repository, ISqlSugarRepository<BbsUserExtraInfoEntity> bbsUserRepository)
public LevelManager(ILocalEventBus localEventBus,
IDistributedCache<List<LevelCacheItem>> levelCache, IRepository<LevelAggregateRoot> repository,
ISqlSugarRepository<BbsUserExtraInfoEntity> bbsUserRepository)
{
_localEventBus = localEventBus;
_repository = repository;
@@ -35,16 +37,16 @@ namespace Yi.Framework.Bbs.Domain.Managers
/// <returns></returns>
public async Task<Dictionary<int, LevelCacheItem>> GetCacheMapAsync()
{
var items = _levelCache.GetOrAdd(LevelConst.LevelCacheKey, () =>
var items = _levelCache.GetOrAdd(LevelConst.LevelCacheKey, () =>
{
var cacheItem = ( _repository.GetListAsync().Result)
var cacheItem = (_repository.GetListAsync().Result)
.OrderByDescending(x => x.CurrentLevel).ToList()
.Adapt<List<LevelCacheItem>>();
return cacheItem;
});
return items.ToDictionary(x=>x.CurrentLevel);
return items.ToDictionary(x => x.CurrentLevel);
}
/// <summary>
/// 使用钱钱投喂等级
/// </summary>
@@ -52,7 +54,7 @@ namespace Yi.Framework.Bbs.Domain.Managers
public async Task ChangeLevelByMoneyAsync(Guid userId, int moneyNumber)
{
//通过用户id获取用户信息的经验和等级
var userInfo = await _bbsUserRepository.GetAsync(x=>x.UserId==userId);
var userInfo = await _bbsUserRepository.GetAsync(x => x.UserId == userId);
//钱钱和经验的比例为11
//根据钱钱修改经验
@@ -63,7 +65,7 @@ namespace Yi.Framework.Bbs.Domain.Managers
false);
//更改最终的经验再变化等级
var levelList = (await GetCacheMapAsync()).Values;
var levelList = (await GetCacheMapAsync()).Values.OrderByDescending(x => x.CurrentLevel);
var currentNewLevel = 1;
foreach (var level in levelList)
{
@@ -73,12 +75,12 @@ namespace Yi.Framework.Bbs.Domain.Managers
break;
}
}
//这里注意,只更新等级
userInfo.Level = currentNewLevel;
userInfo.Experience = currentNewExperience;
await _bbsUserRepository._Db.Updateable(userInfo)
.UpdateColumns(it => new { it.Level,it.Experience })
.UpdateColumns(it => new { it.Level, it.Experience })
.ExecuteCommandAsync();
}
}

View File

@@ -7,6 +7,7 @@ using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json.Converters;
using Volo.Abp.AspNetCore.Authentication.JwtBearer;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.AntiForgery;
@@ -25,6 +26,7 @@ using Yi.Framework.AspNetCore.Authentication.OAuth.QQ;
using Yi.Framework.AspNetCore.Microsoft.AspNetCore.Builder;
using Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection;
using Yi.Framework.Bbs.Application;
using Yi.Framework.Bbs.Application.Extensions;
using Yi.Framework.ChatHub.Application;
using Yi.Framework.CodeGen.Application;
using Yi.Framework.Rbac.Application;
@@ -38,8 +40,6 @@ namespace Yi.Abp.Web
[DependsOn(
typeof(YiAbpSqlSugarCoreModule),
typeof(YiAbpApplicationModule),
typeof(AbpAspNetCoreMultiTenancyModule),
typeof(AbpAspNetCoreMvcModule),
typeof(AbpAutofacModule),
@@ -49,11 +49,11 @@ namespace Yi.Abp.Web
typeof(AbpAspNetCoreAuthenticationJwtBearerModule),
typeof(YiFrameworkAspNetCoreModule),
typeof(YiFrameworkAspNetCoreAuthenticationOAuthModule)
)]
)]
public class YiAbpWebModule : AbpModule
{
private const string DefaultCorsPolicyName = "Default";
public override Task ConfigureServicesAsync(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
@@ -68,15 +68,24 @@ namespace Yi.Abp.Web
optios.AlwaysLogSelectors.Add(x => Task.FromResult(true));
});
//配置错误处理显示详情
Configure<AbpExceptionHandlingOptions>(options => { options.SendExceptionsDetailsToClients = true; });
//动态Api
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(YiAbpApplicationModule).Assembly, options => options.RemoteServiceName = "default");
options.ConventionalControllers.Create(typeof(YiFrameworkRbacApplicationModule).Assembly, options => options.RemoteServiceName = "rbac");
options.ConventionalControllers.Create(typeof(YiFrameworkBbsApplicationModule).Assembly, options => options.RemoteServiceName = "bbs");
options.ConventionalControllers.Create(typeof(YiFrameworkChatHubApplicationModule).Assembly, options => options.RemoteServiceName = "chat-hub");
options.ConventionalControllers.Create(typeof(YiFrameworkTenantManagementApplicationModule).Assembly, options => options.RemoteServiceName = "tenant-management");
options.ConventionalControllers.Create(typeof(YiFrameworkCodeGenApplicationModule).Assembly, options => options.RemoteServiceName = "code-gen");
options.ConventionalControllers.Create(typeof(YiAbpApplicationModule).Assembly,
options => options.RemoteServiceName = "default");
options.ConventionalControllers.Create(typeof(YiFrameworkRbacApplicationModule).Assembly,
options => options.RemoteServiceName = "rbac");
options.ConventionalControllers.Create(typeof(YiFrameworkBbsApplicationModule).Assembly,
options => options.RemoteServiceName = "bbs");
options.ConventionalControllers.Create(typeof(YiFrameworkChatHubApplicationModule).Assembly,
options => options.RemoteServiceName = "chat-hub");
options.ConventionalControllers.Create(typeof(YiFrameworkTenantManagementApplicationModule).Assembly,
options => options.RemoteServiceName = "tenant-management");
options.ConventionalControllers.Create(typeof(YiFrameworkCodeGenApplicationModule).Assembly,
options => options.RemoteServiceName = "code-gen");
//统一前缀
options.ConventionalControllers.ConventionalControllerSettings.ForEach(x => x.RootPath = "api/app");
@@ -91,22 +100,20 @@ namespace Yi.Abp.Web
//设置缓存不要过期默认滑动20分钟
Configure<AbpDistributedCacheOptions>(cacheOptions =>
{
cacheOptions.GlobalCacheEntryOptions.SlidingExpiration = null;
//缓存key前缀
cacheOptions.KeyPrefix = "Yi:";
});
Configure<AbpAntiForgeryOptions>(options =>
{
options.AutoValidate = false;
cacheOptions.GlobalCacheEntryOptions.SlidingExpiration = null;
//缓存key前缀
cacheOptions.KeyPrefix = "Yi:";
});
Configure<AbpAntiForgeryOptions>(options => { options.AutoValidate = false; });
//Swagger
context.Services.AddYiSwaggerGen<YiAbpWebModule>(options =>
{
options.SwaggerDoc("default", new OpenApiInfo { Title = "Yi.Framework.Abp", Version = "v1", Description = "集大成者" });
options.SwaggerDoc("default",
new OpenApiInfo { Title = "Yi.Framework.Abp", Version = "v1", Description = "集大成者" });
});
//跨域
@@ -145,7 +152,7 @@ namespace Yi.Abp.Web
//每60秒限制100个请求滑块添加分6段
service.AddRateLimiter(_ =>
{
_.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
_.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
_.OnRejected = (context, _) =>
{
if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
@@ -153,6 +160,7 @@ namespace Yi.Abp.Web
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.");
@@ -161,20 +169,20 @@ namespace Yi.Abp.Web
//全局使用,链式表达式
_.GlobalLimiter = PartitionedRateLimiter.CreateChained(
PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
{
var userAgent = httpContext.Request.Headers.UserAgent.ToString();
PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
{
var userAgent = httpContext.Request.Headers.UserAgent.ToString();
return RateLimitPartition.GetSlidingWindowLimiter
(userAgent, _ =>
new SlidingWindowRateLimiterOptions
{
PermitLimit = 1000,
Window = TimeSpan.FromSeconds(60),
SegmentsPerWindow = 6,
QueueProcessingOrder = QueueProcessingOrder.OldestFirst
});
}));
return RateLimitPartition.GetSlidingWindowLimiter
(userAgent, _ =>
new SlidingWindowRateLimiterOptions
{
PermitLimit = 1000,
Window = TimeSpan.FromSeconds(60),
SegmentsPerWindow = 6,
QueueProcessingOrder = QueueProcessingOrder.OldestFirst
});
}));
});
@@ -196,14 +204,15 @@ namespace Yi.Abp.Web
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
if (!string.IsNullOrEmpty(accessToken))
{
context.Token = accessToken;
var accessToken = context.Request.Query["access_token"];
if (!string.IsNullOrEmpty(accessToken))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
return Task.CompletedTask;
}
};
})
.AddJwtBearer(TokenTypeConst.Refresh, options =>
@@ -214,37 +223,32 @@ namespace Yi.Abp.Web
ValidateIssuerSigningKey = true,
ValidIssuer = refreshJwtOptions.Issuer,
ValidAudience = refreshJwtOptions.Audience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(refreshJwtOptions.SecurityKey))
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))
{
context.Token = refresh_token;
var refresh_token = context.Request.Headers["refresh_token"];
if (!string.IsNullOrEmpty(refresh_token))
{
context.Token = refresh_token;
return Task.CompletedTask;
}
var refreshToken = context.Request.Query["refresh_token"];
if (!string.IsNullOrEmpty(refreshToken))
{
context.Token = refreshToken;
}
return Task.CompletedTask;
}
var refreshToken = context.Request.Query["refresh_token"];
if (!string.IsNullOrEmpty(refreshToken))
{
context.Token = refreshToken;
}
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();
@@ -284,6 +288,9 @@ namespace Yi.Abp.Web
//swagger
app.UseYiSwagger();
//流量访问统计,需redis支持否则不生效
app.UseAccessLog();
//请求处理
app.UseYiApiHandlinge();
@@ -310,4 +317,4 @@ namespace Yi.Abp.Web
return Task.CompletedTask;
}
}
}
}

View File

@@ -9,16 +9,18 @@ export function access() {
}
// 获取本周数据
export function getWeek() {
export function getWeek(data) {
return request({
url: "/access-log/week",
method: "get",
params :data
});
}
// 获取全部数据
export function getAccessList() {
export function getAccessList(data) {
return request({
url: "/access-log",
method: "get",
params :data
});
}

View File

@@ -0,0 +1,33 @@
import request from "@/config/axios/service";
//接受任务
export function acceptAssignment(id) {
return request({
url: `/assignment/accept/${id}`,
method: "post"
});
}
//领取奖励
export function receiveAssignment(id) {
return request({
url: `/assignment/complete/${id}`,
method: "post",
});
}
//查询能够领取的任务
export function getCanReceiveAssignment() {
return request({
url: `/assignment/receive`,
method: "get",
});
}
//查询已领取的任务
export function getAssignmentList(data) {
return request({
url: `/assignment`,
method: "get",
params:data
});
}

View File

@@ -37,7 +37,14 @@ const onClickText=()=>{
.el-divider {
margin: 0.2rem 0;
}
.VisitsLineChart /deep/ .el-card__body{
padding: 0 20px;
}
.box-card-info {
width: 100%;
height: v-bind(height);
padding-bottom: 10px;
}
.card-header {
display: flex;
justify-content: space-between;
@@ -52,9 +59,4 @@ const onClickText=()=>{
margin: 1rem 0;
}
.box-card {
width: 100%;
height: v-bind(height);
padding-bottom: 10px;
}
</style>

View File

@@ -16,9 +16,9 @@
<el-icon>
<Memo />
</el-icon>
<span>任务列表(暂未开放)</span>
<span>任务列表</span>
</el-menu-item>
<el-menu-item index="4" :route="{ path: '/activity/lucky' }">
<el-menu-item index="4" :route="{ path: '/activity/assignment' }">
<el-icon>
<HelpFilled />
</el-icon>

View File

@@ -156,6 +156,14 @@ const router = createRouter({
title: "银行",
},
},
{
name: "assignment",
path: "assignment",
component: () => import("../views/assignment/Index.vue"),
meta: {
title: "任务",
},
},
],
},

View File

@@ -0,0 +1,115 @@
<script setup>
import {getAssignmentList, getCanReceiveAssignment, acceptAssignment, receiveAssignment} from '@/apis/assignmentApi'
import {onMounted, reactive, ref} from "vue";
import AssignmentCard from "./components/AssignmentCard.vue"
const canReceiveAssignmentList = ref([]);
const assignmentList = ref([]);
const queryForm = reactive({
assignmentQueryState: "Progress"
});
//当前选择table页
const currentTableSelect = ref("canAccept");
//切换tab
const changeClickTable = async (tabName) => {
switch (tabName) {
case "canAccept":
const {data: canReceiveAssignmentListData} = await getCanReceiveAssignment();
canReceiveAssignmentList.value = canReceiveAssignmentListData;
return;
case "progress":
queryForm.assignmentQueryState = "Progress";
break;
case "end":
queryForm.assignmentQueryState = "End";
break;
}
const {data} = await getAssignmentList(queryForm);
assignmentList.value = data;
}
onMounted(async () => {
const {data: canReceiveAssignmentListData} = await getCanReceiveAssignment();
canReceiveAssignmentList.value = canReceiveAssignmentListData;
});
//刷新数据
const refreshData = async () => {
const {data: canReceiveAssignmentListData} = await getCanReceiveAssignment();
canReceiveAssignmentList.value = canReceiveAssignmentListData;
const {data} = await getAssignmentList(queryForm);
assignmentList.value = data;
}
//接收任务
const onClickAcceptAssignment = async (item) => {
await acceptAssignment(item.id);
ElMessage({
type: 'success',
message: '接受任务成功',
});
await refreshData();
}
//领取奖励
const onClickReceiveAssignment = async (item) => {
await receiveAssignment(item.id);
ElMessage({
type: 'success',
message: '任务奖励领取成功',
});
await refreshData();
}
//切换tab
const changeTab = async (state) => {
queryForm.assignmentQueryState = state;
await refreshData();
}
</script>
<template>
<div class="content-body">
<el-tabs
v-model="currentTableSelect"
type="border-card"
@tab-change="changeClickTable"
>
<el-tab-pane label="可接受" name="canAccept"/>
<el-tab-pane label="已接受" name="progress"/>
<el-tab-pane label="已结束" name="end"/>
<div v-if="currentTableSelect==='canAccept'">
<div v-for="item in canReceiveAssignmentList" class="assign-box" v-if="canReceiveAssignmentList.length>0">
<AssignmentCard :isDefind="true" :data="item" @onClick="onClickAcceptAssignment"/>
</div>
<el-empty v-else description="暂时没有可领取的任务" />
</div>
<div v-else>
<div v-for="item in assignmentList" class="assign-box" v-if="assignmentList.length>0">
<AssignmentCard :isDefind="false" :data="item" @onClick="onClickReceiveAssignment"/>
</div>
<el-empty v-else description="暂时没有任务" />
</div>
</el-tabs>
</div>
</template>
<style scoped lang="scss">
.content-body {
padding: 30px;
.assign-box {
margin-bottom: 10px;
}
}
</style>

View File

@@ -0,0 +1,152 @@
<script setup>
import {computed, ref, watch} from "vue";
import { dayjs } from 'element-plus'
const props = defineProps(['data', 'isDefind'])
const cardData = ref(props.data);
const emit = defineEmits(['onClick'])
const onClick = () => {
emit('onClick', cardData.value)
}
//监视组件传值变化
watch(() => props.data, (n, o) => {
cardData.value = n;
})
//任务类型
const assignmentTypeEnum = {
"Daily": {name:"每日任务",backgroundColor:"#fff"},
"Weekly":{name:"每周任务",backgroundColor:"#fff"} ,
"Novice": {name:"新手任务",backgroundColor:"#fff"}
}
const computedAssignmentState = computed(() => {
if (props.isDefind) {
return btnAssignmentStateEnum["CanReceive"];
} else {
return btnAssignmentStateEnum[cardData.value.assignmentState];
}
});
const btnAssignmentStateEnum = {
"CanReceive": {name: "接受任务", backgroundColor: "primary",isDisabled:false},
"End": {name: "已完成", backgroundColor: "info",isDisabled:true},
"Progress": {name: "正在进行", backgroundColor: "Default",isDisabled:true},
"Completed": {name: "领取奖励", backgroundColor: "success",isDisabled:false},
"Expired": {name: "已过期", backgroundColor: "info",isDisabled:true}
}
</script>
<template>
<div class="card-box">
<div class="left">
<div class="left-type" :style="{backgroundColor:assignmentTypeEnum[cardData.assignmentType].backgroundColor}">{{ assignmentTypeEnum[cardData.assignmentType].name }}</div>
<div class="content">
<div class="content-title">
<h2>{{ cardData.name }}</h2>
<h4>{{ cardData.remarks }}</h4>
</div>
<div class="progress" v-if="props.isDefind===false">
<el-progress
:text-inside="true"
:stroke-width="20"
:percentage=" Math.round((cardData.currentStepNumber/cardData.totalStepNumber)*100)"
status="success"
/>
<span>{{cardData.currentStepNumber}}/{{cardData.totalStepNumber}}</span>
</div>
</div>
</div>
<div class="right">
<div class="right-btn">
<h5> {{cardData.expireTime ==null?"":"过期时间:"+dayjs(cardData.expireTime).format('YYYY年M月D日')}}</h5>
<h5>奖励<span style="color: #FF0000;font-weight: bolder ">{{cardData.rewardsMoneyNumber}}</span> 钱钱</h5>
<el-button @click="onClick()" :disabled="computedAssignmentState.isDisabled" :type="computedAssignmentState.backgroundColor">
{{ computedAssignmentState.name }}
</el-button>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.el-progress{
width: 450px;
}
.el-button {
width: 100px;
height: 40px;
margin-top: 5px;
}
h2 {
margin: 0 0 10px 0;
}
h4 {
margin: 0 0 0 20px;
display: flex;
align-items: center;
}
h5 {
margin: 0;
}
.card-box {
padding: 10px;
border: 2px solid #dcdfe6;
display: flex;
justify-content: space-between;
height: 100px;
width: 100%;
align-content: center;
flex-wrap: wrap;
.right {
display: flex;
align-content: center;
flex-wrap: wrap;
.right-btn {
text-align: right;
}
}
.left {
display: flex;
align-content: center;
flex-wrap: wrap;
.content {
margin-left: 30px;
.content-title{
display: flex;
}
.progress{
display: flex;
}
span{
margin: 0 10px;
}
}
.left-type {
border: 1px solid #dcdfe6;
height: 60px;
width: 100px;
display: flex;
align-content: center;
flex-wrap: wrap;
justify-content: center;
}
}
}
</style>

View File

@@ -3,10 +3,11 @@
<el-row :gutter="20" class="top-div">
<el-col :span="17">
<div class="chat-hub">
<p @click="onClickToChatHub">点击前往-最新上线<span>聊天室 </span>现已支持<span>Ai助手</span>希望能帮助大家</p>
<p @click="onClickToChatHub">点击前往-最新上线<span>聊天室 </span>现已支持<span>Ai助手</span>希望能帮助大家
</p>
</div>
<div class="scrollbar">
<ScrollbarInfo />
<ScrollbarInfo/>
</div>
<el-row class="left-div">
@@ -14,23 +15,23 @@
'padding-left': i % 3 == 1 ? 0 : 0.2 + 'rem',
'padding-right': i % 3 == 0 ? 0 : 0.2 + 'rem',
}">
<PlateCard :name="i.name" :introduction="i.introduction" :id="i.id" :isPublish="i.isDisableCreateDiscuss" />
<PlateCard :name="i.name" :introduction="i.introduction" :id="i.id" :isPublish="i.isDisableCreateDiscuss"/>
</el-col>
<template v-if="isDiscussFinished">
<el-col :span="24" v-for="i in discussList" :key="i.id">
<DisscussCard :discuss="i" />
<DisscussCard :discuss="i"/>
</el-col>
</template>
<template v-else>
<Skeleton :isBorder="true" />
<Skeleton :isBorder="true"/>
</template>
<template v-if="isAllDiscussFinished">
<el-col :span="24" v-for="i in allDiscussList" :key="i.id">
<DisscussCard :discuss="i" />
<DisscussCard :discuss="i"/>
</el-col>
</template>
<template v-else>
<Skeleton :isBorder="true" />
<Skeleton :isBorder="true"/>
</template>
</el-row>
</el-col>
@@ -42,7 +43,7 @@
<div class="carousel-font" :style="{ color: item.color }">
{{ item.name }}
</div>
<el-image style="width: 100%; height: 100%" :src="item.logo" fit="cover" />
<el-image style="width: 100%; height: 100%" :src="item.logo" fit="cover"/>
</el-carousel-item>
</el-carousel>
</el-col>
@@ -97,23 +98,26 @@
<el-col :span="24">
<InfoCard header="访问统计" class="VisitsLineChart" text="全站历史统计" @onClickText="onClickAccessLog">
<template #content>
<VisitsLineChart :option="statisOptions" class="statisChart" />
<p class="switch-span" @click="onClickWeekSwitch">切换</p>
<VisitsLineChart :option="statisOptions" class="statisChart"/>
</template>
</InfoCard>
<el-dialog v-model="accessLogDialogVisible" title="全站历史统计" width="1200px" center>
<el-tabs v-model="accessLogTab">
<el-tab-pane label="访问统计近3月" name="AccessLogChart" style="display: flex;justify-content: center;">
<AccessLogChart :option="accessLogOptins" style="height: 600px;width: 1200px;" />
<el-tab-pane label="访问统计近3月" name="AccessLogChart"
style="display: flex;justify-content: center;">
<AccessLogChart :option="accessLogOptins" style="height: 600px;width: 1200px;"/>
</el-tab-pane>
<el-tab-pane label="注册统计近3月" name="RegisterChart" style="display: flex;justify-content: center;">
<AccessLogChart :option="registerLogOptins" style="height: 600px;width: 1200px;" />
<el-tab-pane label="注册统计近3月" name="RegisterChart"
style="display: flex;justify-content: center;">
<AccessLogChart :option="registerLogOptins" style="height: 600px;width: 1200px;"/>
</el-tab-pane>
</el-tabs>
</el-dialog>
</el-col>
@@ -129,16 +133,17 @@
<el-col :span="24">
<template v-if="isPointFinished">
<InfoCard :items="pointList" header="财富排行榜" text="查看我的位置" height="400" @onClickText="onClickMoneyTop">
<InfoCard :items="pointList" header="财富排行榜" text="查看我的位置" height="400"
@onClickText="onClickMoneyTop">
<template #item="temp">
<PointsRanking :pointsData="temp" />
<PointsRanking :pointsData="temp"/>
</template>
</InfoCard>
</template>
<template v-else>
<InfoCard header="本月排行" text="更多">
<template #content>
<Skeleton />
<Skeleton/>
</template>
</InfoCard>
</template>
@@ -148,14 +153,14 @@
<template v-if="isFriendFinished">
<InfoCard :items="friendList" header="推荐好友" text="更多" height="400">
<template #item="temp">
<RecommendFriend :friendData="temp" />
<RecommendFriend :friendData="temp"/>
</template>
</InfoCard>
</template>
<template v-else>
<InfoCard header="推荐好友" text="更多">
<template #content>
<Skeleton />
<Skeleton/>
</template>
</InfoCard>
</template>
@@ -164,21 +169,21 @@
<template v-if="isThemeFinished">
<InfoCard :items="themeList" header="推荐主题" text="更多" height="400">
<template #item="temp">
<ThemeData :themeData="temp" />
<ThemeData :themeData="temp"/>
</template>
</InfoCard>
</template>
<template v-else>
<InfoCard header="推荐主题" text="更多">
<template #content>
<Skeleton />
<Skeleton/>
</template>
</InfoCard>
</template>
</el-col>
<el-col :span="24" style="background: transparent">
<BottomInfo />
<BottomInfo/>
</el-col>
</el-row>
</el-col>
@@ -187,8 +192,8 @@
</template>
<script setup>
import { onMounted, ref, reactive, computed, nextTick, watch } from "vue";
import { useRouter } from "vue-router";
import {onMounted, ref, reactive, computed, nextTick, watch} from "vue";
import {useRouter} from "vue-router";
import DisscussCard from "@/components/DisscussCard.vue";
import InfoCard from "@/components/InfoCard.vue";
import PlateCard from "@/components/PlateCard.vue";
@@ -196,11 +201,11 @@ import ScrollbarInfo from "@/components/ScrollbarInfo.vue";
import BottomInfo from "@/components/BottomInfo.vue";
import VisitsLineChart from "./components/VisitsLineChart/index.vue";
import AccessLogChart from "./components/AccessLogChart/Index.vue"
import { access, getAccessList } from "@/apis/accessApi.js";
import { getList } from "@/apis/plateApi.js";
import { getList as bannerGetList } from "@/apis/bannerApi.js";
import { getHomeDiscuss } from "@/apis/discussApi.js";
import { getWeek } from "@/apis/accessApi.js";
import {access, getAccessList} from "@/apis/accessApi.js";
import {getList} from "@/apis/plateApi.js";
import {getList as bannerGetList} from "@/apis/bannerApi.js";
import {getHomeDiscuss} from "@/apis/discussApi.js";
import {getWeek} from "@/apis/accessApi.js";
import {
getRecommendedTopic,
getRecommendedFriend,
@@ -208,7 +213,7 @@ import {
getUserAnalyse,
getRegisterAnalyse
} from "@/apis/analyseApi.js";
import { getList as getAllDiscussList } from "@/apis/discussApi.js";
import {getList as getAllDiscussList} from "@/apis/discussApi.js";
import PointsRanking from "./components/PointsRanking/index.vue";
import RecommendFriend from "./components/RecommendFriend/index.vue";
import ThemeData from "./components/RecommendTheme/index.vue";
@@ -238,15 +243,15 @@ const userAnalyseInfo = ref({});
const onlineNumber = ref(0);
const accessLogTab = ref()
const activeList = [
{ name: "签到", path: "/activity/sign", icon: "Present" },
{ name: "等级", path: "/activity/level", icon: "Ticket" },
{ name: "大转盘", path: "/activity/lucky", icon: "Sunny" },
{ name: "银行", path: "/activity/bank", icon: "CreditCard" },
{name: "签到", path: "/activity/sign", icon: "Present"},
{name: "等级", path: "/activity/level", icon: "Ticket"},
{name: "大转盘", path: "/activity/lucky", icon: "Sunny"},
{name: "银行", path: "/activity/bank", icon: "CreditCard"},
{ name: "任务", path: "/activity/sign", icon: "Memo" },
{ name: "排行榜", path: "/money", icon: "Money" },
{ name: "开始", path: "/start", icon: "Position" },
{ name: "聊天室", path: "/chat", icon: "ChatRound" },
{name: "任务", path: "/activity/assignment", icon: "Memo"},
{name: "排行榜", path: "/money", icon: "Money"},
{name: "开始", path: "/start", icon: "Position"},
{name: "聊天室", path: "/chat", icon: "ChatRound"},
];
//主题查询参数
@@ -256,37 +261,38 @@ const query = reactive({
isTop: true,
});
const weekQuery = reactive({accessLogType: "Request"});
//初始化
onMounted(async () => {
access();
const { data: plateData } = await getList();
const {data: plateData} = await getList();
plateList.value = plateData.items;
const { data: discussData, config: discussConfig } = await getHomeDiscuss();
const {data: discussData, config: discussConfig} = await getHomeDiscuss();
discussList.value = discussData;
isDiscussFinished.value = discussConfig.isFinish;
const { data: bannerData } = await bannerGetList();
const {data: bannerData} = await bannerGetList();
bannerList.value = bannerData.items;
const { data: weekData } = await getWeek();
const {data: weekData} = await getWeek(weekQuery);
weekList.value = weekData;
const { data: pointData, config: pointConfig } = await getRankingPoints();
const {data: pointData, config: pointConfig} = await getRankingPoints();
pointList.value = pointData.items;
isPointFinished.value = pointConfig.isFinish;
const { data: friendData, config: friendConfig } =
await getRecommendedFriend();
const {data: friendData, config: friendConfig} =
await getRecommendedFriend();
friendList.value = friendData;
isFriendFinished.value = friendConfig.isFinish;
const { data: themeData, config: themeConfig } = await getRecommendedTopic();
const {data: themeData, config: themeConfig} = await getRecommendedTopic();
themeList.value = themeData;
isThemeFinished.value = themeConfig.isFinish;
const { data: allDiscussData, config: allDiscussConfig } =
await getAllDiscussList({
Type: 0,
skipCount: 1,
maxResultCount: 30,
});
const {data: allDiscussData, config: allDiscussConfig} =
await getAllDiscussList({
Type: 0,
skipCount: 1,
maxResultCount: 30,
});
isAllDiscussFinished.value = allDiscussConfig.isFinish;
allDiscussList.value = allDiscussData.items;
const { data: userAnalyseInfoData } = await getUserAnalyse();
const {data: userAnalyseInfoData} = await getUserAnalyse();
onlineNumber.value = userAnalyseInfoData.onlineNumber;
userAnalyseInfo.value = userAnalyseInfoData;
});
@@ -361,29 +367,28 @@ const handleToRouter = (path) => {
// 推送的实时人数获取
const currentOnlineNum = computed(() => useSocketStore().getOnlineNum());
watch(
() => currentOnlineNum.value,
(val) => {
onlineNumber.value = val;
},
{ deep: true }
() => currentOnlineNum.value,
(val) => {
onlineNumber.value = val;
},
{deep: true}
);
watch(
() => accessLogTab.value,
async (value) => {
switch (value) {
case "AccessLogChart":
const { data } = await getAccessList();
accessAllList.value = data;
() => accessLogTab.value,
async (value) => {
switch (value) {
case "AccessLogChart":
const {data} = await getAccessList(weekQuery);
accessAllList.value = data;
break;
case "RegisterChart":
const { data: registerUserListData } = await getRegisterAnalyse();
registerAllList.value = registerUserListData;
break;
case "RegisterChart":
const {data: registerUserListData} = await getRegisterAnalyse();
registerAllList.value = registerUserListData;
break;
break;
}
}
}
)
const onClickAccessLog = async () => {
accessLogDialogVisible.value = true;
@@ -391,6 +396,17 @@ const onClickAccessLog = async () => {
}
//切换统计开关
const onClickWeekSwitch = async () => {
if (weekQuery.accessLogType === "HomeClick") {
weekQuery.accessLogType= "Request";
} else if (weekQuery.accessLogType === "Request") {
weekQuery.accessLogType = "HomeClick";
}
const {data: weekData} = await getWeek(weekQuery);
weekList.value = weekData;
}
</script>
<style scoped lang="scss">
.home-box {
@@ -535,10 +551,17 @@ const onClickAccessLog = async () => {
}
}
.VisitsLineChart>>>.el-card__body {
.VisitsLineChart > > > .el-card__body {
padding: 0.5rem;
}
.VisitsLineChart p{
display: flex;
justify-content: flex-end;
color: #409eff;
cursor: pointer;
margin-top: 8px;
}
.statisChart {
width: 100%;
height: 300px;
@@ -550,6 +573,7 @@ const onClickAccessLog = async () => {
}
}
//走马灯,聊天室链接
.chat-hub {
background-color: #E6A23C;

View File

@@ -0,0 +1,113 @@
<script setup>
import AwardPedestal from "./components/AwardPedestal.vue";
import AvatarInfo from "@/components/AvatarInfo.vue";
import { onMounted, reactive, ref, computed } from "vue";
import {
getRankingPoints,
} from "@/apis/analyseApi.js";
const pointList = ref([]);
const total = ref(0);
const moneyQuery = reactive({ skipCount: 1, maxResultCount: 30 });
const isFirstPage = computed(() => {
return moneyQuery.skipCount == 1;
})
const pointListFilter=computed(() => {
//如果是第一页去掉前3个
if(moneyQuery.skipCount == 1)
{
return pointList.value.slice(3);
}
return pointList.value;
})
//初始化
onMounted(async () => {
await initData();
});
const initData = async () => {
const { data: pointData } = await getRankingPoints(moneyQuery);
pointList.value = pointData.items;;
total.value = pointData.totalCount
}
//分页事件
const changePage = async (currentPage) => {
await initData();
}
</script>
<template>
<div class="content-body">
<AwardPedestal v-show="isFirstPage" :goldUserInfo="pointList[0]" :silverUserInfo="pointList[1]"
:bronzeUserInfo="pointList[2]" />
<div v-for="item in pointListFilter" :key="item.id" class="list-div">
<div class="list-left">
<span> {{ item.order }}</span>
<AvatarInfo :userInfo="item" :isSelf="false" />
<span class="money">
{{ item.money }}
</span>
</div>
<div class="list-right">
关注
</div>
</div>
<el-pagination background layout="total, sizes, prev, pager, next, jumper" :total="total"
:page-sizes="[10, 30, 50, 100]" v-model:current-page="moneyQuery.skipCount"
v-model:page-size="moneyQuery.maxResultCount" @current-change="changePage" @size-change="changePage" />
</div>
</template>
<style scoped lang="scss">
.el-pagination {
padding: 10px;
display: flex;
justify-content: center;
}
.content-body {
margin-bottom: 40px;
margin-top: 20px;
padding: 20px;
background-color: #ffffff;
}
.list-div {
justify-content: space-between;
border-radius: 4px;
display: flex;
background-color: #ffffff;
height: 80px;
width: 850px;
cursor: pointer;
padding: 16px 12px;
.list-left {
display: flex;
span {
margin-right: 20px;
color: #515767;
font-size: 1.5rem;
font-weight: 600;
line-height: 2rem;
display: flex;
align-content: center;
flex-wrap: wrap;
}
.money {
font-size: 1.0rem;
color: #ff0000;
}
}
}
.list-div:hover {
background-color: #f7f8fa;
}
</style>

View File

@@ -0,0 +1,92 @@
<script setup>
import AvatarInfo from "@/components/AvatarInfo.vue";
const props = defineProps([
"goldUserInfo",
"silverUserInfo",
"bronzeUserInfo",
]);
</script>
<template>
<div class="podium">
<div class="avatar">
<AvatarInfo :userInfo="silverUserInfo" :isSelf="true" />
<div class="tier silver">
<h2>第二</h2>
<h3>{{ silverUserInfo?.money }}</h3>
</div>
</div>
<div class="avatar">
<AvatarInfo :userInfo="goldUserInfo" :isSelf="true" :size="50" />
<div class="tier gold">
<h2>第一</h2>
<h3>{{ goldUserInfo?.money }}</h3>
</div>
</div>
<div class="avatar">
<AvatarInfo :userInfo="bronzeUserInfo" :isSelf="true" />
<div class="tier bronze">
<h2>第三</h2>
<h3>{{ bronzeUserInfo?.money }}</h3>
</div>
</div>
</div>
</template>
<style scoped>
h2{
margin: 10px;
}
h3{
margin: 10px;
}
.avatar {
display: flex;
justify-content: center;
flex-direction: column;
}
.podium {
justify-content: center;
display: flex;
align-items: flex-end;
position: relative;
}
.tier {
flex-direction: column;
width: 150px;
height: 200px;
display: flex;
justify-content: center;
align-items: center;
margin: 5px 40px 40px 40px;
border-radius: 20px;
color: white;
font-size: 24px;
position: relative;
}
.gold:hover {
box-shadow: 0 0 20px 10px rgba(255, 215, 0, 0.7); /* 金色光辉效果 */
}
.gold {
background: linear-gradient(to bottom, #ffd700 0%, #ffcc00 100%);
height: 250px;
box-shadow: 0 8px 20px rgba(255, 215, 0, 0.5);
}
.silver {
background: linear-gradient(to bottom, #c0c0c0 0%, #d3d3d3 100%);
height: 200px;
box-shadow: 0 6px 18px rgba(192, 192, 192, 0.5);
}
.bronze {
background: linear-gradient(to bottom, #cd7f32 0%, #a0522d 100%);
height: 170px;
box-shadow: 0 4px 16px rgba(205, 127, 50, 0.5);
}
</style>