推荐
This commit is contained in:
parent
261e57a6a3
commit
9b9e8f99b2
|
|
@ -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`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ const ENV = {
|
|||
}
|
||||
|
||||
// 当前环境 - 开发时使用 development,打包时改为 production
|
||||
const CURRENT_ENV = 'development'
|
||||
const CURRENT_ENV = 'production'
|
||||
|
||||
// 导出配置
|
||||
export const config = {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 />
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user