diff --git a/miniapp/api/user.js b/miniapp/api/user.js index 907ba37..d9fcb4d 100644 --- a/miniapp/api/user.js +++ b/miniapp/api/user.js @@ -15,7 +15,9 @@ import { get, post } from './request' * @returns {Promise} 推荐用户列表 */ export async function getRecommend(pageIndex = 1, pageSize = 10) { - const response = await get('/users/recommend', { pageIndex, pageSize }, { needAuth: false }) + // 如果用户已登录,发送token以便后端排除当前用户 + const token = uni.getStorageSync('token') + const response = await get('/users/recommend', { pageIndex, pageSize }, { needAuth: !!token }) return response } diff --git a/miniapp/config/index.js b/miniapp/config/index.js index 1887089..9260896 100644 --- a/miniapp/config/index.js +++ b/miniapp/config/index.js @@ -21,7 +21,7 @@ const ENV = { } // 当前环境 - 开发时使用 development,打包时改为 production -const CURRENT_ENV = 'production' +const CURRENT_ENV = 'development' // 导出配置 export const config = { diff --git a/miniapp/pages/profile/edit.vue b/miniapp/pages/profile/edit.vue index 6c57790..6c9e1ab 100644 --- a/miniapp/pages/profile/edit.vue +++ b/miniapp/pages/profile/edit.vue @@ -585,6 +585,7 @@ import { ref, reactive, computed, onMounted, watch } from 'vue' import { useUserStore } from '@/store/user.js' import { createOrUpdate, getMyProfile, uploadPhotos, deletePhoto, cleanupOrphanPhotos } from '@/api/profile.js' +import { decryptPhone } from '@/api/user.js' import { generateNickname, getBirthYearRange } from '@/utils/format.js' import config from '@/config/index.js' @@ -1095,15 +1096,19 @@ const handleChoosePhoto = () => { const handleDeletePhoto = async (index) => { const photo = formData.photos[index] - if (photo.id) { - try { - await deletePhoto(photo.id) - } catch (error) { - console.error('删除照片失败:', error) - } - } + uni.showLoading({ title: '删除中...' }) - formData.photos.splice(index, 1) + try { + if (photo.id) { + await deletePhoto(photo.id) + } + formData.photos.splice(index, 1) + } catch (error) { + console.error('删除照片失败:', error) + uni.showToast({ title: '删除失败', icon: 'none' }) + } finally { + uni.hideLoading() + } } // 手机号验证 - 处理微信获取手机号回调 @@ -1123,7 +1128,6 @@ const handleGetPhoneNumber = async (e) => { uni.showLoading({ title: '验证中...' }) try { - const { decryptPhone } = await import('@/api/user.js') const res = await decryptPhone(code) if (res && res.code === 0 && res.data?.phone) { @@ -1322,6 +1326,12 @@ const loadProfile = async () => { formData.introduction = profile.introduction || '' formData.weChatNo = profile.weChatNo || '' + // 手机号 + if (profile.phone) { + formData.phone = profile.phone + phoneVerified.value = true + } + // 照片 - 需要拼接完整URL(使用统一配置的地址) if (profile.photos && profile.photos.length > 0) { formData.photos = profile.photos.map(p => ({ diff --git a/miniapp/pages/profile/personal.vue b/miniapp/pages/profile/personal.vue index 45cd9bc..3effdfb 100644 --- a/miniapp/pages/profile/personal.vue +++ b/miniapp/pages/profile/personal.vue @@ -133,7 +133,7 @@ export default { // 上传图片(使用统一配置的地址) const uploadRes = await new Promise((resolve, reject) => { uni.uploadFile({ - url: `${API_BASE_URL}/api/app/upload/image`, + url: `${API_BASE_URL}/upload/image`, filePath: tempFilePath, name: 'file', header: { diff --git a/server/src/XiangYi.Application/DTOs/Requests/ProfileRequests.cs b/server/src/XiangYi.Application/DTOs/Requests/ProfileRequests.cs index df8d5d1..1ddbdb5 100644 --- a/server/src/XiangYi.Application/DTOs/Requests/ProfileRequests.cs +++ b/server/src/XiangYi.Application/DTOs/Requests/ProfileRequests.cs @@ -110,6 +110,11 @@ public class ProfileRequest /// public string WeChatNo { get; set; } = string.Empty; + /// + /// 手机号 + /// + public string? Phone { get; set; } + /// /// 择偶要求 /// diff --git a/server/src/XiangYi.Application/DTOs/Responses/ProfileResponses.cs b/server/src/XiangYi.Application/DTOs/Responses/ProfileResponses.cs index 882d8ad..1fb8cdf 100644 --- a/server/src/XiangYi.Application/DTOs/Responses/ProfileResponses.cs +++ b/server/src/XiangYi.Application/DTOs/Responses/ProfileResponses.cs @@ -130,6 +130,11 @@ public class ProfileResponse /// public string WeChatNo { get; set; } = string.Empty; + /// + /// 手机号 + /// + public string? Phone { get; set; } + /// /// 审核状态:0待审核 1已通过 2已拒绝 /// diff --git a/server/src/XiangYi.Application/Services/ProfileService.cs b/server/src/XiangYi.Application/Services/ProfileService.cs index a0a2837..a9cfdbf 100644 --- a/server/src/XiangYi.Application/Services/ProfileService.cs +++ b/server/src/XiangYi.Application/Services/ProfileService.cs @@ -21,6 +21,7 @@ public class ProfileService : IProfileService private readonly IRepository _profileRepository; private readonly IRepository _photoRepository; private readonly IRepository _requirementRepository; + private readonly IRepository _recommendRepository; private readonly IStorageProvider _storageProvider; private readonly ILogger _logger; @@ -34,6 +35,7 @@ public class ProfileService : IProfileService IRepository profileRepository, IRepository photoRepository, IRepository requirementRepository, + IRepository recommendRepository, IStorageProvider storageProvider, ILogger logger) { @@ -41,6 +43,7 @@ public class ProfileService : IProfileService _profileRepository = profileRepository; _photoRepository = photoRepository; _requirementRepository = requirementRepository; + _recommendRepository = recommendRepository; _storageProvider = storageProvider; _logger = logger; } @@ -146,9 +149,18 @@ public class ProfileService : IProfileService user.Nickname = nickname; user.Gender = request.ChildGender; user.IsProfileCompleted = true; + if (!string.IsNullOrEmpty(request.Phone)) + { + user.Phone = request.Phone; + } user.UpdateTime = DateTime.Now; await _userRepository.UpdateAsync(user); + // 6. 清除今日推荐(性别等关键信息变更后需要重新生成推荐) + var today = DateTime.Today; + await _recommendRepository.DeleteAsync(r => r.UserId == userId && r.RecommendDate == today); + _logger.LogInformation("已清除用户今日推荐: UserId={UserId}", userId); + return profileId; } @@ -202,6 +214,7 @@ public class ProfileService : IProfileService Nickname = user.Nickname, XiangQinNo = user.XiangQinNo, Avatar = user.Avatar, + Phone = user.Phone, Relationship = profile.Relationship, Surname = profile.Surname, ChildGender = profile.ChildGender, diff --git a/server/src/XiangYi.Application/Services/RecommendService.cs b/server/src/XiangYi.Application/Services/RecommendService.cs index 95ac887..f1a37b9 100644 --- a/server/src/XiangYi.Application/Services/RecommendService.cs +++ b/server/src/XiangYi.Application/Services/RecommendService.cs @@ -83,7 +83,7 @@ public class RecommendService : IRecommendService // 未登录用户,返回默认推荐(随机推荐已审核通过的用户) if (userId <= 0) { - return await GetDefaultRecommendAsync(pageIndex, pageSize); + return await GetDefaultRecommendAsync(pageIndex, pageSize, 0); } // 获取今日推荐列表 @@ -92,8 +92,15 @@ public class RecommendService : IRecommendService if (!recommends.Any()) { - // 如果今日没有推荐,生成新的推荐 - await GenerateDailyRecommendForUserAsync(userId); + // 如果今日没有推荐,尝试生成新的推荐 + var generatedCount = await GenerateDailyRecommendForUserAsync(userId); + + // 如果生成失败(用户资料未完成等),返回默认推荐但排除当前用户 + if (generatedCount == 0) + { + return await GetDefaultRecommendAsync(pageIndex, pageSize, userId); + } + recommends = await _recommendRepository.GetListAsync( r => r.UserId == userId && r.RecommendDate == today); } @@ -163,13 +170,16 @@ public class RecommendService : IRecommendService } /// - /// 获取默认推荐列表(未登录用户) + /// 获取默认推荐列表(未登录用户或无每日推荐时) /// - private async Task> GetDefaultRecommendAsync(int pageIndex, int pageSize) + /// 页码 + /// 每页数量 + /// 要排除的用户ID(当前登录用户) + private async Task> GetDefaultRecommendAsync(int pageIndex, int pageSize, long excludeUserId = 0) { - // 获取已完成资料、状态正常的用户 + // 获取已完成资料、状态正常的用户,排除当前用户 var allUsers = await _userRepository.GetListAsync( - u => u.IsProfileCompleted && u.Status == 1); + u => u.IsProfileCompleted && u.Status == 1 && (excludeUserId == 0 || u.Id != excludeUserId)); // 过滤出审核通过的用户 var validUsers = new List<(Core.Entities.Biz.User User, Core.Entities.Biz.UserProfile Profile)>(); diff --git a/server/src/XiangYi.Infrastructure/WeChat/WeChatService.cs b/server/src/XiangYi.Infrastructure/WeChat/WeChatService.cs index 5a56070..dc346e3 100644 --- a/server/src/XiangYi.Infrastructure/WeChat/WeChatService.cs +++ b/server/src/XiangYi.Infrastructure/WeChat/WeChatService.cs @@ -99,10 +99,27 @@ public class WeChatService : IWeChatService } var url = $"https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token={accessToken}"; - var requestBody = new { code }; + var requestBodyJson = System.Text.Json.JsonSerializer.Serialize(new { code }); - var response = await _httpClient.PostAsJsonAsync(url, requestBody); - var result = await response.Content.ReadFromJsonAsync(); + var content = new StringContent(requestBodyJson, System.Text.Encoding.UTF8, "application/json"); + var response = await _httpClient.PostAsync(url, content); + var responseContent = await response.Content.ReadAsStringAsync(); + + if (string.IsNullOrWhiteSpace(responseContent)) + { + _logger.LogError("微信接口返回空响应"); + await _cache.RemoveAsync(AccessTokenCacheKey); + return null; + } + + var result = System.Text.Json.JsonSerializer.Deserialize(responseContent); + + // 如果是AccessToken过期,清除缓存 + if (result?.ErrCode == 40001 || result?.ErrCode == 42001) + { + _logger.LogWarning("AccessToken已过期,清除缓存"); + await _cache.RemoveAsync(AccessTokenCacheKey); + } if (result?.ErrCode != 0) { @@ -365,7 +382,9 @@ public class WeChatService : IWeChatService $"&appid={_options.MiniProgram.AppId}" + $"&secret={_options.MiniProgram.AppSecret}"; - var response = await _httpClient.GetFromJsonAsync(url); + var httpResponse = await _httpClient.GetAsync(url); + var responseContent = await httpResponse.Content.ReadAsStringAsync(); + var response = System.Text.Json.JsonSerializer.Deserialize(responseContent); if (response == null || string.IsNullOrEmpty(response.AccessToken)) {