问题修改.

This commit is contained in:
18631081161 2026-01-22 00:58:45 +08:00
parent 3f11b13d5c
commit d5e677a09b
17 changed files with 1705 additions and 1294 deletions

View File

@ -12,12 +12,17 @@ import { get, post } from './request'
*
* @param {number} pageIndex - 页码
* @param {number} pageSize - 每页数量
* @param {number} gender - 筛选性别1=2=0=不限
* @returns {Promise<Object>} 推荐用户列表
*/
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
}

View File

@ -23,7 +23,7 @@ const ENV = {
}
// 当前环境 - 开发时使用 development打包时改为 production
const CURRENT_ENV = 'production'
const CURRENT_ENV = 'development'
// 导出配置
export const config = {

View File

@ -90,11 +90,7 @@
<!-- 推荐标题栏 -->
<view class="section-header">
<view class="section-title-wrapper">
<text class="section-title-main">今日优质推荐</text>
<text class="section-title-highlight">已更新</text>
</view>
<text class="section-subtitle">每天早上五点准时更新</text>
<image class="recommend-title-img" src="/static/recommend_title.png" mode="widthFix" />
</view>
<!-- 用户列表 -->
@ -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 @@
<style lang="scss" scoped>
.home-page {
height: 100vh;
background-color: #f8f8f8;
background-color: #F3F3F3;
}
//
@ -654,7 +652,6 @@
//
.kingkong-section {
padding: 20rpx;
background-color: #fff;
.kingkong-grid {
display: flex;
@ -689,7 +686,10 @@
align-items: center;
justify-content: center;
padding: 5rpx 20rpx 24rpx;
background-color: #f8f8f8;
.recommend-title-img {
width: 410rpx;
}
.section-title-wrapper {
display: flex;

View File

@ -430,7 +430,7 @@ export default {
.tier-badge {
position: absolute;
top: -2rpx;
top: -20rpx;
right: -2rpx;
padding: 6rpx 16rpx;
background: linear-gradient(135deg, #ff6b6b 0%, #ff5252 100%);

View File

@ -206,16 +206,47 @@
if (!userStore.isLoggedIn) return
try {
//
const lastViewedTime = uni.getStorageSync('lastViewedMeTime') || 0
const lastFavoritedTime = uni.getStorageSync('lastFavoritedMeTime') || 0
const lastUnlockedTime = uni.getStorageSync('lastUnlockedMeTime') || 0
const [viewedRes, favoritedRes, unlockedRes] = await Promise.all([
getViewedMe(1, 1).catch(() => null),
getFavoritedMe(1, 1).catch(() => null),
getUnlockedMe(1, 1).catch(() => null)
getViewedMe(1, 100).catch(() => null),
getFavoritedMe(1, 100).catch(() => null),
getUnlockedMe(1, 100).catch(() => null)
])
//
let viewedCount = 0
let favoritedCount = 0
let unlockedCount = 0
if (viewedRes?.data?.items) {
viewedCount = viewedRes.data.items.filter(item => {
const itemTime = new Date(item.viewTime || item.createTime).getTime()
return itemTime > lastViewedTime
}).length
}
if (favoritedRes?.data?.items) {
favoritedCount = favoritedRes.data.items.filter(item => {
const itemTime = new Date(item.favoriteTime || item.createTime).getTime()
return itemTime > lastFavoritedTime
}).length
}
if (unlockedRes?.data?.items) {
unlockedCount = unlockedRes.data.items.filter(item => {
const itemTime = new Date(item.unlockTime || item.createTime).getTime()
return itemTime > lastUnlockedTime
}).length
}
interactCounts.value = {
viewedMe: viewedRes?.data?.total || 0,
favoritedMe: favoritedRes?.data?.total || 0,
unlockedMe: unlockedRes?.data?.total || 0
viewedMe: viewedCount,
favoritedMe: favoritedCount,
unlockedMe: unlockedCount
}
} catch (error) {
console.error('加载互动统计失败:', error)
@ -277,6 +308,19 @@
//
const navigateTo = (url) => {
//
const now = Date.now()
if (url.includes('viewedMe')) {
uni.setStorageSync('lastViewedMeTime', now)
interactCounts.value.viewedMe = 0
} else if (url.includes('favoritedMe')) {
uni.setStorageSync('lastFavoritedMeTime', now)
interactCounts.value.favoritedMe = 0
} else if (url.includes('unlockedMe')) {
uni.setStorageSync('lastUnlockedMeTime', now)
interactCounts.value.unlockedMe = 0
}
uni.navigateTo({
url
})

View File

@ -200,16 +200,47 @@ export default {
if (!userStore.isLoggedIn) return
try {
//
const lastViewedTime = uni.getStorageSync('lastViewedMeTime') || 0
const lastFavoritedTime = uni.getStorageSync('lastFavoritedMeTime') || 0
const lastUnlockedTime = uni.getStorageSync('lastUnlockedMeTime') || 0
const [viewedRes, favoritedRes, unlockedRes] = await Promise.all([
getViewedMe(1, 1),
getFavoritedMe(1, 1),
getUnlockedMe(1, 1)
getViewedMe(1, 100).catch(() => null),
getFavoritedMe(1, 100).catch(() => null),
getUnlockedMe(1, 100).catch(() => null)
])
//
let viewedCount = 0
let favoritedCount = 0
let unlockedCount = 0
if (viewedRes?.data?.items) {
viewedCount = viewedRes.data.items.filter(item => {
const itemTime = new Date(item.viewTime || item.createTime).getTime()
return itemTime > lastViewedTime
}).length
}
if (favoritedRes?.data?.items) {
favoritedCount = favoritedRes.data.items.filter(item => {
const itemTime = new Date(item.favoriteTime || item.createTime).getTime()
return itemTime > lastFavoritedTime
}).length
}
if (unlockedRes?.data?.items) {
unlockedCount = unlockedRes.data.items.filter(item => {
const itemTime = new Date(item.unlockTime || item.createTime).getTime()
return itemTime > lastUnlockedTime
}).length
}
interactCounts.value = {
viewedMe: viewedRes?.data?.total || 0,
favoritedMe: favoritedRes?.data?.total || 0,
unlockedMe: unlockedRes?.data?.total || 0
viewedMe: viewedCount,
favoritedMe: favoritedCount,
unlockedMe: unlockedCount
}
} catch (error) {
console.error('加载互动统计失败:', error)
@ -299,8 +330,21 @@ export default {
})
}
//
//
const navigateTo = (url) => {
//
const now = Date.now()
if (url.includes('viewedMe')) {
uni.setStorageSync('lastViewedMeTime', now)
interactCounts.value.viewedMe = 0
} else if (url.includes('favoritedMe')) {
uni.setStorageSync('lastFavoritedMeTime', now)
interactCounts.value.favoritedMe = 0
} else if (url.includes('unlockedMe')) {
uni.setStorageSync('lastUnlockedMeTime', now)
interactCounts.value.unlockedMe = 0
}
uni.navigateTo({ url })
}

View File

@ -138,13 +138,14 @@
@click="previewPhoto(index)"
>
<image
v-if="userDetail.isPhotoPublic || isUnlocked"
class="photo-image"
:class="{ 'photo-blurred': !userDetail.isPhotoPublic && !isUnlocked }"
:src="photo.photoUrl"
mode="aspectFill"
/>
<view v-else class="photo-blur">
<text class="blur-text">私密</text>
<view v-if="!userDetail.isPhotoPublic && !isUnlocked" class="photo-lock-overlay">
<text class="lock-icon">🔒</text>
<text class="lock-text">私密</text>
</view>
</view>
</view>
@ -936,10 +937,40 @@ onShareAppMessage(() => {
aspect-ratio: 1;
border-radius: 16rpx;
overflow: hidden;
position: relative;
.photo-image {
width: 100%;
height: 100%;
&.photo-blurred {
filter: blur(20px);
transform: scale(1.1);
}
}
.photo-lock-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.2);
.lock-icon {
font-size: 40rpx;
margin-bottom: 8rpx;
}
.lock-text {
font-size: 24rpx;
color: #fff;
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.3);
}
}
.photo-blur {

View File

@ -18,32 +18,27 @@
<!-- 头像区域 -->
<view class="avatar-section" :style="{ marginTop: (statusBarHeight + 44) + 'px' }">
<view class="avatar-wrapper" @click="handleChangeAvatar">
<image
class="avatar-img"
:src="avatarLoadError ? defaultAvatar : (userInfo.avatar || defaultAvatar)"
mode="aspectFill"
@error="onAvatarError"
/>
<view class="avatar-edit-icon">
<text></text>
<button class="avatar-wrapper-btn" open-type="chooseAvatar" @chooseavatar="handleChooseWxAvatar">
<view class="avatar-wrapper">
<image
class="avatar-img"
:src="avatarLoadError ? defaultAvatar : (userInfo.avatar || defaultAvatar)"
mode="aspectFill"
@error="onAvatarError"
/>
<view class="avatar-edit-icon">
<text></text>
</view>
</view>
</view>
</button>
</view>
<!-- 表单区域 -->
<view class="form-section">
<view class="form-item">
<text class="form-label">用户名</text>
<view class="form-input">
<input
type="text"
:value="nickname"
@input="e => nickname = e.detail.value"
placeholder="请输入用户名"
maxlength="20"
class="input-field"
/>
<view class="form-input disabled">
<text>{{ userInfo.nickname || '未设置' }}</text>
</view>
</view>
<view class="form-item">
@ -57,7 +52,7 @@
<!-- 保存按钮 -->
<view class="btn-section">
<button class="btn-save" @click="handleSave">保存</button>
<button class="btn-save" @click="handleBack">返回</button>
</view>
</view>
</template>
@ -126,54 +121,66 @@ export default {
sourceType: ['album', 'camera'],
success: async (res) => {
const tempFilePath = res.tempFilePaths[0]
uni.showLoading({ title: '上传中...' })
try {
// 使
const uploadRes = await new Promise((resolve, reject) => {
uni.uploadFile({
url: `${API_BASE_URL}/upload/image`,
filePath: tempFilePath,
name: 'file',
header: {
Authorization: `Bearer ${userStore.token}`
},
success: (res) => {
if (res.statusCode === 200) {
resolve(JSON.parse(res.data))
} else {
reject(new Error('上传失败'))
}
},
fail: reject
})
})
if (uploadRes.code === 0 && uploadRes.data?.url) {
// API
const saveRes = await updateAvatar(uploadRes.data.url)
if (saveRes && saveRes.code === 0) {
// store
userStore.updateUserInfo({ avatar: uploadRes.data.url })
avatarLoadError.value = false
uni.showToast({ title: '头像更新成功', icon: 'success' })
} else {
uni.showToast({ title: saveRes?.message || '保存失败', icon: 'none' })
}
} else {
uni.showToast({ title: uploadRes.message || '上传失败', icon: 'none' })
}
} catch (error) {
console.error('上传头像失败:', error)
uni.showToast({ title: '上传失败', icon: 'none' })
} finally {
uni.hideLoading()
}
await uploadAvatarFile(tempFilePath)
}
})
}
//
const handleChooseWxAvatar = async (e) => {
const avatarUrl = e.detail.avatarUrl
if (avatarUrl) {
await uploadAvatarFile(avatarUrl)
}
}
//
const uploadAvatarFile = async (tempFilePath) => {
uni.showLoading({ title: '上传中...' })
try {
// 使
const uploadRes = await new Promise((resolve, reject) => {
uni.uploadFile({
url: `${API_BASE_URL}/upload/image`,
filePath: tempFilePath,
name: 'file',
header: {
Authorization: `Bearer ${userStore.token}`
},
success: (res) => {
if (res.statusCode === 200) {
resolve(JSON.parse(res.data))
} else {
reject(new Error('上传失败'))
}
},
fail: reject
})
})
if (uploadRes.code === 0 && uploadRes.data?.url) {
// API
const saveRes = await updateAvatar(uploadRes.data.url)
if (saveRes && saveRes.code === 0) {
// store
userStore.updateUserInfo({ avatar: uploadRes.data.url })
avatarLoadError.value = false
uni.showToast({ title: '头像更新成功', icon: 'success' })
} else {
uni.showToast({ title: saveRes?.message || '保存失败', icon: 'none' })
}
} else {
uni.showToast({ title: uploadRes.message || '上传失败', icon: 'none' })
}
} catch (error) {
console.error('上传头像失败:', error)
uni.showToast({ title: '上传失败', icon: 'none' })
} finally {
uni.hideLoading()
}
}
//
const handleCopyXiangQinNo = () => {
const xiangQinNo = userStore.xiangQinNo
@ -258,6 +265,7 @@ export default {
onAvatarError,
handleBack,
handleChangeAvatar,
handleChooseWxAvatar,
handleCopyXiangQinNo,
handleSave,
initNickname
@ -347,6 +355,18 @@ export default {
justify-content: center;
padding: 60rpx 0;
.avatar-wrapper-btn {
padding: 0;
margin: 0;
background: transparent;
border: none;
line-height: normal;
&::after {
border: none;
}
}
.avatar-wrapper {
position: relative;
width: 180rpx;

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@ -45,13 +45,14 @@ public class UserController : ControllerBase
/// </summary>
/// <param name="pageIndex">页码</param>
/// <param name="pageSize">每页数量</param>
/// <param name="gender">筛选性别1=男2=女0=不限)</param>
/// <returns>推荐用户列表</returns>
[HttpGet("recommend")]
[AllowAnonymous]
public async Task<ApiResponse<PagedResult<RecommendUserResponse>>> GetRecommend([FromQuery] int pageIndex = 1, [FromQuery] int pageSize = 10)
public async Task<ApiResponse<PagedResult<RecommendUserResponse>>> 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<PagedResult<RecommendUserResponse>>.Success(result);
}

View File

@ -13,8 +13,9 @@ public interface IRecommendService
/// <param name="userId">用户ID0表示未登录</param>
/// <param name="pageIndex">页码</param>
/// <param name="pageSize">每页数量</param>
/// <param name="gender">筛选性别1=男2=女0=不限)</param>
/// <returns>分页推荐用户列表</returns>
Task<PagedResult<RecommendUserResponse>> GetDailyRecommendAsync(long userId, int pageIndex = 1, int pageSize = 10);
Task<PagedResult<RecommendUserResponse>> GetDailyRecommendAsync(long userId, int pageIndex = 1, int pageSize = 10, int gender = 0);
/// <summary>
/// 为指定用户生成每日推荐列表

View File

@ -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);

View File

@ -76,14 +76,14 @@ public class RecommendService : IRecommendService
}
/// <inheritdoc />
public async Task<PagedResult<RecommendUserResponse>> GetDailyRecommendAsync(long userId, int pageIndex = 1, int pageSize = 10)
public async Task<PagedResult<RecommendUserResponse>> 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
/// <param name="pageIndex">页码</param>
/// <param name="pageSize">每页数量</param>
/// <param name="excludeUserId">要排除的用户ID当前登录用户</param>
private async Task<PagedResult<RecommendUserResponse>> GetDefaultRecommendAsync(int pageIndex, int pageSize, long excludeUserId = 0)
/// <param name="gender">筛选性别1=男2=女0=不限)</param>
private async Task<PagedResult<RecommendUserResponse>> 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))