This commit is contained in:
18631081161 2026-02-08 13:58:03 +08:00
parent 261e57a6a3
commit 9b9e8f99b2
8 changed files with 114 additions and 28 deletions

View File

@ -96,3 +96,12 @@ export function updateMemberLevel(id: number, memberLevel: number, memberExpireT
export function cancelRealName(id: number): Promise<void> {
return request.put(`/admin/users/${id}/cancel-realname`)
}
/**
*
* @param id ID
* @returns
*/
export function refreshRecommend(id: number): Promise<number> {
return request.post(`/admin/users/${id}/refresh-recommend`)
}

View File

@ -9,7 +9,6 @@ import { ElMessage, ElMessageBox } from 'element-plus'
import { ArrowLeft } from '@element-plus/icons-vue'
import StatusTag from '@/components/StatusTag/index.vue'
import { getUserDetail, updateUserStatus, updateContactCount, updateMemberLevel, cancelRealName } from '@/api/user'
import { getMemberTierList } from '@/api/memberTier'
import { getFullImageUrl } from '@/utils/image'
import type { UserDetail } from '@/types/user.d'
@ -107,26 +106,14 @@ const handleEditContactCount = async () => {
}
}
// -
const memberLevelOptions = ref([
{ value: 0, label: '非会员' }
])
//
const loadMemberTierOptions = async () => {
try {
const res = await getMemberTierList()
const tiers = res.data || res || []
if (Array.isArray(tiers) && tiers.length > 0) {
memberLevelOptions.value = [
{ value: 0, label: '非会员' },
...tiers.map((t: any) => ({ value: t.level, label: t.name }))
]
}
} catch (error) {
console.error('加载会员等级配置失败:', error)
}
}
// -
const memberLevelOptions = [
{ value: 0, label: '非会员' },
{ value: 1, label: '等级1 - 不限时会员' },
{ value: 2, label: '等级2 - 诚意会员' },
{ value: 3, label: '等级3 - 家庭版会员' },
{ value: 4, label: '等级4 - 限时会员' }
]
//
const memberLevelDialogVisible = ref(false)
@ -270,7 +257,6 @@ const formatIncome = (min: number | undefined, max: number | undefined) => {
onMounted(() => {
fetchUserDetail()
loadMemberTierOptions()
})
</script>

View File

@ -6,11 +6,11 @@
import { ref, reactive, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { View, Edit, Plus, Delete } from '@element-plus/icons-vue'
import { View, Edit, Plus, Delete, Refresh } from '@element-plus/icons-vue'
import SearchForm from '@/components/SearchForm/index.vue'
import Pagination from '@/components/Pagination/index.vue'
import StatusTag from '@/components/StatusTag/index.vue'
import { getUserList, updateUserStatus, createTestUsers, deleteUser } from '@/api/user'
import { getUserList, updateUserStatus, createTestUsers, deleteUser, refreshRecommend } from '@/api/user'
import { getMemberTierList } from '@/api/memberTier'
import { getFullImageUrl } from '@/utils/image'
import type { UserListItem, UserQueryParams } from '@/types/user.d'
@ -245,6 +245,28 @@ const handleCreateTestUsers = async () => {
}
}
//
const handleRefreshRecommend = async (row: UserListItem) => {
try {
await ElMessageBox.confirm(
`确定要刷新用户「${row.nickname || row.xiangQinNo}」的今日推荐列表吗?将清空现有推荐并按当前会员等级重新生成。`,
'刷新推荐',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
)
const count = await refreshRecommend(row.userId)
ElMessage.success(`已重新生成${count}条推荐`)
} catch (error) {
if (error !== 'cancel') {
console.error('刷新推荐失败:', error)
}
}
}
onMounted(() => {
fetchUserList()
loadMemberTierOptions()
@ -516,7 +538,7 @@ onMounted(() => {
</el-table-column>
<el-table-column
label="操作"
width="200"
width="260"
fixed="right"
align="center"
>
@ -530,6 +552,14 @@ onMounted(() => {
>
详情
</el-button>
<el-button
type="warning"
link
:icon="Refresh"
@click="handleRefreshRecommend(row)"
>
刷新推荐
</el-button>
<el-button
:type="row.status === 1 ? 'danger' : 'success'"
link

View File

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

View File

@ -17,11 +17,13 @@ namespace XiangYi.AdminApi.Controllers;
public class AdminUserController : ControllerBase
{
private readonly IAdminUserService _adminUserService;
private readonly IRecommendService _recommendService;
private readonly ILogger<AdminUserController> _logger;
public AdminUserController(IAdminUserService adminUserService, ILogger<AdminUserController> logger)
public AdminUserController(IAdminUserService adminUserService, IRecommendService recommendService, ILogger<AdminUserController> logger)
{
_adminUserService = adminUserService;
_recommendService = recommendService;
_logger = logger;
}
@ -159,6 +161,27 @@ public class AdminUserController : ControllerBase
var result = await _adminUserService.CancelRealNameAsync(id, adminId);
return result ? ApiResponse.Success("取消实名成功") : ApiResponse.Error(40001, "取消实名失败");
}
/// <summary>
/// 刷新用户今日推荐(清空并重新生成)
/// </summary>
/// <param name="id">用户ID</param>
/// <returns>重新生成的推荐数量</returns>
[HttpPost("{id}/refresh-recommend")]
[OperationLog("用户管理", "修改", Description = "刷新用户推荐列表")]
public async Task<ApiResponse<int>> RefreshRecommend(long id)
{
try
{
var count = await _recommendService.RefreshDailyRecommendAsync(id);
return ApiResponse<int>.Success(count, $"已重新生成{count}条推荐");
}
catch (Exception ex)
{
_logger.LogError(ex, "刷新用户 {UserId} 推荐失败", id);
return ApiResponse<int>.Error(40001, "刷新推荐失败:" + ex.Message);
}
}
}
/// <summary>

View File

@ -60,4 +60,11 @@ public interface IRecommendService
/// <param name="userId">用户ID</param>
/// <param name="recommendUserId">推荐用户ID</param>
Task MarkAsViewedAsync(long userId, long recommendUserId);
/// <summary>
/// 清空用户今日推荐并重新生成
/// </summary>
/// <param name="userId">用户ID</param>
/// <returns>重新生成的推荐数量</returns>
Task<int> RefreshDailyRecommendAsync(long userId);
}

View File

@ -20,6 +20,7 @@ public class PaymentService : IPaymentService
private readonly IRepository<MemberTierConfig> _tierConfigRepository;
private readonly IRepository<User> _userRepository;
private readonly IWeChatPayService _weChatPayService;
private readonly IRecommendService _recommendService;
private readonly ILogger<PaymentService> _logger;
public PaymentService(
@ -28,6 +29,7 @@ public class PaymentService : IPaymentService
IRepository<MemberTierConfig> tierConfigRepository,
IRepository<User> userRepository,
IWeChatPayService weChatPayService,
IRecommendService recommendService,
ILogger<PaymentService> logger)
{
_orderRepository = orderRepository;
@ -35,6 +37,7 @@ public class PaymentService : IPaymentService
_tierConfigRepository = tierConfigRepository;
_userRepository = userRepository;
_weChatPayService = weChatPayService;
_recommendService = recommendService;
_logger = logger;
}
@ -244,6 +247,17 @@ public class PaymentService : IPaymentService
_logger.LogInformation("会员开通成功: UserId={UserId}, Level={Level}, ExpireTime={ExpireTime}",
order.UserId, memberLevel, expireTime);
// 会员等级变更后,重新生成今日推荐(按新等级的推荐数量)
try
{
await _recommendService.RefreshDailyRecommendAsync(order.UserId);
_logger.LogInformation("会员开通后已刷新推荐: UserId={UserId}", order.UserId);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "会员开通后刷新推荐失败: UserId={UserId}(不影响会员开通)", order.UserId);
}
}
/// <inheritdoc />

View File

@ -281,9 +281,15 @@ public class RecommendService : IRecommendService
// 多获取一些候选用户,弥补查询时可能被过滤掉的(用户被禁用、资料未审核等)
var candidateCount = (int)Math.Ceiling(recommendCount * 1.5);
// 获取候选用户
// 获取候选用户(多取一些,弥补过滤损耗)
var candidates = await GetCandidateUsersAsync(userId, userProfile, userRequirement, candidateCount);
// 按实际推荐数量截断
if (candidates.Count > recommendCount)
{
candidates = candidates.Take(recommendCount).ToList();
}
// 删除今日已有的推荐
var today = DateTime.Today;
await _recommendRepository.DeleteAsync(r => r.UserId == userId && r.RecommendDate == today);
@ -522,6 +528,17 @@ public class RecommendService : IRecommendService
}
}
/// <inheritdoc />
public async Task<int> RefreshDailyRecommendAsync(long userId)
{
// 删除今日推荐记录
var today = DateTime.Today;
await _recommendRepository.DeleteAsync(r => r.UserId == userId && r.RecommendDate == today);
// 重新生成
return await GenerateDailyRecommendForUserAsync(userId);
}
/// <summary>
/// 获取候选用户列表
/// </summary>