live-forum/server/webapi/LiveForum/LiveForum.Service/Others/SearchService.cs
2026-03-24 11:27:37 +08:00

298 lines
12 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using FreeSql;
using LiveForum.Code.Base;
using LiveForum.Code.JwtInfrastructure;
using LiveForum.IService.Others;
using LiveForum.Model;
using LiveForum.Model.Dto.Others;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace LiveForum.Service.Others
{
public class SearchService : ISearchService
{
private readonly IBaseRepository<T_Posts> _postsRepository;
private readonly IBaseRepository<T_Users> _usersRepository;
private readonly IBaseRepository<T_Follows> _followsRepository;
private readonly IBaseRepository<T_UserTokens> _tokensRepository;
private readonly JwtUserInfoModel _userInfoModel;
private readonly IConfiguration _configuration;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="postsRepository">帖子仓储</param>
/// <param name="usersRepository">用户仓储</param>
/// <param name="followsRepository">用户关注仓储</param>
/// <param name="tokensRepository">token仓储</param>
/// <param name="userInfoModel">用户信息模型</param>
/// <param name="configuration">配置</param>
public SearchService(
IBaseRepository<T_Posts> postsRepository,
IBaseRepository<T_Users> usersRepository,
IBaseRepository<T_Follows> followsRepository,
IBaseRepository<T_UserTokens> tokensRepository,
JwtUserInfoModel userInfoModel,
IConfiguration configuration)
{
_postsRepository = postsRepository;
_usersRepository = usersRepository;
_followsRepository = followsRepository;
_userInfoModel = userInfoModel;
_tokensRepository = tokensRepository;
_configuration = configuration;
}
/// <summary>
/// 搜索
/// </summary>
/// <param name="request">请求参数</param>
/// <returns></returns>
public async Task<BaseResponse<SearchRespDto>> Search(SearchReq request)
{
try
{
// 验证搜索关键词
if (string.IsNullOrWhiteSpace(request.Keyword))
{
return new BaseResponse<SearchRespDto>(ResponseCode.Error, "搜索关键词不能为空");
}
// 验证搜索类型
if (request.SearchType != 1 && request.SearchType != 2)
{
return new BaseResponse<SearchRespDto>(ResponseCode.Error, "无效的搜索类型");
}
var keyword = request.Keyword.Trim();
// 记录搜索历史(可选)
// await RecordSearchHistory(keyword);
if (request.SearchType == 1)
{
// 搜索帖子
return await SearchPosts(keyword, request);
}
else
{
// 搜索用户
return await SearchUsers(keyword, request);
}
}
catch (Exception ex)
{
return new BaseResponse<SearchRespDto>(ResponseCode.Error, $"搜索失败:{ex.Message}");
}
}
/// <summary>
/// 搜索帖子
/// </summary>
/// <param name="keyword">关键词</param>
/// <param name="request">请求参数</param>
/// <returns></returns>
private async Task<BaseResponse<SearchRespDto>> SearchPosts(string keyword, SearchReq request)
{
var query = _postsRepository.Select
.Where(x => !x.IsDeleted && x.Status == Model.Enum.Posts.PostsStatusEnum.Publish)
.Where(x => x.Title.Contains(keyword) || x.Content.Contains(keyword))
.OrderByDescending(x => x.PublishTime);
// 获取总数
var total = await query.CountAsync();
// 分页查询
var posts = await query
.Page(request.PageIndex, request.PageSize)
.ToListAsync();
// 获取当前用户ID如果已登录
var currentUserId = _userInfoModel.UserId;
// 获取发帖用户信息
var userIds = posts.Select(p => p.UserId).Distinct().ToList();
var users = await _usersRepository.Select
.Where(u => userIds.Contains(u.Id))
.ToListAsync();
var userDict = users.ToDictionary(u => u.Id);
// 构建返回结果
var items = new List<object>();
foreach (var post in posts)
{
var user = userDict.GetValueOrDefault(post.UserId);
items.Add(new SearchPostDto
{
PostId = post.Id,
Title = post.Title,
Content = post.Content.Length > 200 ? post.Content.Substring(0, 200) + "..." : post.Content,
CoverImage = post.CoverImage,
LikeCount = post.LikeCount,
CommentCount = post.CommentCount,
PublishTime = post.PublishTime ?? post.CreatedAt,
User = user != null ? new SearchUserDto
{
UserId = user.Id,
UserName = "", // T_Users 没有 UserName 字段
NickName = user.NickName ?? "",
Avatar = user.Avatar ?? "",
IsVip = user.IsVip,
IsCertified = user.IsCertified,
Signature = user.Signature ?? "",
FollowerCount = 0, // 需要从 T_UserFollows 统计
IsFollowed = currentUserId > 0 && await IsFollowingUser(currentUserId, user.Id)
} : null
});
}
var totalPages = (int)Math.Ceiling(total / (double)request.PageSize);
return new BaseResponse<SearchRespDto>(new SearchRespDto
{
PageIndex = request.PageIndex,
PageSize = request.PageSize,
Total =(int)total,
TotalPages = totalPages,
Items = items
});
}
/// <summary>
/// 搜索用户
/// </summary>
/// <param name="keyword">关键词</param>
/// <param name="request">请求参数</param>
/// <returns></returns>
private async Task<BaseResponse<SearchRespDto>> SearchUsers(string keyword, SearchReq request)
{
var query = _usersRepository.Select
.Where(x => x.Status == Model.Enum.Users.UserStatusEnum.Normal)
.Where(x => x.NickName.Contains(keyword) || x.Signature.Contains(keyword));
// 获取总数
var total = await query.CountAsync();
// 分页查询
var users = await query
.Page(request.PageIndex, request.PageSize)
.OrderByDescending(x => x.CreatedAt)
.ToListAsync();
// 获取当前用户ID如果已登录
var currentUserId = _userInfoModel.UserId;
// 获取关注信息
var userIds = users.Select(u => u.Id).ToList();
var followInfo = currentUserId > 0
? await _followsRepository.Select
.Where(f => f.FollowerId == currentUserId && userIds.Contains(f.FollowedUserId))
.ToListAsync()
: new List<T_Follows>();
var followSet = followInfo.Select(f => f.FollowedUserId).ToHashSet();
// 统计每个用户的粉丝数
var followerCountsDict = new Dictionary<long, int>();
foreach (var userId in userIds)
{
var count = await _followsRepository.Select
.Where(f => f.FollowedUserId == userId)
.CountAsync();
followerCountsDict[userId] =(int)count;
}
// 构建返回结果
var items = new List<object>();
foreach (var user in users)
{
items.Add(new SearchUserDto
{
UserId = user.Id,
UserName = "", // T_Users 没有 UserName 字段
NickName = user.NickName ?? "",
Avatar = user.Avatar ?? "",
IsVip = user.IsVip,
IsCertified = user.IsCertified,
Signature = user.Signature ?? "",
FollowerCount = followerCountsDict.GetValueOrDefault(user.Id, 0),
IsFollowed = currentUserId > 0 && followSet.Contains(user.Id)
});
}
var totalPages = (int)Math.Ceiling(total / (double)request.PageSize);
return new BaseResponse<SearchRespDto>(new SearchRespDto
{
PageIndex = request.PageIndex,
PageSize = request.PageSize,
Total =(int)total,
TotalPages = totalPages,
Items = items
});
}
/// <summary>
/// 检查是否关注用户
/// </summary>
/// <param name="currentUserId">当前用户ID</param>
/// <param name="targetUserId">目标用户ID</param>
/// <returns></returns>
private async Task<bool> IsFollowingUser(long currentUserId, long targetUserId)
{
if (currentUserId <= 0) return false;
var count = await _followsRepository.Select
.Where(f => f.FollowerId == currentUserId && f.FollowedUserId == targetUserId)
.CountAsync();
return count > 0;
}
/// <summary>
/// 获取热门搜索关键词
/// </summary>
/// <param name="request">请求参数</param>
/// <returns></returns>
public async Task<BaseResponseList<HotKeywordDto>> GetHotKeywords(GetHotKeywordsReq request)
{
try
{
// 从配置中获取热门关键词
// 这是一个简化的实现,实际项目中可以从数据库或缓存中获取
var hotKeywords = new List<HotKeywordDto>
{
new HotKeywordDto { Keyword = "精彩分享", SearchCount = 1250, Rank = 1 },
new HotKeywordDto { Keyword = "技术交流", SearchCount = 980, Rank = 2 },
new HotKeywordDto { Keyword = "生活分享", SearchCount = 856, Rank = 3 },
new HotKeywordDto { Keyword = "学习心得", SearchCount = 742, Rank = 4 },
new HotKeywordDto { Keyword = "经验分享", SearchCount = 689, Rank = 5 },
new HotKeywordDto { Keyword = "问题求助", SearchCount = 567, Rank = 6 },
new HotKeywordDto { Keyword = "资源分享", SearchCount = 523, Rank = 7 },
new HotKeywordDto { Keyword = "社区活动", SearchCount = 489, Rank = 8 },
new HotKeywordDto { Keyword = "行业动态", SearchCount = 456, Rank = 9 },
new HotKeywordDto { Keyword = "创新想法", SearchCount = 423, Rank = 10 }
};
// 限制返回数量
var result = hotKeywords.Take(Math.Min(request.Limit, 20)).ToList();
return new BaseResponseList<HotKeywordDto>(result);
}
catch (Exception ex)
{
return new BaseResponseList<HotKeywordDto>(ResponseCode.Error, $"获取热门关键词失败:{ex.Message}");
}
}
}
}