diff --git a/miniapp/api/user.js b/miniapp/api/user.js index d9fcb4d..9a474ad 100644 --- a/miniapp/api/user.js +++ b/miniapp/api/user.js @@ -12,12 +12,17 @@ import { get, post } from './request' * * @param {number} pageIndex - 页码 * @param {number} pageSize - 每页数量 + * @param {number} gender - 筛选性别(1=男,2=女,0=不限) * @returns {Promise} 推荐用户列表 */ -export async function getRecommend(pageIndex = 1, pageSize = 10) { +export async function getRecommend(pageIndex = 1, pageSize = 10, gender = 0) { // 如果用户已登录,发送token以便后端排除当前用户 const token = uni.getStorageSync('token') - const response = await get('/users/recommend', { pageIndex, pageSize }, { needAuth: !!token }) + const params = { pageIndex, pageSize } + if (gender > 0) { + params.gender = gender + } + const response = await get('/users/recommend', params, { needAuth: !!token }) return response } diff --git a/miniapp/config/index.js b/miniapp/config/index.js index 6bdbd8d..7794e45 100644 --- a/miniapp/config/index.js +++ b/miniapp/config/index.js @@ -23,7 +23,7 @@ const ENV = { } // 当前环境 - 开发时使用 development,打包时改为 production -const CURRENT_ENV = 'production' +const CURRENT_ENV = 'development' // 导出配置 export const config = { diff --git a/miniapp/pages/index/index.vue b/miniapp/pages/index/index.vue index f9329f8..a035f21 100644 --- a/miniapp/pages/index/index.vue +++ b/miniapp/pages/index/index.vue @@ -90,11 +90,7 @@ - - 今日优质推荐 - 已更新 - - 每天早上五点准时更新 + @@ -240,7 +236,9 @@ noMoreData.value = false } - const res = await getRecommend(pageIndex.value, pageSize) + // 获取用户的性别偏好 + const genderPref = userStore.genderPreference || 0 + const res = await getRecommend(pageIndex.value, pageSize, genderPref) if (res && res.code === 0 && res.data) { const items = res.data.items || [] total.value = res.data.total || 0 @@ -569,7 +567,7 @@ + .badge-icon { + font-size: 32rpx; + margin-right: 12rpx; + } + + .badge-text { + font-size: 26rpx; + color: #1890ff; + font-weight: 500; + } + } + } + + // 未认证状态 + .unverified-section { + height: 100vh; + position: relative; + z-index: 1; + overflow: hidden; + } + + // 可滚动内容区域 + .content-scroll { + position: fixed; + left: 0; + right: 0; + background: transparent; + } + + // 步骤内容区域 + .step-payment, + .step-upload, + .step-result { + padding: 0; + } + + // 固定的步骤指示器 + .step-header-fixed { + position: fixed; + left: 0; + right: 0; + z-index: 99; + padding: 20rpx 30rpx; + background: transparent; + + .step-indicator { + display: flex; + align-items: center; + justify-content: center; + background: #fff; + border-radius: 20rpx; + padding: 24rpx 20rpx; + box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08); + + .step-item { + display: flex; + flex-direction: column; + align-items: center; + + .step-num { + width: 48rpx; + height: 48rpx; + border-radius: 50%; + background: #e0e0e0; + color: #999; + font-size: 24rpx; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 12rpx; + } + + .step-text { + font-size: 22rpx; + color: #999; + } + + &.active { + .step-num { + background: linear-gradient(135deg, #ff6b6b 0%, #ff5252 100%); + color: #fff; + } + + .step-text { + color: #ff6b6b; + font-weight: 500; + } + } + + &.completed { + .step-num { + background: linear-gradient(135deg, #52c41a 0%, #389e0d 100%); + color: #fff; + } + + .step-text { + color: #52c41a; + } + } + } + + .step-line { + width: 80rpx; + height: 4rpx; + background: #e0e0e0; + margin: 0 16rpx; + margin-bottom: 30rpx; + + &.completed { + background: linear-gradient(135deg, #52c41a 0%, #389e0d 100%); + } + } + } + } + + // 步骤指示器(保留旧样式兼容) + .step-header { + padding: 30rpx; + padding-top: 20rpx; + + .step-indicator { + display: flex; + align-items: center; + justify-content: center; + background: #fff; + border-radius: 20rpx; + padding: 24rpx 20rpx; + box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08); + + .step-item { + display: flex; + flex-direction: column; + align-items: center; + + .step-num { + width: 48rpx; + height: 48rpx; + border-radius: 50%; + background: #e0e0e0; + color: #999; + font-size: 24rpx; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 12rpx; + } + + .step-text { + font-size: 22rpx; + color: #999; + } + + &.active { + .step-num { + background: linear-gradient(135deg, #52c41a 0%, #389e0d 100%); + color: #fff; + } + + .step-text { + color: #52c41a; + } + } + } + + .step-line { + width: 80rpx; + height: 4rpx; + background: #e0e0e0; + margin: 0 16rpx; + margin-bottom: 30rpx; + + &.completed { + background: linear-gradient(135deg, #52c41a 0%, #389e0d 100%); + } + } + } + } + + // 步骤1: 支付页面 + .step-payment { + .payment-content { + padding: 40rpx 30rpx; + display: flex; + flex-direction: column; + align-items: center; + position: relative; + + .realname-title-img { + width: 500rpx; + margin-bottom: 40rpx; + } + + // 实名认证的好处 + .benefits-section { + width: 100%; + background: #fff; + border-radius: 24rpx; + padding: 40rpx; + + .benefits-title { + font-size: 36rpx; + font-weight: 600; + color: #1890ff; + margin-bottom: 32rpx; + } + + .benefits-list { + .benefit-item { + display: flex; + align-items: flex-start; + margin-bottom: 28rpx; + line-height: 1.6; + + &:last-child { + margin-bottom: 0; + } + + .benefit-num { + font-size: 30rpx; + color: #333; + flex-shrink: 0; + } + + .benefit-text { + font-size: 30rpx; + color: #333; + + .highlight { + color: #1890ff; + font-weight: 500; + } + } + } + } + } + + // 联系客服 + .contact-service { + position: absolute; + right: 30rpx; + top: 380rpx; + display: flex; + flex-direction: column; + align-items: center; + + .service-avatar { + width: 80rpx; + height: 80rpx; + border-radius: 50%; + margin-bottom: 8rpx; + } + + .service-text { + font-size: 22rpx; + color: #666; + } + } + + .payment-icon { + font-size: 100rpx; + margin-bottom: 24rpx; + } + + .payment-title { + font-size: 40rpx; + font-weight: 600; + color: #333; + margin-bottom: 16rpx; + } + + .payment-desc { + font-size: 28rpx; + color: #666; + margin-bottom: 40rpx; + } + + .payment-benefits { + width: 100%; + background: #fff; + border-radius: 16rpx; + padding: 30rpx; + margin-bottom: 40rpx; + + .benefit-item { + display: flex; + align-items: center; + padding: 16rpx 0; + + .benefit-icon { + width: 40rpx; + height: 40rpx; + background: #52c41a; + border-radius: 50%; + color: #fff; + font-size: 24rpx; + display: flex; + align-items: center; + justify-content: center; + margin-right: 20rpx; + } + + .benefit-text { + font-size: 28rpx; + color: #333; + } + } + } + + .payment-price { + display: flex; + flex-direction: column; + align-items: center; + + .price-label { + font-size: 26rpx; + color: #999; + margin-bottom: 12rpx; + } + + .price-value { + display: flex; + align-items: baseline; + + .symbol { + font-size: 32rpx; + color: #ff6b6b; + font-weight: 500; + } + + .amount { + font-size: 72rpx; + color: #ff6b6b; + font-weight: 700; + } + } + } + } + } + + // 步骤2: 上传页面 + .step-upload { + .upload-content { + padding: 40rpx 30rpx; + display: flex; + flex-direction: column; + gap: 40rpx; + + .upload-tip-text { + display: flex; + align-items: baseline; + justify-content: center; + margin-bottom: 20rpx; + + .tip-main { + font-size: 34rpx; + font-weight: 600; + color: #333; + } + + .tip-sub { + font-size: 26rpx; + color: #999; + margin-left: 8rpx; + } + } + + .upload-card-new { + background: #fff; + border-radius: 24rpx; + padding: 40rpx; + display: flex; + flex-direction: column; + align-items: center; + + .card-frame { + width: 100%; + height: 320rpx; + background: #F6F8FA; + border-radius: 10rpx; + display: flex; + align-items: center; + justify-content: center; + + .card-placeholder-img { + width: 400rpx; + height: 260rpx; + } + + .card-image-uploaded { + width: 100%; + height: 100%; + border-radius: 10rpx; + } + } + + .card-label-new { + margin-top: 24rpx; + font-size: 32rpx; + color: #333; + font-weight: 500; + } + } + + .upload-title { + font-size: 32rpx; + font-weight: 600; + color: #333; + text-align: center; + margin-bottom: 12rpx; + } + + .upload-desc { + font-size: 26rpx; + color: #999; + text-align: center; + margin-bottom: 40rpx; + } + + .upload-card { + background: #fff; + border-radius: 16rpx; + padding: 24rpx; + margin-bottom: 24rpx; + + .card-label { + font-size: 28rpx; + font-weight: 500; + color: #333; + margin-bottom: 20rpx; + } + + .card-upload { + width: 100%; + height: 320rpx; + background: #f8f8f8; + border: 2rpx dashed #ddd; + border-radius: 12rpx; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + + &.has-image { + border-style: solid; + border-color: #52c41a; + } + + .card-image { + width: 100%; + height: 100%; + } + + .card-placeholder { + display: flex; + flex-direction: column; + align-items: center; + + .placeholder-icon { + font-size: 60rpx; + margin-bottom: 16rpx; + } + + .placeholder-text { + font-size: 26rpx; + color: #999; + } + } + } + } + + .upload-tips { + background: #fffbe6; + border-radius: 12rpx; + padding: 24rpx; + margin-top: 20rpx; + + .tips-title { + font-size: 26rpx; + font-weight: 500; + color: #d48806; + margin-bottom: 12rpx; + } + + .tips-item { + font-size: 24rpx; + color: #d48806; + line-height: 1.8; + } + } + } + } + + // 步骤3: 结果页面 + .step-result { + .result-content { + padding: 40rpx 30rpx; + display: flex; + flex-direction: column; + align-items: center; + + .result-shield-img { + width: 400rpx; + margin-bottom: 60rpx; + } + + .result-info-card { + width: 100%; + background: #fff; + border-radius: 24rpx; + padding: 40rpx; + + .card-title { + font-size: 34rpx; + font-weight: 600; + color: #333; + margin-bottom: 32rpx; + } + + .info-row { + display: flex; + align-items: center; + justify-content: space-between; + padding: 24rpx 0; + border-bottom: 1rpx solid #f5f5f5; + + &:last-child { + border-bottom: none; + } + + .info-label { + font-size: 30rpx; + color: #666; + } + + .info-value { + font-size: 30rpx; + color: #333; + } + } + } + + .result-icon { + width: 120rpx; + height: 120rpx; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 60rpx; + color: #fff; + margin-bottom: 30rpx; + + &.success { + background: linear-gradient(135deg, #52c41a 0%, #389e0d 100%); + } + } + + .result-title { + font-size: 36rpx; + font-weight: 600; + color: #333; + margin-bottom: 16rpx; + } + + .result-desc { + font-size: 28rpx; + color: #666; + margin-bottom: 40rpx; + } + + .result-info { + width: 100%; + background: #fff; + border-radius: 16rpx; + padding: 30rpx; + + .info-item { + display: flex; + align-items: center; + padding: 20rpx 0; + border-bottom: 1rpx solid #f5f5f5; + + &:last-child { + border-bottom: none; + } + + .label { + font-size: 28rpx; + color: #666; + width: 160rpx; + } + + .value { + font-size: 28rpx; + color: #333; + font-weight: 500; + } + } + } + } + } + + // 底部操作按钮 + .bottom-action { + position: fixed; + left: 0; + right: 0; + bottom: 0; + background: #fff; + padding: 20rpx 30rpx; + padding-bottom: calc(20rpx + env(safe-area-inset-bottom)); + box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05); + + // 步骤1的两个按钮 + .step1-buttons { + display: flex; + flex-direction: column; + gap: 20rpx; + + .btn-member-wrapper { + position: relative; + + .btn-member { + width: 100%; + height: 96rpx; + display: flex; + align-items: center; + justify-content: center; + border-radius: 48rpx; + border: none; + background: linear-gradient(135deg, #ABCEFF 0%, #156EFF 100%); + + &::after { + border: none; + } + + text { + font-size: 32rpx; + font-weight: 600; + color: #fff; + } + } + + .btn-tag { + position: absolute; + right: 40rpx; + top: -16rpx; + background: linear-gradient(135deg, #ff6b6b 0%, #ff5252 100%); + color: #fff; + font-size: 22rpx; + padding: 6rpx 16rpx; + border-radius: 16rpx; + } + } + + .btn-pay-simple { + width: 100%; + height: 96rpx; + display: flex; + align-items: center; + justify-content: center; + border-radius: 48rpx; + border: 2rpx solid #0062FF; + background: #fff; + + &::after { + border: none; + } + + text { + font-size: 32rpx; + font-weight: 600; + color: #46A2FF; + } + + &[disabled] { + opacity: 0.6; + } + } + } + + .btn-pay, + .btn-submit, + .btn-done { + width: 100%; + height: 96rpx; + display: flex; + align-items: center; + justify-content: center; + border-radius: 48rpx; + border: none; + + &::after { + border: none; + } + + text { + font-size: 32rpx; + font-weight: 600; + } + + &[disabled] { + opacity: 0.6; + } + } + + .btn-pay { + background: linear-gradient(135deg, #ff6b6b 0%, #ff5252 100%); + + text { + color: #fff; + } + } + + .btn-submit { + background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%); + + text { + color: #fff; + } + } + + .btn-done { + background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%); + + text { + color: #fff; + } + } + } + \ No newline at end of file diff --git a/miniapp/static/ic_real_name.png b/miniapp/static/ic_real_name.png new file mode 100644 index 0000000..af49e5a Binary files /dev/null and b/miniapp/static/ic_real_name.png differ diff --git a/miniapp/static/real_name_card_1.png b/miniapp/static/real_name_card_1.png new file mode 100644 index 0000000..f04fbec Binary files /dev/null and b/miniapp/static/real_name_card_1.png differ diff --git a/miniapp/static/real_name_card_2.png b/miniapp/static/real_name_card_2.png new file mode 100644 index 0000000..0238f1e Binary files /dev/null and b/miniapp/static/real_name_card_2.png differ diff --git a/miniapp/static/recommend_title.png b/miniapp/static/recommend_title.png new file mode 100644 index 0000000..53bfda4 Binary files /dev/null and b/miniapp/static/recommend_title.png differ diff --git a/server/src/XiangYi.AppApi/Controllers/UserController.cs b/server/src/XiangYi.AppApi/Controllers/UserController.cs index ec3c45d..0f5fd3a 100644 --- a/server/src/XiangYi.AppApi/Controllers/UserController.cs +++ b/server/src/XiangYi.AppApi/Controllers/UserController.cs @@ -45,13 +45,14 @@ public class UserController : ControllerBase /// /// 页码 /// 每页数量 + /// 筛选性别(1=男,2=女,0=不限) /// 推荐用户列表 [HttpGet("recommend")] [AllowAnonymous] - public async Task>> GetRecommend([FromQuery] int pageIndex = 1, [FromQuery] int pageSize = 10) + public async Task>> GetRecommend([FromQuery] int pageIndex = 1, [FromQuery] int pageSize = 10, [FromQuery] int gender = 0) { var userId = GetCurrentUserId(); - var result = await _recommendService.GetDailyRecommendAsync(userId, pageIndex, pageSize); + var result = await _recommendService.GetDailyRecommendAsync(userId, pageIndex, pageSize, gender); return ApiResponse>.Success(result); } diff --git a/server/src/XiangYi.Application/Interfaces/IRecommendService.cs b/server/src/XiangYi.Application/Interfaces/IRecommendService.cs index a40316e..d06c2ea 100644 --- a/server/src/XiangYi.Application/Interfaces/IRecommendService.cs +++ b/server/src/XiangYi.Application/Interfaces/IRecommendService.cs @@ -13,8 +13,9 @@ public interface IRecommendService /// 用户ID,0表示未登录 /// 页码 /// 每页数量 + /// 筛选性别(1=男,2=女,0=不限) /// 分页推荐用户列表 - Task> GetDailyRecommendAsync(long userId, int pageIndex = 1, int pageSize = 10); + Task> GetDailyRecommendAsync(long userId, int pageIndex = 1, int pageSize = 10, int gender = 0); /// /// 为指定用户生成每日推荐列表 diff --git a/server/src/XiangYi.Application/Services/ChatService.cs b/server/src/XiangYi.Application/Services/ChatService.cs index 28f3662..61ad423 100644 --- a/server/src/XiangYi.Application/Services/ChatService.cs +++ b/server/src/XiangYi.Application/Services/ChatService.cs @@ -265,12 +265,7 @@ public class ChatService : IChatService !m.IsRead && m.Status == (int)MessageStatus.Normal); - if (!unreadMessages.Any()) - { - return 0; - } - - // 标记为已读 + // 标记消息为已读 foreach (var message in unreadMessages) { message.IsRead = true; @@ -278,17 +273,24 @@ public class ChatService : IChatService await _messageRepository.UpdateAsync(message); } - // 重置会话未读数 - if (session.User1Id == userId) + // 始终重置会话未读数(即使没有未读消息,也确保未读数为0) + var needUpdateSession = false; + if (session.User1Id == userId && session.User1UnreadCount > 0) { session.User1UnreadCount = 0; + needUpdateSession = true; } - else + else if (session.User2Id == userId && session.User2UnreadCount > 0) { session.User2UnreadCount = 0; + needUpdateSession = true; + } + + if (needUpdateSession) + { + session.UpdateTime = DateTime.Now; + await _sessionRepository.UpdateAsync(session); } - session.UpdateTime = DateTime.Now; - await _sessionRepository.UpdateAsync(session); _logger.LogInformation("标记消息已读: SessionId={SessionId}, UserId={UserId}, Count={Count}", sessionId, userId, unreadMessages.Count); diff --git a/server/src/XiangYi.Application/Services/RecommendService.cs b/server/src/XiangYi.Application/Services/RecommendService.cs index f1a37b9..70e7fb9 100644 --- a/server/src/XiangYi.Application/Services/RecommendService.cs +++ b/server/src/XiangYi.Application/Services/RecommendService.cs @@ -76,14 +76,14 @@ public class RecommendService : IRecommendService } /// - public async Task> GetDailyRecommendAsync(long userId, int pageIndex = 1, int pageSize = 10) + public async Task> GetDailyRecommendAsync(long userId, int pageIndex = 1, int pageSize = 10, int gender = 0) { var today = DateTime.Today; // 未登录用户,返回默认推荐(随机推荐已审核通过的用户) if (userId <= 0) { - return await GetDefaultRecommendAsync(pageIndex, pageSize, 0); + return await GetDefaultRecommendAsync(pageIndex, pageSize, 0, gender); } // 获取今日推荐列表 @@ -98,7 +98,7 @@ public class RecommendService : IRecommendService // 如果生成失败(用户资料未完成等),返回默认推荐但排除当前用户 if (generatedCount == 0) { - return await GetDefaultRecommendAsync(pageIndex, pageSize, userId); + return await GetDefaultRecommendAsync(pageIndex, pageSize, userId, gender); } recommends = await _recommendRepository.GetListAsync( @@ -175,12 +175,19 @@ public class RecommendService : IRecommendService /// 页码 /// 每页数量 /// 要排除的用户ID(当前登录用户) - private async Task> GetDefaultRecommendAsync(int pageIndex, int pageSize, long excludeUserId = 0) + /// 筛选性别(1=男,2=女,0=不限) + private async Task> GetDefaultRecommendAsync(int pageIndex, int pageSize, long excludeUserId = 0, int gender = 0) { // 获取已完成资料、状态正常的用户,排除当前用户 var allUsers = await _userRepository.GetListAsync( u => u.IsProfileCompleted && u.Status == 1 && (excludeUserId == 0 || u.Id != excludeUserId)); + // 如果指定了性别筛选,过滤用户 + if (gender > 0) + { + allUsers = allUsers.Where(u => u.Gender == gender).ToList(); + } + // 过滤出审核通过的用户 var validUsers = new List<(Core.Entities.Biz.User User, Core.Entities.Biz.UserProfile Profile)>(); foreach (var user in allUsers.OrderByDescending(u => u.CreateTime))