feat: 新增找回密码功能

This commit is contained in:
橙子
2024-10-04 00:00:44 +08:00
parent d7629763ef
commit 10e1fad7f3
21 changed files with 602 additions and 297 deletions

View File

@@ -9,11 +9,11 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Discuss
public class DiscussGetListOutputDto : EntityDto<Guid>
{
/// <summary>
/// <EFBFBD>Ƿ<EFBFBD><EFBFBD><EFBFBD>ֹ<EFBFBD><EFBFBD><EFBFBD>۴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// 是否禁止评论创建功能
/// </summary>
public bool IsDisableCreateComment { get; set; }
/// <summary>
/// <EFBFBD>Ƿ<EFBFBD><EFBFBD>ѵ<EFBFBD><EFBFBD>ޣ<EFBFBD>Ĭ<EFBFBD><EFBFBD>δ<EFBFBD><EFBFBD>¼<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// 是否已点赞,默认未登录不点赞
/// </summary>
public bool IsAgree { get; set; } = false;
public string Title { get; set; }
@@ -23,26 +23,26 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Discuss
public int AgreeNum { get; set; }
public int SeeNum { get; set; }
//<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѯ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ܿ<EFBFBD><EFBFBD><EFBFBD>
//批量查询,不给内容,性能考虑
//public string Content { get; set; }
public string? Color { get; set; }
public Guid PlateId { get; set; }
//<EFBFBD>Ƿ<EFBFBD><EFBFBD>ö<EFBFBD><EFBFBD><EFBFBD>Ĭ<EFBFBD><EFBFBD>false
//是否置顶,默认false
public bool IsTop { get; set; }
public DiscussPermissionTypeEnum PermissionType { get; set; }
//<EFBFBD>Ƿ<EFBFBD><EFBFBD><EFBFBD>ֹ<EFBFBD><EFBFBD>Ĭ<EFBFBD><EFBFBD>false
//是否禁止,默认false
public bool IsBan { get; set; }
/// <summary>
/// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// 封面
/// </summary>
public string? Cover { get; set; }
//˽<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD>ж<EFBFBD>codeȨ<EFBFBD><EFBFBD>
//私有需要判断code权限
public string? PrivateCode { get; set; }
public DateTime CreationTime { get; set; }
@@ -55,7 +55,7 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Discuss
Title = DiscussConst.Privacy;
Introduction = "";
Cover = null;
//<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֹ
//被禁止
IsBan = true;
}
}
@@ -73,14 +73,14 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Discuss
case DiscussPermissionTypeEnum.Public:
break;
case DiscussPermissionTypeEnum.Oneself:
//<EFBFBD><EFBFBD>ǰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǽ<EFBFBD><EFBFBD>Լ<EFBFBD><EFBFBD>ɼ<EFBFBD><EFBFBD><EFBFBD>ͬʱ<EFBFBD><EFBFBD><EFBFBD>ǵ<EFBFBD>ǰ<EFBFBD><EFBFBD>¼<EFBFBD>û<EFBFBD>
//当前主题是仅自己可见,同时不是当前登录用户
if (dto.User.Id != userId)
{
dto.SetBan();
}
break;
case DiscussPermissionTypeEnum.User:
//<EFBFBD><EFBFBD>ǰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD><EFBFBD><EFBFBD>ֿɼ<EFBFBD><EFBFBD><EFBFBD>ͬʱ<EFBFBD><EFBFBD><EFBFBD>ǵ<EFBFBD>ǰ<EFBFBD><EFBFBD>¼<EFBFBD>û<EFBFBD> Ҳ <20><><EFBFBD>ڿɼ<DABF><C9BC>û<EFBFBD><C3BB>б<EFBFBD><D0B1><EFBFBD>
//当前主题为部分可见,同时不是当前登录用户 也 不在可见用户列表中
if (dto.User.Id != userId && !dto.PermissionUserIds.Contains(userId))
{
dto.SetBan();

View File

@@ -17,9 +17,11 @@ namespace Yi.Framework.Bbs.Application.Services.Analyses
public class BbsForumAnalyseService : ApplicationService, IApplicationService
{
private ForumManager _forumManager;
public BbsForumAnalyseService(ForumManager forumManager)
private ISqlSugarRepository<AgreeEntity> _agreeRepository;
public BbsForumAnalyseService(ForumManager forumManager, ISqlSugarRepository<AgreeEntity> agreeRepository)
{
_forumManager = forumManager;
_agreeRepository = agreeRepository;
}
/// <summary>
@@ -38,7 +40,7 @@ namespace Yi.Framework.Bbs.Application.Services.Analyses
.Select((discuss, user, info) => new DiscussGetListOutputDto
{
Id = discuss.Id,
IsAgree = SqlFunc.Subqueryable<AgreeEntity>().WhereIF(CurrentUser.Id != null, x => x.CreatorId == CurrentUser.Id && x.DiscussId == discuss.Id).Any(),
// IsAgree = SqlFunc.Subqueryable<AgreeEntity>().WhereIF(CurrentUser.Id != null, x => x.CreatorId == CurrentUser.Id && x.DiscussId == discuss.Id).Any(),
User = new BbsUserGetListOutputDto()
{
@@ -52,6 +54,26 @@ namespace Yi.Framework.Bbs.Application.Services.Analyses
}, true)
.ToPageListAsync(input.SkipCount, input.MaxResultCount);
var discussId = output.Select(x => x.Id);
//点赞字典key为主题idy为用户ids
var agreeDic =
(await _agreeRepository._DbQueryable.Where(x => discussId.Contains(x.DiscussId)).ToListAsync())
.GroupBy(x => x.DiscussId)
.ToDictionary(x => x.Key, y => y.Select(y => y.CreatorId).ToList());
//等级、是否点赞赋值
output?.ForEach(x =>
{
if (CurrentUser.Id is not null)
{
//默认fasle
if (agreeDic.TryGetValue(x.Id,out var userIds))
{
x.IsAgree = userIds.Contains(CurrentUser.Id);
}
}
});
return output;
}

View File

@@ -29,19 +29,27 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
/// <summary>
/// Discuss应用服务实现,用于参数校验、领域服务业务组合、日志记录、事务处理、账户信息
/// </summary>
public class DiscussService : YiCrudAppService<DiscussAggregateRoot, DiscussGetOutputDto, DiscussGetListOutputDto, Guid, DiscussGetListInputVo, DiscussCreateInputVo, DiscussUpdateInputVo>,
IDiscussService
public class DiscussService : YiCrudAppService<DiscussAggregateRoot, DiscussGetOutputDto, DiscussGetListOutputDto,
Guid, DiscussGetListInputVo, DiscussCreateInputVo, DiscussUpdateInputVo>,
IDiscussService
{
private ISqlSugarRepository<DiscussTopEntity> _discussTopEntityRepository;
private ISqlSugarRepository<DiscussTopEntity> _discussTopRepository;
private ISqlSugarRepository<AgreeEntity> _agreeRepository;
private BbsUserManager _bbsUserManager;
public DiscussService(BbsUserManager bbsUserManager, ForumManager forumManager, ISqlSugarRepository<DiscussTopEntity> discussTopEntityRepository, ISqlSugarRepository<PlateAggregateRoot> plateEntityRepository, ILocalEventBus localEventBus) : base(forumManager._discussRepository)
public DiscussService(BbsUserManager bbsUserManager, ForumManager forumManager,
ISqlSugarRepository<DiscussTopEntity> discussTopRepository,
ISqlSugarRepository<PlateAggregateRoot> plateEntityRepository, ILocalEventBus localEventBus,
ISqlSugarRepository<AgreeEntity> agreeRepository) : base(forumManager._discussRepository)
{
_forumManager = forumManager;
_plateEntityRepository = plateEntityRepository;
_localEventBus = localEventBus;
_discussTopEntityRepository = discussTopEntityRepository;
_bbsUserManager=bbsUserManager;
_agreeRepository = agreeRepository;
_discussTopRepository = discussTopRepository;
_bbsUserManager = bbsUserManager;
}
private readonly ILocalEventBus _localEventBus;
private ForumManager _forumManager { get; set; }
@@ -49,8 +57,6 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
private ISqlSugarRepository<PlateAggregateRoot> _plateEntityRepository { get; set; }
/// <summary>
/// 单查
/// </summary>
@@ -58,42 +64,43 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
/// <returns></returns>
public async override Task<DiscussGetOutputDto> GetAsync(Guid id)
{
//查询主题发布 浏览主题 事件,浏览数+1
var item = await _forumManager._discussRepository._DbQueryable.LeftJoin<UserAggregateRoot>((discuss, user) => discuss.CreatorId == user.Id)
var item = await _forumManager._discussRepository._DbQueryable
.LeftJoin<UserAggregateRoot>((discuss, user) => discuss.CreatorId == user.Id)
.LeftJoin<BbsUserExtraInfoEntity>((discuss, user, info) => user.Id == info.UserId)
.LeftJoin<PlateAggregateRoot>((discuss, user, info, plate) => plate.Id == discuss.PlateId)
.Select((discuss, user, info, plate) => new DiscussGetOutputDto
{
Id = discuss.Id,
IsAgree = SqlFunc.Subqueryable<AgreeEntity>().WhereIF(CurrentUser.Id != null, x => x.CreatorId == CurrentUser.Id && x.DiscussId == discuss.Id).Any(),
User = new BbsUserGetListOutputDto()
{
UserName = user.UserName,
Nick = user.Nick,
Icon = user.Icon,
Id = user.Id,
Level = info.Level,
UserLimit = info.UserLimit,
Money=info.Money,
Experience=info.Experience
},
Plate = new Contracts.Dtos.Plate.PlateGetOutputDto()
{
Name = plate.Name,
Id = plate.Id,
Code = plate.Code,
Introduction = plate.Introduction,
Logo = plate.Logo
}
}, true)
.SingleAsync(discuss => discuss.Id == id);
.Select((discuss, user, info, plate) => new DiscussGetOutputDto
{
Id = discuss.Id,
IsAgree = SqlFunc.Subqueryable<AgreeEntity>().WhereIF(CurrentUser.Id != null,
x => x.CreatorId == CurrentUser.Id && x.DiscussId == discuss.Id).Any(),
User = new BbsUserGetListOutputDto()
{
UserName = user.UserName,
Nick = user.Nick,
Icon = user.Icon,
Id = user.Id,
Level = info.Level,
UserLimit = info.UserLimit,
Money = info.Money,
Experience = info.Experience
},
Plate = new Contracts.Dtos.Plate.PlateGetOutputDto()
{
Name = plate.Name,
Id = plate.Id,
Code = plate.Code,
Introduction = plate.Introduction,
Logo = plate.Logo
}
}, true)
.SingleAsync(discuss => discuss.Id == id);
if (item is not null)
{
await VerifyDiscussPermissionAsync(item.Id);
await _localEventBus.PublishAsync(new SeeDiscussEventArgs { DiscussId = item.Id, OldSeeNum = item.SeeNum });
await _localEventBus.PublishAsync(new SeeDiscussEventArgs
{ DiscussId = item.Id, OldSeeNum = item.SeeNum });
}
return item;
@@ -105,49 +112,65 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public override async Task<PagedResultDto<DiscussGetListOutputDto>> GetListAsync([FromQuery] DiscussGetListInputVo input)
public override async Task<PagedResultDto<DiscussGetListOutputDto>> GetListAsync(
[FromQuery] DiscussGetListInputVo input)
{
//需要关联创建者用户
RefAsync<int> total = 0;
var items = await _forumManager._discussRepository._DbQueryable
.WhereIF(!string.IsNullOrEmpty(input.Title), x => x.Title.Contains(input.Title))
.WhereIF(input.PlateId is not null, x => x.PlateId == input.PlateId)
.WhereIF(input.IsTop is not null, x => x.IsTop == input.IsTop)
.WhereIF(input.UserId is not null,x=>x.CreatorId==input.UserId)
.LeftJoin<UserAggregateRoot>((discuss, user) => discuss.CreatorId == user.Id)
.WhereIF(input.UserName is not null, (discuss, user)=>user.UserName==input.UserName!)
.LeftJoin<BbsUserExtraInfoEntity>((discuss, user, info) => user.Id == info.UserId)
.OrderByDescending(discuss => discuss.OrderNum)
.OrderByIF(input.Type == QueryDiscussTypeEnum.New, discuss => discuss.CreationTime, OrderByType.Desc)
.OrderByIF(input.Type == QueryDiscussTypeEnum.Host, discuss => discuss.SeeNum, OrderByType.Desc)
.OrderByIF(input.Type == QueryDiscussTypeEnum.Suggest, discuss => discuss.AgreeNum, OrderByType.Desc)
.Select((discuss, user, info) => new DiscussGetListOutputDto
{
Id = discuss.Id,
IsAgree = SqlFunc.Subqueryable<AgreeEntity>().WhereIF(CurrentUser.Id != null, x => x.CreatorId == CurrentUser.Id && x.DiscussId == discuss.Id).Any(),
User = new BbsUserGetListOutputDto()
{
Id = user.Id,
UserName = user.UserName,
Nick = user.Nick,
Icon = user.Icon,
Level = info.Level,
UserLimit = info.UserLimit,
Money = info.Money,
Experience = info.Experience
}
}, true)
.WhereIF(!string.IsNullOrEmpty(input.Title), x => x.Title.Contains(input.Title))
.WhereIF(input.PlateId is not null, x => x.PlateId == input.PlateId)
.WhereIF(input.IsTop is not null, x => x.IsTop == input.IsTop)
.WhereIF(input.UserId is not null, x => x.CreatorId == input.UserId)
.LeftJoin<UserAggregateRoot>((discuss, user) => discuss.CreatorId == user.Id)
.WhereIF(input.UserName is not null, (discuss, user) => user.UserName == input.UserName!)
.LeftJoin<BbsUserExtraInfoEntity>((discuss, user, info) => user.Id == info.UserId)
.OrderByDescending(discuss => discuss.OrderNum)
.OrderByIF(input.Type == QueryDiscussTypeEnum.New, discuss => discuss.CreationTime, OrderByType.Desc)
.OrderByIF(input.Type == QueryDiscussTypeEnum.Host, discuss => discuss.SeeNum, OrderByType.Desc)
.OrderByIF(input.Type == QueryDiscussTypeEnum.Suggest, discuss => discuss.AgreeNum, OrderByType.Desc)
.Select((discuss, user, info) => new DiscussGetListOutputDto
{
Id = discuss.Id,
// 优化查询,不使用子查询
// IsAgree = SqlFunc.Subqueryable<AgreeEntity>().WhereIF(CurrentUser.Id != null, x => x.CreatorId == CurrentUser.Id && x.DiscussId == discuss.Id).Any(),
User = new BbsUserGetListOutputDto()
{
Id = user.Id,
UserName = user.UserName,
Nick = user.Nick,
Icon = user.Icon,
Level = info.Level,
UserLimit = info.UserLimit,
Money = info.Money,
Experience = info.Experience
}
}, true)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
var discussId = items.Select(x => x.Id);
//点赞字典key为主题idy为用户ids
var agreeDic =
(await _agreeRepository._DbQueryable.Where(x => discussId.Contains(x.DiscussId)).ToListAsync())
.GroupBy(x => x.DiscussId)
.ToDictionary(x => x.Key, y => y.Select(y => y.CreatorId).ToList());
//查询完主题之后,要过滤一下私有的主题信息
items.ApplyPermissionTypeFilter(CurrentUser.Id ?? Guid.Empty);
items?.ForEach(x => x.User.LevelName = _bbsUserManager._levelCacheDic[x.User.Level].Name);
//等级、是否点赞赋值
items?.ForEach(x =>
{
x.User.LevelName = _bbsUserManager._levelCacheDic[x.User.Level].Name;
if (CurrentUser.Id is not null)
{
//默认fasle
if (agreeDic.TryGetValue(x.Id,out var userIds))
{
x.IsAgree = userIds.Contains(CurrentUser.Id);
}
}
});
return new PagedResultDto<DiscussGetListOutputDto>(total, items);
}
@@ -157,14 +180,16 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
/// <returns></returns>
public async Task<List<DiscussGetListOutputDto>> GetListTopAsync()
{
var output = await _discussTopEntityRepository._DbQueryable.LeftJoin<DiscussAggregateRoot>((top, discuss) => top.DiscussId == discuss.Id)
var output = await _discussTopRepository._DbQueryable
.LeftJoin<DiscussAggregateRoot>((top, discuss) => top.DiscussId == discuss.Id)
.LeftJoin<UserAggregateRoot>((top, discuss, user) => discuss.CreatorId == user.Id)
.LeftJoin<BbsUserExtraInfoEntity>((top, discuss, user, info) => user.Id == info.UserId)
.OrderByDescending(top => top.OrderNum)
.Select((top, discuss, user, info) => new DiscussGetListOutputDto
{
Id = discuss.Id,
IsAgree = SqlFunc.Subqueryable<AgreeEntity>().WhereIF(CurrentUser.Id != null, x => x.CreatorId == CurrentUser.Id && x.DiscussId == discuss.Id).Any(),
IsAgree = SqlFunc.Subqueryable<AgreeEntity>().WhereIF(CurrentUser.Id != null,
x => x.CreatorId == CurrentUser.Id && x.DiscussId == discuss.Id).Any(),
User = new BbsUserGetListOutputDto
{
Id = user.Id,
@@ -206,6 +231,11 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
throw new UserFriendlyException(PlateConst.No_Exist);
}
if (await _forumManager._discussRepository.IsAnyAsync(x => x.Title == input.Title))
{
throw new UserFriendlyException(DiscussConst.Repeat);
}
//如果开启了禁用创建主题
if (plate.IsDisableCreateDiscuss == true)
{
@@ -233,6 +263,7 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
{
throw new UserFriendlyException(DiscussConst.No_Exist);
}
if (discuss.PermissionType == DiscussPermissionTypeEnum.Oneself)
{
if (discuss.CreatorId != CurrentUser.Id)
@@ -240,13 +271,15 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
throw new UserFriendlyException(DiscussConst.Privacy);
}
}
if (discuss.PermissionType == DiscussPermissionTypeEnum.User)
{
if (discuss.CreatorId != CurrentUser.Id && !discuss.PermissionUserIds.Contains(CurrentUser.Id ?? Guid.Empty))
if (discuss.CreatorId != CurrentUser.Id &&
!discuss.PermissionUserIds.Contains(CurrentUser.Id ?? Guid.Empty))
{
throw new UserFriendlyException(DiscussConst.Privacy);
}
}
}
}
}
}

View File

@@ -11,6 +11,7 @@ namespace Yi.Framework.Bbs.Domain.Shared.Consts
/// </summary>
public class DiscussConst
{
public const string Repeat = "创建主题重复";
public const string No_Exist = "传入的主题id不存在";
public const string Privacy = "【私密】您无该主题权限,可联系作者申请开放";

View File

@@ -184,22 +184,24 @@ namespace Yi.Framework.Rbac.Application.Services
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("captcha-phone")]
[HttpPost("account/captcha-phone")]
[AllowAnonymous]
public async Task<object> PostCaptchaPhoneForRegisterAsync(PhoneCaptchaImageDto input)
{
return await PostCaptchaPhoneAsync(ValidationPhoneTypeEnum.Register, input);
}
/// <summary>
/// 手机验证码-找回密码
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("captcha-phone/repassword")]
[HttpPost("account/captcha-phone/repassword")]
public async Task<object> PostCaptchaPhoneForRetrievePasswordAsync(PhoneCaptchaImageDto input)
{
return await PostCaptchaPhoneAsync(ValidationPhoneTypeEnum.RetrievePassword, input);
}
/// <summary>
/// 手机验证码
/// </summary>
@@ -223,7 +225,7 @@ namespace Yi.Framework.Rbac.Application.Services
var uuid = Guid.NewGuid();
await _aliyunManger.SendSmsAsync(input.Phone, code);
await _phoneCache.SetAsync(new CaptchaPhoneCacheKey(ValidationPhoneTypeEnum.Register, input.Phone),
await _phoneCache.SetAsync(new CaptchaPhoneCacheKey(ValidationPhoneTypeEnum.RetrievePassword, input.Phone),
new CaptchaPhoneCacheItem(code),
new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(10) });
return new
@@ -255,14 +257,11 @@ namespace Yi.Framework.Rbac.Application.Services
/// <param name="input"></param>
[AllowAnonymous]
[UnitOfWork]
public async Task PostRetrievePasswordAsync(RetrievePasswordDto input)
public async Task<string> PostRetrievePasswordAsync(RetrievePasswordDto input)
{
if (_rbacOptions.EnableCaptcha)
{
//校验验证码,根据电话号码获取 value比对验证码已经uuid
await ValidationPhoneCaptchaAsync(ValidationPhoneTypeEnum.RetrievePassword, input.Phone, input.Code);
}
//校验验证码,根据电话号码获取 value比对验证码已经uuid
await ValidationPhoneCaptchaAsync(ValidationPhoneTypeEnum.RetrievePassword, input.Phone, input.Code);
var entity = await _userRepository.GetFirstAsync(x => x.Phone == input.Phone);
if (entity is null)
{
@@ -270,6 +269,8 @@ namespace Yi.Framework.Rbac.Application.Services
}
await _accountManager.RestPasswordAsync(entity.Id, input.Password);
return entity.UserName;
}

View File

@@ -39,7 +39,8 @@ namespace Yi.Framework.Rbac.Application.Services
if (!File.Exists(path))
{
throw new UserFriendlyException("文件不存在",code:"404");
return new NotFoundResult();
// throw new UserFriendlyException("文件不存在",code:"404");
}
@@ -66,12 +67,6 @@ namespace Yi.Framework.Rbac.Application.Services
// path = $"wwwroot/{FileTypeEnum.Thumbnail}/{file.Id}{Path.GetExtension(file.FileName)}";
//}
//路径为: 文件路径/文件id+文件扩展名
if (!File.Exists(path))
{
throw new UserFriendlyException("本地文件不存在", "404");
}
return path;
}

View File

@@ -7,4 +7,7 @@ VITE_APP_URL="http://localhost:19001/api/app"
# ws/开发环境
VITE_APP_BASE_WS = '/dev-ws'
VITE_APP_BASE_URL_WS="http://localhost:19001/hub"
VITE_APP_BASE_URL_WS="http://localhost:19001/hub"
# 是否开启ICP备案模式
VITE_APP_ICP = true

View File

@@ -6,4 +6,7 @@ VITE_APP_URL="http://ccnetcore.com:19001/api/app"
# ws
VITE_APP_BASE_WS = '/prod-ws'
VITE_APP_BASE_URL_WS="http://ccnetcore.com:19001/hub"
VITE_APP_BASE_URL_WS="http://ccnetcore.com:19001/hub"
# 是否开启ICP备案模式
VITE_APP_ICP = true

View File

@@ -4,20 +4,22 @@
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>.Net意社区</title>
<link rel="stylesheet" href="/src/assets/loading.css" />
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5453339688995325"
crossorigin="anonymous"></script>
</head>
<body>
<div id="Loading">
<div class="loader JS_on">
<span class="binary"></span>
<span class="binary"></span>
<span class="getting-there">意社区很大,你要等一下...</span>
</div>
</div>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
<!--<title>.Net意社区</title>-->
<!-- 为了icp备案-->
<title>个人成果展示</title>
<link rel="stylesheet" href="/src/assets/loading.css" />
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5453339688995325"
crossorigin="anonymous"></script>
</head>
<body>
<div id="Loading">
<div class="loader JS_on">
<span class="binary"></span>
<span class="binary"></span>
<span class="getting-there">意社区很大,你要等一下...</span>
</div>
</div>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

View File

@@ -18,6 +18,24 @@ export function login(username, password, code, uuid) {
});
}
//找回密码
export function retrievePassword(password, phone, code, uuid) {
const data = {
password,
phone,
code,
uuid,
};
return request({
url: "/account/retrieve-password",
headers: {
isToken: false,
},
method: "post",
data: data,
});
}
// 注册方法
export function register(userName, password, phone, code, uuid) {
const data = {
@@ -76,3 +94,15 @@ export function getCodePhone(phone) {
data: { phone },
});
}
// 获取短信验证码-为了重置密码
export function getCodePhoneForRetrievePassword(phone) {
return request({
url: "/account/captcha-phone/repassword",
headers: {
isToken: false,
},
method: "post",
timeout: 20000,
data: { phone },
});
}

View File

@@ -1,5 +1,4 @@
import request from "@/config/axios/service";
/**
* 用户登录
* @param {*} data 账号密码
@@ -23,6 +22,17 @@ export function userRegister(data) {
data,
});
}
/**
* 用户找回密码
* @param {*} data 账号密码
*/
export function userRetrievePassword(data) {
return request({
url: `/account/retrieve-password`,
method: "post",
data,
});
}
/**
* 获取用户详细信息
@@ -44,15 +54,6 @@ export function userLogout() {
});
}
/**
* 获取验证码
*/
export function getCodeImg() {
return request({
url: `/account/captcha-image`,
method: "get",
});
}
/**
* 获取短信验证码
*/

View File

@@ -103,13 +103,18 @@ height: 25px;
.left-lable
{
display: flex;
align-items: center;
justify-content: space-between;
font-size: 12px;
}
.left-lable label{
margin-left: 5px;
}
.right-forgot{
cursor: pointer;
}
.right-forgot:hover{
color: #7f438c;
}
.bottom-div
{
font-size: 12px;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -7,7 +7,7 @@ import {
userLogin,
getUserDetailInfo,
userLogout,
userRegister,
userRegister, userRetrievePassword,
} from "@/apis/auth";
const TokenKey = "AccessToken";
export const AUTH_MENUS = "AUTH_MENUS";
@@ -182,6 +182,20 @@ const currentUserInfo=computed(()=>{
// }
};
// 找回密码
const retrievePasswordFun = async (params) => {
// try {
const {data}=await userRetrievePassword(params);
ElMessage({
message: `恭喜!账号:${data},找回成功!密码已重置,请登录!`,
type: "success",
duration: 8000
});
// } catch (error) {
// console.log(error);
// }
};
return {
getToken,
setToken,
@@ -189,6 +203,7 @@ const currentUserInfo=computed(()=>{
loginFun,
getUserInfo,
logoutFun,
retrievePasswordFun,
clearStorage,
registerFun,
loginSuccess,

View File

@@ -4,7 +4,8 @@
<div class="image">
<img class="img-icon" src="@/assets/common/icons/logo.ico" />
</div>
<div class="text">{{ configStore.name }}</div>
<div class="text">{{ isIcp===true?"个人成果展示":configStore.name }}</div>
</div>
<div class="tab">
<el-menu :default-active="activeIndex" mode="horizontal" :ellipsis="false" @select="handleSelect">
@@ -152,6 +153,8 @@ const searchText = ref("");
const noticeForNoReadCount = computed(() => {
return noticeList.value.filter(x => x.isRead == false).length;
})
const isIcp=import.meta.env.VITE_APP_ICP==="true";
//加载初始化离线消息
onMounted(async () => {
//登录了才去判断消息通知

View File

@@ -26,13 +26,18 @@ const router = createRouter({
name: "login",
path: "/login",
// component: () => import("../views/Login.vue"),
component: () => import("../views/login/index.vue"),
component: () => import("../views/login/login.vue"),
},
{
name: "register",
path: "/register",
component: () => import("../views/login/register.vue"),
},
{
name: "forgotPassword",
path: "/forgotPassword",
component: () => import("../views/login/forgotPassword.vue"),
},
{
name: "auth",
path: "/auth/:type",

View File

@@ -1,4 +1,4 @@
import { login, logout, register } from "@/apis/accountApi";
import { login, logout, register,retrievePassword } from "@/apis/accountApi";
import { getUserDetailInfo, getLoginCode } from "@/apis/auth";
import useAuths from "@/hooks/useAuths";
import { defineStore } from "pinia";
@@ -122,6 +122,23 @@ const useUserStore = defineStore("user", {
});
});
},
//找回密码
retrievePassword(userInfo)
{
const password = userInfo.password.trim();
const phone = userInfo.phone;
const uuid = userInfo.uuid;
const code = userInfo.code;
return new Promise((resolve, reject) => {
retrievePassword(password, phone, code, uuid)
.then((response) => {
resolve(response);
})
.catch((error) => {
reject(error);
});
});
},
// 重置用户信息
resetInfo() {
this.roles = [];

View File

@@ -4,7 +4,26 @@
<el-col :span="17">
<div class="chat-hub">
<!-- <p @click="onClickToChatHub">点击前往-最新上线<span>聊天室 </span>现已支持<span>Ai助手</span>希望能帮助大家</p>-->
<p @click="onClickToWeChat">点击关注-最新上线<span>.Net官方微信公众号 </span>分享有<span>深度</span>.Net知识希望能帮助大家</p>
<p v-if="isIcp"
style="font-size: 25px;
color: blue;
background: red;
/* height: 80px; */
font-weight: 900;
text-align: center;
margin: 10px auto;">
本站点为个人内容分享全部资料免费开源学习所有数据为假数据
<br/>
不涉及企业团体论坛和经营销售等内容只做简单的成果展示
<br/>
富强民主文明和谐自由平等
<br/>
公正法治爱国敬业诚信友善
</p>
<p v-else @click="onClickToWeChat">点击关注-最新上线<span>.Net官方微信公众号 </span>分享有<span>深度</span>.Net知识希望能帮助大家</p>
</div>
<div class="scrollbar">
<ScrollbarInfo/>
@@ -272,7 +291,7 @@ const activeList = [
{name: "开始", path: "/start", icon: "Position"},
{name: "聊天室", path: "/chat", icon: "ChatRound"},
];
const isIcp=import.meta.env.VITE_APP_ICP==="true";
//主题查询参数
const query = reactive({
skipCount: 1,
@@ -622,7 +641,7 @@ const onClickToWeChat=()=>{
display: flex;
align-content: center;
flex-wrap: wrap;
height: 30px;
min-height: 30px;
p {
margin: 0 auto;

View File

@@ -1,11 +0,0 @@
<script setup>
</script>
<template>
</template>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,153 @@
<script setup>
// 注册逻辑
import {reactive, ref} from "vue";
import {getCodePhoneForRetrievePassword} from "@/apis/accountApi";
import useAuths from "@/hooks/useAuths";
import { useRouter} from "vue-router";
const { retrievePasswordFun } = useAuths();
const router = useRouter();
const retrievePasswordFormRef = ref();
// 确认密码
const passwordConfirm = ref("");
const registerForm = reactive({
phone: "",
password: "",
uuid: "",
code: ""
});
const registerRules = reactive({
phone: [{ required: true, message: "请输入手机号", trigger: "blur" }],
code: [{ required: true, message: "请输入验证码", trigger: "blur" }],
password: [
{ required: true, message: "请输入新的密码", trigger: "blur" },
{ min: 6, message: "密码需大于六位", trigger: "blur" },
],
});
const retrievePassword = async (formEl) => {
if (!formEl) return;
await formEl.validate(async (valid) => {
if (valid) {
try {
if (registerForm.password != passwordConfirm.value) {
ElMessage.error("两次密码输入不一致");
return;
}
await retrievePasswordFun(registerForm);
//注册成功返回登录
handleSignInNow();
} catch (error) {
ElMessage({
message: error.message,
type: "error",
duration: 2000,
});
}
}
});
};
//验证码
const codeInfo = ref("发送短信");
const isDisabledCode = ref(false);
//前往登录
const handleSignInNow=()=>{
router.push("/login");
}
const captcha = async () => {
if (registerForm.phone !== "") {
const { data } = await getCodePhoneForRetrievePassword(registerForm.phone);
registerForm.uuid = data.uuid;
ElMessage({
message: `已向${registerForm.phone}发送验证码,请注意查收`,
type: "success",
});
isDisabledCode.value = true;
let time = 60; //定义剩下的秒数
let timer = setInterval(function () {
if (time == 0) {
//清除定时器和复原按钮
clearInterval(timer);
codeInfo.value = "发送验证码";
time = 60; //这个10是重新开始
} else {
codeInfo.value = "剩余" + time + "秒";
time--;
}
}, 1000);
} else {
ElMessage({
message: `请先输入手机号`,
type: "warning",
});
}
};
</script>
<template>
<div class="container">
<!-- 找回密码 -->
<div class="div-content">
<div class="div-right-register">
<img class="div-img" src="@/assets/login.png"/>
</div>
<div class="div-left-register">
<div class="left-container">
<p class="title register-title">Find Password!</p>
<el-form
class="registerForm"
ref="retrievePasswordFormRef"
:model="registerForm"
:rules="registerRules"
>
<div class="input-content">
<div class="input" style="margin-top: 0">
<p>*电话</p>
<el-form-item prop="phone">
<div class="phone-code">
<input class="phone-code-input" type="text" v-model.trim="registerForm.phone">
<button type="button" class="phone-code-btn" @click="captcha()">{{codeInfo}}</button>
</div>
</el-form-item>
</div>
<div class="input">
<p>*短信验证码</p>
<el-form-item prop="code" >
<input :disabled="!isDisabledCode" type="text" v-model.trim="registerForm.code">
</el-form-item>
</div>
<div class="input">
<p>*新的密码</p>
<el-form-item prop="password">
<input :disabled="!isDisabledCode" type="password" v-model.trim="registerForm.password">
</el-form-item>
</div>
<div class="input">
<p>*确认密码</p>
<el-form-item>
<input :disabled="!isDisabledCode" type="password" v-model.trim="passwordConfirm">
</el-form-item>
</div>
</div>
</el-form>
<div class="left-btn">
<button type="button" class="btn-login" @click="retrievePassword(retrievePasswordFormRef)">确认重置密码</button>
<button type="button" class="btn-reg" @click="handleSignInNow">前往登录</button>
</div>
</div>
</div>
</div>
</div>
</template>
<style src="@/assets/styles/login.css" scoped>
</style>

View File

@@ -1,145 +1,153 @@
<template>
<div class="container">
<!-- 登录 -->
<div class="div-content">
<div class="div-left">
<div class="left-container">
<p class="title title-1">Hello,<span @click="guestlogin">you can go to homepage >></span></p>
<p class="title title-2">Welcome to Yi!</p>
<el-form
ref="loginFormRef"
:model="loginForm"
:rules="rules"
>
<div class="input-content">
<div class="input">
<p>用户名</p>
<el-form-item prop="userName">
<input type="text" v-model="loginForm.userName">
</el-form-item>
</div>
<div class="input">
<p>密码</p>
<el-form-item prop="password">
<input type="password" v-model="loginForm.password">
</el-form-item>
</div>
<div class="input">
<p>验证码</p>
<el-form-item prop="code">
<div class="code">
<input class="code-input" type="text" v-model.trim="loginForm.code">
<img class="code-img" alt="加载中" @click="handleGetCodeImage" :src="codeImageURL">
</div>
</el-form-item>
</div>
</div>
</el-form>
<div class="left-lable">
<input type="checkbox">
<label>记住我</label>
</div>
<div class="left-btn">
<button type="button" class="btn-login" @click="login(loginFormRef)">登录</button>
<button type="button" class="btn-reg" @click="handleRegister">前往注册</button>
</div>
<div class="bottom-div">
<p>其他方式: <span @click="handleQQLogin"><img src="@/assets/login_images/qq-setting.png" alt="QQ" /></span> <span @click="handleGiteeLogin"><img src="@/assets/login_images/gitee-setting.png" alt="Gitee" /></span></p>
</div>
</div>
</div>
<div class="div-right">
<img class="div-img" src="@/assets/login.png" alt=""/>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { useRouter, useRoute } from "vue-router";
import useAuths from "@/hooks/useAuths";
import useUserStore from "@/stores/user";
const { loginFun, loginSuccess } = useAuths();
const router = useRouter();
const route = useRoute();
const loginFormRef = ref();
const rules = reactive({
userName: [{ required: true, message: "请输入用户名", trigger: "blur" }],
password: [{ required: true, message: "请输入密码", trigger: "blur" }],
});
const loginForm = reactive({
userName: "",
password: "",
uuid: "",
code: "",
});
//
const handleRegister=()=>{
router.push("/register");
}
//
const guestlogin = () => {
const redirect = route.query?.redirect ?? "/index";
router.push(redirect);
};
const codeUUid = computed(() => useUserStore().codeUUid);
const login = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (valid) {
try {
loginForm.uuid = codeUUid.value;
loginFun(loginForm);
} catch (error) {
console.log(error.message, "error.message");
ElMessage({
message: error.message,
type: "error",
duration: 2000,
});
}
}
});
};
//
const codeImageURL = computed(() => useUserStore().codeImageURL);
const handleGetCodeImage = () => {
useUserStore().updateCodeImage();
};
onMounted(async () => {
await useUserStore().updateCodeImage();
});
const handleQQLogin = () => {
window.open(
"https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=102087446&redirect_uri=https://ccnetcore.com/auth/qq&state=0&scope=get_user_info",
undefined,
"width=500,height=500,left=50,top=50"
);
};
const handleGiteeLogin = () => {
window.open(
"https://gitee.com/oauth/authorize?client_id=949f3519969adc5cfe82c209b71300e8e0868e8536f3d7f59195c8f1e5b72502&redirect_uri=https%3A%2F%2Fccnetcore.com%2Fauth%2Fgitee&state=0&response_type=code",
undefined,
"width=500,height=500,left=50,top=50"
);
};
window.addEventListener("message", async (e) => {
const { authData, type } = e.data;
console.log(authData, "传到登录页的值");
if (authData) {
await loginSuccess({ data: JSON.parse(authData) });
}
});
</script>
<style src="@/assets/styles/login.css" scoped>
<template>
<div class="container">
<!-- 登录 -->
<div class="div-content">
<div class="div-left">
<div class="left-container">
<p class="title title-1">Hello,<span @click="guestlogin">you can go to homepage >></span></p>
<p class="title title-2">Welcome to Yi!</p>
<el-form
ref="loginFormRef"
:model="loginForm"
:rules="rules"
>
<div class="input-content">
<div class="input">
<p>用户名</p>
<el-form-item prop="userName">
<input type="text" v-model="loginForm.userName">
</el-form-item>
</div>
<div class="input">
<p>密码</p>
<el-form-item prop="password">
<input type="password" v-model="loginForm.password">
</el-form-item>
</div>
<div class="input">
<p>验证码</p>
<el-form-item prop="code">
<div class="code">
<input class="code-input" type="text" v-model.trim="loginForm.code">
<img class="code-img" alt="加载中" @click="handleGetCodeImage" :src="codeImageURL">
</div>
</el-form-item>
</div>
</div>
</el-form>
<div class="left-lable">
<div>
<input type="checkbox">
<label>记住我</label>
</div>
<span class="right-forgot" @click="handleForgotPassword">忘记密码点击找回</span>
</div>
<div class="left-btn">
<button type="button" class="btn-login" @click="login(loginFormRef)">登录</button>
<button type="button" class="btn-reg" @click="handleRegister">前往注册</button>
</div>
<div class="bottom-div">
<p>其他方式: <span @click="handleQQLogin"><img src="@/assets/login_images/qq-setting.png" alt="QQ" /></span> <span @click="handleGiteeLogin"><img src="@/assets/login_images/gitee-setting.png" alt="Gitee" /></span></p>
</div>
</div>
</div>
<div class="div-right">
<img class="div-img" src="@/assets/login.png" alt=""/>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { useRouter, useRoute } from "vue-router";
import useAuths from "@/hooks/useAuths";
import useUserStore from "@/stores/user";
const { loginFun, loginSuccess } = useAuths();
const router = useRouter();
const route = useRoute();
const loginFormRef = ref();
const rules = reactive({
userName: [{ required: true, message: "请输入用户名", trigger: "blur" }],
password: [{ required: true, message: "请输入密码", trigger: "blur" }],
});
const loginForm = reactive({
userName: "",
password: "",
uuid: "",
code: "",
});
//
const handleRegister=()=>{
router.push("/register");
}
//
const handleForgotPassword = () => {
router.push("/forgotPassword");
}
//
const guestlogin = () => {
const redirect = route.query?.redirect ?? "/index";
router.push(redirect);
};
const codeUUid = computed(() => useUserStore().codeUUid);
const login = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (valid) {
try {
loginForm.uuid = codeUUid.value;
loginFun(loginForm);
} catch (error) {
console.log(error.message, "error.message");
ElMessage({
message: error.message,
type: "error",
duration: 2000,
});
}
}
});
};
//
const codeImageURL = computed(() => useUserStore().codeImageURL);
const handleGetCodeImage = () => {
useUserStore().updateCodeImage();
};
onMounted(async () => {
await useUserStore().updateCodeImage();
});
const handleQQLogin = () => {
window.open(
"https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=102087446&redirect_uri=https://ccnetcore.com/auth/qq&state=0&scope=get_user_info",
undefined,
"width=500,height=500,left=50,top=50"
);
};
const handleGiteeLogin = () => {
window.open(
"https://gitee.com/oauth/authorize?client_id=949f3519969adc5cfe82c209b71300e8e0868e8536f3d7f59195c8f1e5b72502&redirect_uri=https%3A%2F%2Fccnetcore.com%2Fauth%2Fgitee&state=0&response_type=code",
undefined,
"width=500,height=500,left=50,top=50"
);
};
window.addEventListener("message", async (e) => {
const { authData, type } = e.data;
console.log(authData, "传到登录页的值");
if (authData) {
await loginSuccess({ data: JSON.parse(authData) });
}
});
</script>
<style src="@/assets/styles/login.css" scoped>
</style>