@@ -274,6 +297,8 @@ import {
setPrivacyPolicy,
getSearchBanner,
setSearchBanner,
+ getRealNameBanner,
+ setRealNameBanner,
getButlerQrcode,
setButlerQrcode,
getMemberIcons,
@@ -294,6 +319,7 @@ const activeTab = ref('basic')
const configForm = ref({
defaultAvatar: '',
searchBanner: '',
+ realNameBanner: '',
butlerQrcode: '',
unlimitedMemberIcon: '',
sincereMemberIcon: '',
@@ -326,9 +352,10 @@ const getFullUrl = (url) => {
const loadConfig = async () => {
try {
- const [avatarRes, bannerRes, qrcodeRes, memberIconsRes, memberEntryRes, realNamePriceRes] = await Promise.all([
+ const [avatarRes, bannerRes, realNameBannerRes, qrcodeRes, memberIconsRes, memberEntryRes, realNamePriceRes] = await Promise.all([
getDefaultAvatar(),
getSearchBanner(),
+ getRealNameBanner(),
getButlerQrcode(),
getMemberIcons(),
getMemberEntryImage(),
@@ -340,6 +367,9 @@ const loadConfig = async () => {
if (bannerRes) {
configForm.value.searchBanner = bannerRes.imageUrl || ''
}
+ if (realNameBannerRes) {
+ configForm.value.realNameBanner = realNameBannerRes.imageUrl || ''
+ }
if (qrcodeRes) {
configForm.value.butlerQrcode = qrcodeRes.imageUrl || ''
}
@@ -397,6 +427,15 @@ const handleBannerSuccess = (response) => {
}
}
+const handleRealNameBannerSuccess = (response) => {
+ if (response.code === 0 && response.data) {
+ configForm.value.realNameBanner = response.data.url
+ ElMessage.success('上传成功')
+ } else {
+ ElMessage.error(response.message || '上传失败')
+ }
+}
+
const handleQrcodeSuccess = (response) => {
if (response.code === 0 && response.data) {
configForm.value.butlerQrcode = response.data.url
@@ -476,6 +515,9 @@ const saveBasicConfig = async () => {
if (configForm.value.searchBanner) {
promises.push(setSearchBanner(configForm.value.searchBanner))
}
+ if (configForm.value.realNameBanner) {
+ promises.push(setRealNameBanner(configForm.value.realNameBanner))
+ }
if (configForm.value.butlerQrcode) {
promises.push(setButlerQrcode(configForm.value.butlerQrcode))
}
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 040488c..371427b 100644
--- a/miniapp/pages/index/index.vue
+++ b/miniapp/pages/index/index.vue
@@ -75,6 +75,13 @@
+
+
+
+
+
+
+
-
+
-
+
-
-
-
-
-
+
+
+
@@ -260,6 +266,9 @@ export default {
/** 下拉刷新状态 */
const isRefreshing = ref(false)
+
+ /** 页面是否已滚动(用于导航栏背景切换) */
+ const isScrolled = ref(false)
// ==================== 分页参数 ====================
@@ -945,6 +954,13 @@ export default {
}
}
+ /**
+ * 页面滚动 - 控制导航栏背景
+ */
+ const handleScroll = (e) => {
+ isScrolled.value = e.detail.scrollTop > 20
+ }
+
// ==================== 生命周期 ====================
onMounted(() => {
@@ -1008,7 +1024,9 @@ export default {
handleGoMember,
handleScrollToLower,
handleRefresh,
- handleCloseAuditingPopup
+ handleScroll,
+ handleCloseAuditingPopup,
+ isScrolled
}
},
@@ -1098,9 +1116,17 @@ export default {
z-index: 10;
}
-/* ==================== 自定义导航栏 ==================== */
+/* ==================== 固定导航栏 ==================== */
+
+.fixed-navbar {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 100;
+ background: transparent;
+ transition: background 0.3s ease;
-.custom-navbar {
.navbar-content {
height: 44px;
display: flex;
@@ -1116,6 +1142,17 @@ export default {
}
}
+.navbar-scrolled {
+ background: #FF8A93 !important;
+}
+
+/* 导航栏占位(防止内容被固定导航栏遮挡) */
+.navbar-placeholder {
+ .navbar-content {
+ height: 44px;
+ }
+}
+
/* ==================== 搜索框 ==================== */
.search-section {
diff --git a/miniapp/pages/profile/edit.vue b/miniapp/pages/profile/edit.vue
index 37177f4..5ad509b 100644
--- a/miniapp/pages/profile/edit.vue
+++ b/miniapp/pages/profile/edit.vue
@@ -160,7 +160,7 @@
- 籍贯
+ 籍贯
- 手机号验证
+ 手机号验证
@@ -225,6 +227,9 @@
import {
getToken
} from '@/utils/storage.js'
+ import {
+ getFullImageUrl
+ } from '@/utils/image.js'
import Loading from '@/components/Loading/index.vue'
export default {
@@ -249,6 +254,12 @@
// 认证费用 - 从配置中获取
const verificationFee = computed(() => configStore.realNamePrice || 88)
+ // 实名认证页Banner
+ const realNameBannerUrl = computed(() => {
+ const url = configStore.realNameBanner
+ return url ? getFullImageUrl(url) : ''
+ })
+
// 身份信息输入(二要素验证)
const idCardNumber = ref('')
const realName = ref('')
@@ -573,6 +584,7 @@
submitting,
currentStep,
verificationFee,
+ realNameBannerUrl,
idCardNumber,
realName,
isVerified,
@@ -903,15 +915,39 @@
// 步骤1: 支付页面
.step-payment {
.payment-content {
- padding: 40rpx 30rpx;
+ padding: 20rpx 30rpx 40rpx;
display: flex;
flex-direction: column;
align-items: center;
- position: relative;
- .realname-title-img {
- width: 500rpx;
- margin-bottom: 40rpx;
+ // 实名认证Banner
+ .realname-banner {
+ width: 100%;
+ margin-bottom: 30rpx;
+ border-radius: 24rpx;
+ overflow: hidden;
+
+ .banner-img {
+ width: 100%;
+ display: block;
+ }
+
+ .banner-default {
+ width: 100%;
+ height: 320rpx;
+ background: linear-gradient(135deg, #FFB6C1 0%, #FFC0CB 50%, #FFE4E1 100%);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ .banner-default-content {
+ .banner-title {
+ font-size: 40rpx;
+ font-weight: 700;
+ color: #D4145A;
+ }
+ }
+ }
}
// 实名认证的好处
@@ -924,7 +960,7 @@
.benefits-title {
font-size: 36rpx;
font-weight: 600;
- color: #1890ff;
+ color: #333;
margin-bottom: 32rpx;
}
@@ -950,114 +986,13 @@
color: #333;
.highlight {
- color: #1890ff;
- font-weight: 500;
+ color: #FF6A6A;
+ font-weight: 600;
}
}
}
}
}
-
- // 联系客服
- .contact-service {
- position: absolute;
- right: 30rpx;
- top: 480rpx;
- 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;
- }
- }
- }
}
}
@@ -1389,7 +1324,7 @@
justify-content: center;
border-radius: 48rpx;
border: none;
- background: linear-gradient(135deg, #ABCEFF 0%, #156EFF 100%);
+ background: linear-gradient(135deg, #FF9A9E 0%, #FF6B6B 100%);
&::after {
border: none;
@@ -1406,7 +1341,7 @@
position: absolute;
right: 40rpx;
top: -16rpx;
- background: linear-gradient(135deg, #ff6b6b 0%, #ff5252 100%);
+ background: linear-gradient(135deg, #4A90D9 0%, #2B6CB0 100%);
color: #fff;
font-size: 22rpx;
padding: 6rpx 16rpx;
@@ -1421,7 +1356,7 @@
align-items: center;
justify-content: center;
border-radius: 48rpx;
- border: 2rpx solid #0062FF;
+ border: 2rpx solid #FF6B6B;
background: #fff;
&::after {
@@ -1431,7 +1366,7 @@
text {
font-size: 32rpx;
font-weight: 600;
- color: #46A2FF;
+ color: #FF6B6B;
}
&[disabled] {
@@ -1447,7 +1382,7 @@
justify-content: center;
border-radius: 48rpx;
border: none;
- background: linear-gradient(135deg, #46A2FF 0%, #0062FF 100%);
+ background: linear-gradient(135deg, #FFABAC 0%, #FF5457 100%);
&::after {
border: none;
diff --git a/miniapp/store/config.js b/miniapp/store/config.js
index 82617b8..9627f8d 100644
--- a/miniapp/store/config.js
+++ b/miniapp/store/config.js
@@ -39,6 +39,7 @@ export const useConfigStore = defineStore('config', {
// 系统配置
defaultAvatar: getDefaultAvatar() || '/static/logo.png',
searchBanner: '',
+ realNameBanner: '',
butlerQrcode: '', // 管家指导二维码
memberEntryImage: '', // 会员入口图
@@ -130,6 +131,7 @@ export const useConfigStore = defineStore('config', {
setDefaultAvatar(config.defaultAvatar)
}
this.searchBanner = config.searchBanner || ''
+ this.realNameBanner = config.realNameBanner || ''
this.butlerQrcode = config.butlerQrcode || ''
this.memberEntryImage = config.memberEntryImage || ''
@@ -387,6 +389,7 @@ export const useConfigStore = defineStore('config', {
this.banners = []
this.kingKongs = []
this.searchBanner = ''
+ this.realNameBanner = ''
this.dailyPopup = null
this.memberAdConfig = null
this.serviceAccountPopup = null
diff --git a/server/src/XiangYi.AdminApi/Controllers/AdminConfigController.cs b/server/src/XiangYi.AdminApi/Controllers/AdminConfigController.cs
index 7aa2454..3ce6037 100644
--- a/server/src/XiangYi.AdminApi/Controllers/AdminConfigController.cs
+++ b/server/src/XiangYi.AdminApi/Controllers/AdminConfigController.cs
@@ -175,6 +175,34 @@ public class AdminConfigController : ControllerBase
return result ? ApiResponse.Success("设置成功") : ApiResponse.Error(40001, "设置失败");
}
+ ///
+ /// 获取实名认证页Banner
+ ///
+ [HttpGet("realNameBanner")]
+ public async Task> GetRealNameBanner()
+ {
+ var imageUrl = await _configService.GetRealNameBannerAsync();
+ return ApiResponse.Success(new RealNameBannerResponse
+ {
+ ImageUrl = imageUrl
+ });
+ }
+
+ ///
+ /// 设置实名认证页Banner
+ ///
+ [HttpPost("realNameBanner")]
+ public async Task SetRealNameBanner([FromBody] SetRealNameBannerRequest request)
+ {
+ if (string.IsNullOrWhiteSpace(request.ImageUrl))
+ {
+ return ApiResponse.Error(40001, "图片URL不能为空");
+ }
+
+ var result = await _configService.SetRealNameBannerAsync(request.ImageUrl);
+ return result ? ApiResponse.Success("设置成功") : ApiResponse.Error(40001, "设置失败");
+ }
+
///
/// 获取管家二维码
///
@@ -396,6 +424,28 @@ public class SetSearchBannerRequest
public string ImageUrl { get; set; } = string.Empty;
}
+///
+/// 实名认证页Banner响应
+///
+public class RealNameBannerResponse
+{
+ ///
+ /// 图片URL
+ ///
+ public string? ImageUrl { get; set; }
+}
+
+///
+/// 设置实名认证页Banner请求
+///
+public class SetRealNameBannerRequest
+{
+ ///
+ /// 图片URL
+ ///
+ public string ImageUrl { get; set; } = string.Empty;
+}
+
///
/// 管家二维码响应
///
diff --git a/server/src/XiangYi.Application/Interfaces/IConfigService.cs b/server/src/XiangYi.Application/Interfaces/IConfigService.cs
index 0d988dd..731623e 100644
--- a/server/src/XiangYi.Application/Interfaces/IConfigService.cs
+++ b/server/src/XiangYi.Application/Interfaces/IConfigService.cs
@@ -91,6 +91,11 @@ public class AppConfigResponse
///
public string? SearchBanner { get; set; }
+ ///
+ /// 实名认证页Banner URL
+ ///
+ public string? RealNameBanner { get; set; }
+
///
/// 管家指导二维码URL
///
diff --git a/server/src/XiangYi.Application/Interfaces/ISystemConfigService.cs b/server/src/XiangYi.Application/Interfaces/ISystemConfigService.cs
index a2e3c4a..7ad4d12 100644
--- a/server/src/XiangYi.Application/Interfaces/ISystemConfigService.cs
+++ b/server/src/XiangYi.Application/Interfaces/ISystemConfigService.cs
@@ -72,6 +72,16 @@ public interface ISystemConfigService
///
Task SetSearchBannerAsync(string imageUrl);
+ ///
+ /// 获取实名认证页Banner图URL
+ ///
+ Task GetRealNameBannerAsync();
+
+ ///
+ /// 设置实名认证页Banner图URL
+ ///
+ Task SetRealNameBannerAsync(string imageUrl);
+
///
/// 获取管家指导二维码URL
///
diff --git a/server/src/XiangYi.Application/Services/AdminUserService.cs b/server/src/XiangYi.Application/Services/AdminUserService.cs
index 59b7c54..833fb1d 100644
--- a/server/src/XiangYi.Application/Services/AdminUserService.cs
+++ b/server/src/XiangYi.Application/Services/AdminUserService.cs
@@ -391,7 +391,7 @@ public class AdminUserService : IAdminUserService
IsRealName = random.Next(2) == 1,
IsMember = random.Next(3) == 0, // 1/3概率是会员
MemberLevel = 0,
- ContactCount = 2,
+ ContactCount = 0,
CreateTime = DateTime.Now.AddDays(-random.Next(30)),
UpdateTime = DateTime.Now,
LastLoginTime = DateTime.Now.AddHours(-random.Next(72))
diff --git a/server/src/XiangYi.Application/Services/AuthService.cs b/server/src/XiangYi.Application/Services/AuthService.cs
index 5a31d7a..707b8d5 100644
--- a/server/src/XiangYi.Application/Services/AuthService.cs
+++ b/server/src/XiangYi.Application/Services/AuthService.cs
@@ -79,7 +79,7 @@ public class AuthService : IAuthService
XiangQinNo = xiangQinNo,
Avatar = defaultAvatar,
Status = 1,
- ContactCount = 2,
+ ContactCount = 0,
CreateTime = DateTime.Now,
UpdateTime = DateTime.Now
};
diff --git a/server/src/XiangYi.Application/Services/ConfigService.cs b/server/src/XiangYi.Application/Services/ConfigService.cs
index 82abaf4..51a4f15 100644
--- a/server/src/XiangYi.Application/Services/ConfigService.cs
+++ b/server/src/XiangYi.Application/Services/ConfigService.cs
@@ -47,6 +47,7 @@ public class ConfigService : IConfigService
var kingKongs = await GetKingKongsAsync();
var defaultAvatar = await _systemConfigService.GetDefaultAvatarAsync();
var searchBanner = await _systemConfigService.GetSearchBannerAsync();
+ var realNameBanner = await _systemConfigService.GetRealNameBannerAsync();
var butlerQrcode = await _systemConfigService.GetButlerQrcodeAsync();
var memberIcons = await _systemConfigService.GetMemberIconsAsync();
var dailyPopup = await GetPopupConfigAsync(1); // 每日弹窗
@@ -62,6 +63,7 @@ public class ConfigService : IConfigService
KingKongs = kingKongs,
DefaultAvatar = defaultAvatar,
SearchBanner = searchBanner,
+ RealNameBanner = realNameBanner,
ButlerQrcode = butlerQrcode,
MemberIcons = memberIcons,
DailyPopup = dailyPopup,
diff --git a/server/src/XiangYi.Application/Services/RealNameService.cs b/server/src/XiangYi.Application/Services/RealNameService.cs
index 0ba0d1e..9f6f64a 100644
--- a/server/src/XiangYi.Application/Services/RealNameService.cs
+++ b/server/src/XiangYi.Application/Services/RealNameService.cs
@@ -255,10 +255,12 @@ public class RealNameService : IRealNameService
// 更新用户实名状态
user.IsRealName = true;
+ // 权益1:实名认证赠送1次联系次数
+ user.ContactCount += 1;
user.UpdateTime = DateTime.Now;
await _userRepository.UpdateAsync(user);
- _logger.LogInformation("实名认证成功: UserId={UserId}", userId);
+ _logger.LogInformation("实名认证成功: UserId={UserId}, 赠送1次联系次数, 当前={ContactCount}", userId, user.ContactCount);
return new RealNameSubmitResponse
{
@@ -519,10 +521,12 @@ public class RealNameService : IRealNameService
// 更新用户实名状态
user.IsRealName = true;
+ // 权益1:实名认证赠送1次联系次数
+ user.ContactCount += 1;
user.UpdateTime = DateTime.Now;
await _userRepository.UpdateAsync(user);
- _logger.LogInformation("实名认证成功: UserId={UserId}", userId);
+ _logger.LogInformation("实名认证成功: UserId={UserId}, 赠送1次联系次数, 当前={ContactCount}", userId, user.ContactCount);
return new RealNameVerifyResponse
{
diff --git a/server/src/XiangYi.Application/Services/RecommendService.cs b/server/src/XiangYi.Application/Services/RecommendService.cs
index 715d2f6..3a56fd9 100644
--- a/server/src/XiangYi.Application/Services/RecommendService.cs
+++ b/server/src/XiangYi.Application/Services/RecommendService.cs
@@ -25,7 +25,7 @@ public class RecommendService : IRecommendService
///
/// 普通用户推荐数量
///
- public const int NormalUserRecommendCount = 10;
+ public const int NormalUserRecommendCount = 4;
///
/// 不限时会员推荐数量
@@ -539,8 +539,114 @@ public class RecommendService : IRecommendService
return await GenerateDailyRecommendForUserAsync(userId);
}
+ ///
+ /// 计算候选用户满足择偶要求的条件数量(静态方法,用于测试)
+ ///
+ public static (int matched, int total) CalculateMatchedConditions(UserRequirement? requirement, UserProfile targetProfile)
+ {
+ if (requirement == null) return (0, 0);
+
+ var matched = 0;
+ var total = 0;
+
+ // 年龄条件
+ if (requirement.AgeMin > 0 || requirement.AgeMax > 0)
+ {
+ total++;
+ var targetAge = DateTime.Now.Year - targetProfile.BirthYear;
+ if ((requirement.AgeMin <= 0 || targetAge >= requirement.AgeMin) &&
+ (requirement.AgeMax <= 0 || targetAge <= requirement.AgeMax))
+ {
+ matched++;
+ }
+ }
+
+ // 身高条件
+ if (requirement.HeightMin.HasValue || requirement.HeightMax.HasValue)
+ {
+ total++;
+ var heightOk = true;
+ if (requirement.HeightMin.HasValue && targetProfile.Height < requirement.HeightMin.Value) heightOk = false;
+ if (requirement.HeightMax.HasValue && targetProfile.Height > requirement.HeightMax.Value) heightOk = false;
+ if (heightOk) matched++;
+ }
+
+ // 学历条件
+ if (!string.IsNullOrEmpty(requirement.Education))
+ {
+ var eduList = ParseJsonArray(requirement.Education);
+ if (eduList.Count > 0)
+ {
+ total++;
+ if (eduList.Contains(targetProfile.Education)) matched++;
+ }
+ }
+
+ // 城市条件
+ if (!string.IsNullOrEmpty(requirement.City1City) || !string.IsNullOrEmpty(requirement.City2City))
+ {
+ total++;
+ if ((!string.IsNullOrEmpty(requirement.City1City) && targetProfile.WorkCity == requirement.City1City) ||
+ (!string.IsNullOrEmpty(requirement.City2City) && targetProfile.WorkCity == requirement.City2City))
+ {
+ matched++;
+ }
+ }
+
+ // 收入条件
+ if (requirement.MonthlyIncomeMin.HasValue || requirement.MonthlyIncomeMax.HasValue)
+ {
+ total++;
+ var incomeOk = true;
+ if (requirement.MonthlyIncomeMin.HasValue && targetProfile.MonthlyIncome < requirement.MonthlyIncomeMin.Value) incomeOk = false;
+ if (requirement.MonthlyIncomeMax.HasValue && targetProfile.MonthlyIncome > requirement.MonthlyIncomeMax.Value) incomeOk = false;
+ if (incomeOk) matched++;
+ }
+
+ // 房产条件
+ if (!string.IsNullOrEmpty(requirement.HouseStatus))
+ {
+ var houseList = ParseJsonArray(requirement.HouseStatus);
+ if (houseList.Count > 0)
+ {
+ total++;
+ if (houseList.Contains(targetProfile.HouseStatus)) matched++;
+ }
+ }
+
+ // 车辆条件
+ if (!string.IsNullOrEmpty(requirement.CarStatus))
+ {
+ var carList = ParseJsonArray(requirement.CarStatus);
+ if (carList.Count > 0)
+ {
+ total++;
+ if (carList.Contains(targetProfile.CarStatus)) matched++;
+ }
+ }
+
+ // 婚姻状态条件
+ if (!string.IsNullOrEmpty(requirement.MarriageStatus))
+ {
+ var marriageList = ParseJsonArray(requirement.MarriageStatus);
+ if (marriageList.Count > 0)
+ {
+ total++;
+ if (marriageList.Contains(targetProfile.MarriageStatus)) matched++;
+ }
+ }
+
+ return (matched, total);
+ }
+
///
/// 获取候选用户列表
+ /// 推荐优先级:
+ /// 1. 全部满足择偶要求的候选人
+ /// 2. 逐一减少满足条件数量
+ /// 3. 同城优先,已实名用户优先
+ /// 4. 家乡城市较近,已实名用户优先
+ /// 原则上不推荐3天内推荐过的用户
///
private async Task> GetCandidateUsersAsync(
long userId,
@@ -576,6 +682,9 @@ public class RecommendService : IRecommendService
? CalculateMatchScoreStatic(userRequirement, profile)
: 50;
+ // 计算满足择偶要求的条件数量
+ var (matchedCount, totalCount) = CalculateMatchedConditions(userRequirement, profile);
+
// 检查是否在去重期内
var isRecentlyRecommended = await IsRecommendedInDaysAsync(userId, user.Id, DeduplicationDays);
@@ -583,26 +692,42 @@ public class RecommendService : IRecommendService
{
UserId = user.Id,
MatchScore = matchScore,
+ MatchedConditionCount = matchedCount,
+ TotalConditionCount = totalCount,
IsNewUser = user.CreateTime > DateTime.Now.AddMinutes(-NewUserPriorityMinutes),
IsMember = user.IsMember,
+ IsRealName = user.IsRealName,
IsRecentlyRecommended = isRecentlyRecommended,
- WorkCity = profile.WorkCity
+ WorkCity = profile.WorkCity,
+ WorkProvince = profile.WorkProvince,
+ HomeCity = profile.HomeCity,
+ HomeProvince = profile.HomeProvince
});
}
- // 排序逻辑:
- // 1. 新用户优先(30分钟内注册)推荐给会员
- // 2. 未在去重期内的用户优先
- // 3. 按匹配度排序
- // 4. 同城优先
+ // 排序逻辑(新推荐优先级):
+ // 1. 未在去重期内的用户优先
+ // 2. 满足择偶要求的条件数量多的优先(全部满足 > 逐一减少)
+ // 3. 同城(工作城市相同)优先,同城中已实名用户优先
+ // 4. 同省优先
+ // 5. 家乡城市相同优先,其中已实名用户优先
+ // 6. 家乡省份相同优先
+ // 7. 会员优先看新用户
var currentUser = await _userRepository.GetByIdAsync(userId);
var isCurrentUserMember = currentUser?.IsMember ?? false;
var sortedCandidates = candidates
- .OrderByDescending(c => isCurrentUserMember && c.IsNewUser ? 1 : 0) // 会员优先看新用户
- .ThenBy(c => c.IsRecentlyRecommended ? 1 : 0) // 未推荐过的优先
- .ThenByDescending(c => c.MatchScore) // 匹配度高的优先
- .ThenByDescending(c => c.WorkCity == userProfile.WorkCity ? 1 : 0) // 同城优先
+ .OrderBy(c => c.IsRecentlyRecommended ? 1 : 0) // 未推荐过的优先
+ .ThenByDescending(c => c.MatchedConditionCount) // 满足条件数量多的优先
+ .ThenByDescending(c => c.WorkCity == userProfile.WorkCity ? 1 : 0) // 同城(工作城市)优先
+ .ThenByDescending(c => c.WorkCity == userProfile.WorkCity && c.IsRealName ? 1 : 0) // 同城中已实名优先
+ .ThenByDescending(c => c.WorkProvince == userProfile.WorkProvince ? 1 : 0) // 同省优先
+ .ThenByDescending(c => c.HomeCity == userProfile.HomeCity ? 1 : 0) // 家乡城市相同优先
+ .ThenByDescending(c => c.HomeCity == userProfile.HomeCity && c.IsRealName ? 1 : 0) // 家乡城市相同中已实名优先
+ .ThenByDescending(c => c.HomeProvince == userProfile.HomeProvince ? 1 : 0) // 家乡省份相同优先
+ .ThenByDescending(c => c.IsRealName ? 1 : 0) // 已实名用户优先
+ .ThenByDescending(c => isCurrentUserMember && c.IsNewUser ? 1 : 0) // 会员优先看新用户
+ .ThenByDescending(c => c.MatchScore) // 匹配度高的优先
.ToList();
// 如果候选用户不足,允许重复推荐
@@ -668,7 +793,19 @@ public class RecommendService : IRecommendService
public int MatchScore { get; set; }
public bool IsNewUser { get; set; }
public bool IsMember { get; set; }
+ public bool IsRealName { get; set; }
public bool IsRecentlyRecommended { get; set; }
public string? WorkCity { get; set; }
+ public string? WorkProvince { get; set; }
+ public string? HomeCity { get; set; }
+ public string? HomeProvince { get; set; }
+ ///
+ /// 满足择偶要求的条件数量(用于逐一减少排序)
+ ///
+ public int MatchedConditionCount { get; set; }
+ ///
+ /// 择偶要求总条件数
+ ///
+ public int TotalConditionCount { get; set; }
}
}
diff --git a/server/src/XiangYi.Application/Services/SystemConfigService.cs b/server/src/XiangYi.Application/Services/SystemConfigService.cs
index 9bae329..4e1da99 100644
--- a/server/src/XiangYi.Application/Services/SystemConfigService.cs
+++ b/server/src/XiangYi.Application/Services/SystemConfigService.cs
@@ -38,6 +38,11 @@ public class SystemConfigService : ISystemConfigService
///
public const string SearchBannerKey = "search_banner";
+ ///
+ /// 实名认证页Banner配置键
+ ///
+ public const string RealNameBannerKey = "realname_banner";
+
///
/// 管家二维码配置键
///
@@ -211,6 +216,18 @@ public class SystemConfigService : ISystemConfigService
return await SetConfigValueAsync(SearchBannerKey, imageUrl, "搜索页Banner图URL");
}
+ ///
+ public async Task GetRealNameBannerAsync()
+ {
+ return await GetConfigValueAsync(RealNameBannerKey);
+ }
+
+ ///
+ public async Task SetRealNameBannerAsync(string imageUrl)
+ {
+ return await SetConfigValueAsync(RealNameBannerKey, imageUrl, "实名认证页Banner图URL");
+ }
+
///
public async Task GetButlerQrcodeAsync()
{
diff --git a/server/src/XiangYi.Core/Entities/Biz/User.cs b/server/src/XiangYi.Core/Entities/Biz/User.cs
index 5cbc12a..246f7c9 100644
--- a/server/src/XiangYi.Core/Entities/Biz/User.cs
+++ b/server/src/XiangYi.Core/Entities/Biz/User.cs
@@ -97,9 +97,9 @@ public class User : SoftDeleteEntity
public DateTime? MemberExpireTime { get; set; }
///
- /// 剩余联系次数,默认2
+ /// 剩余联系次数,默认0
///
- public int ContactCount { get; set; } = 2;
+ public int ContactCount { get; set; } = 0;
///
/// 最后登录时间
diff --git a/server/tests/XiangYi.Application.Tests/Services/RecommendServicePropertyTests.cs b/server/tests/XiangYi.Application.Tests/Services/RecommendServicePropertyTests.cs
index 27c399b..12a5ec1 100644
--- a/server/tests/XiangYi.Application.Tests/Services/RecommendServicePropertyTests.cs
+++ b/server/tests/XiangYi.Application.Tests/Services/RecommendServicePropertyTests.cs
@@ -20,8 +20,8 @@ public class RecommendCountPropertyTests
[Property(MaxTest = 100)]
public Property RecommendCount_ShouldMatchMemberLevel()
{
- // 生成有效的会员等级(0-3)
- var memberLevelArb = Gen.Choose(0, 3);
+ // 生成有效的会员等级(0-4)
+ var memberLevelArb = Gen.Choose(0, 4);
return Prop.ForAll(
memberLevelArb.ToArbitrary(),
@@ -34,9 +34,9 @@ public class RecommendCountPropertyTests
// Assert
return memberLevel switch
{
- // 普通用户:恰好10人
+ // 普通用户:恰好4人
0 => actualCount == RecommendService.NormalUserRecommendCount &&
- minCount == 10 && maxCount == 10,
+ minCount == 4 && maxCount == 4,
// 不限时会员:恰好24人
1 => actualCount == RecommendService.UnlimitedMemberRecommendCount &&
@@ -47,8 +47,13 @@ public class RecommendCountPropertyTests
actualCount <= RecommendService.SincereMemberMaxRecommendCount &&
minCount == 24 && maxCount == 29,
- // 家庭版会员:恰好24人
- 3 => actualCount == RecommendService.FamilyMemberRecommendCount &&
+ // 家庭版会员:24-29人
+ 3 => actualCount >= RecommendService.SincereMemberMinRecommendCount &&
+ actualCount <= RecommendService.SincereMemberMaxRecommendCount &&
+ minCount == 24 && maxCount == 29,
+
+ // 限时会员:恰好24人
+ 4 => actualCount == RecommendService.UnlimitedMemberRecommendCount &&
minCount == 24 && maxCount == 24,
_ => false
@@ -57,7 +62,7 @@ public class RecommendCountPropertyTests
}
///
- /// 普通用户推荐数量应为10
+ /// 普通用户推荐数量应为4
///
[Property(MaxTest = 100)]
public Property NormalUser_ShouldGet10Recommendations()
@@ -67,7 +72,7 @@ public class RecommendCountPropertyTests
_ =>
{
var count = RecommendService.GetRecommendCountByMemberLevelStatic(0);
- return count == 10;
+ return count == 4;
});
}
@@ -102,7 +107,7 @@ public class RecommendCountPropertyTests
}
///
- /// 家庭版会员推荐数量应为24
+ /// 家庭版会员推荐数量应在24-29之间
///
[Property(MaxTest = 100)]
public Property FamilyMember_ShouldGet24Recommendations()
@@ -112,7 +117,7 @@ public class RecommendCountPropertyTests
_ =>
{
var count = RecommendService.GetRecommendCountByMemberLevelStatic(3);
- return count == 24;
+ return count >= 24 && count <= 29;
});
}
@@ -122,10 +127,10 @@ public class RecommendCountPropertyTests
[Property(MaxTest = 100)]
public Property InvalidMemberLevel_ShouldReturnNormalUserCount()
{
- // 生成无效的会员等级(负数或大于3)
+ // 生成无效的会员等级(负数或大于4)
var invalidLevelArb = Gen.OneOf(
Gen.Choose(-100, -1),
- Gen.Choose(4, 100)
+ Gen.Choose(5, 100)
);
return Prop.ForAll(
@@ -143,7 +148,7 @@ public class RecommendCountPropertyTests
[Property(MaxTest = 100)]
public Property MemberRecommendCount_ShouldBeGreaterOrEqualToNormalUser()
{
- var memberLevelArb = Gen.Choose(1, 3); // 会员等级1-3
+ var memberLevelArb = Gen.Choose(1, 4); // 会员等级1-4
return Prop.ForAll(
memberLevelArb.ToArbitrary(),
@@ -161,7 +166,7 @@ public class RecommendCountPropertyTests
[Fact]
public void Constants_ShouldHaveCorrectValues()
{
- Assert.Equal(10, RecommendService.NormalUserRecommendCount);
+ Assert.Equal(4, RecommendService.NormalUserRecommendCount);
Assert.Equal(24, RecommendService.UnlimitedMemberRecommendCount);
Assert.Equal(24, RecommendService.SincereMemberMinRecommendCount);
Assert.Equal(29, RecommendService.SincereMemberMaxRecommendCount);
From e05ecd90f55e1d4779501a2e549b13880c573dae Mon Sep 17 00:00:00 2001
From: 18631081161 <2088094923@qq.com>
Date: Sat, 28 Feb 2026 12:45:07 +0800
Subject: [PATCH 3/3] =?UTF-8?q?=E9=9A=90=E8=97=8F=E6=90=9C=E7=B4=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
miniapp/config/index.js | 2 +-
miniapp/pages/index/index.vue | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/miniapp/config/index.js b/miniapp/config/index.js
index 7794e45..6bdbd8d 100644
--- a/miniapp/config/index.js
+++ b/miniapp/config/index.js
@@ -23,7 +23,7 @@ const ENV = {
}
// 当前环境 - 开发时使用 development,打包时改为 production
-const CURRENT_ENV = 'development'
+const CURRENT_ENV = 'production'
// 导出配置
export const config = {
diff --git a/miniapp/pages/index/index.vue b/miniapp/pages/index/index.vue
index 371427b..c80a38a 100644
--- a/miniapp/pages/index/index.vue
+++ b/miniapp/pages/index/index.vue
@@ -125,13 +125,13 @@
-
-
+
+