Merge branch 'master' of 192.168.195.14:outsource/xiangyixiangqin
This commit is contained in:
commit
91f22008ed
|
|
@ -77,6 +77,20 @@ export function setSearchBanner(imageUrl: string) {
|
|||
return request.post('/admin/config/searchBanner', { imageUrl })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实名认证页Banner
|
||||
*/
|
||||
export function getRealNameBanner() {
|
||||
return request.get('/admin/config/realNameBanner')
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置实名认证页Banner
|
||||
*/
|
||||
export function setRealNameBanner(imageUrl: string) {
|
||||
return request.post('/admin/config/realNameBanner', { imageUrl })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取管家二维码
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -57,6 +57,29 @@
|
|||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 实名认证页Banner设置 -->
|
||||
<el-form-item label="实名认证Banner">
|
||||
<div class="banner-upload">
|
||||
<el-upload
|
||||
class="banner-uploader"
|
||||
:action="uploadUrl"
|
||||
:headers="uploadHeaders"
|
||||
:show-file-list="false"
|
||||
:on-success="handleRealNameBannerSuccess"
|
||||
:before-upload="beforeAvatarUpload"
|
||||
accept="image/*"
|
||||
>
|
||||
<img v-if="configForm.realNameBanner" :src="getFullUrl(configForm.realNameBanner)" class="banner-preview" />
|
||||
<el-icon v-else class="banner-uploader-icon"><Plus /></el-icon>
|
||||
</el-upload>
|
||||
<div class="avatar-tip">
|
||||
<p>建议尺寸:750x400像素</p>
|
||||
<p>支持格式:JPG、PNG</p>
|
||||
<p>小程序实名认证页顶部展示的Banner图片</p>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 管家二维码设置 -->
|
||||
<el-form-item label="管家二维码">
|
||||
<div class="qrcode-upload">
|
||||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,6 +75,13 @@
|
|||
|
||||
<!-- ==================== 主内容区 ==================== -->
|
||||
|
||||
<!-- 固定导航栏 - 不随页面滚动 -->
|
||||
<view class="fixed-navbar" :class="{ 'navbar-scrolled': isScrolled }" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="header-title">相宜相亲</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 整页滚动区域 - 支持下拉刷新和上拉加载更多 -->
|
||||
<scroll-view
|
||||
class="page-scroll"
|
||||
|
|
@ -83,8 +90,9 @@
|
|||
:refresher-triggered="isRefreshing"
|
||||
@refresherrefresh="handleRefresh"
|
||||
@scrolltolower="handleScrollToLower"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
<!-- Banner 区域(导航栏和搜索框浮在 Banner 上) -->
|
||||
<!-- Banner 区域(搜索框浮在 Banner 上) -->
|
||||
<view class="banner-section">
|
||||
<!-- Banner 轮播图 -->
|
||||
<swiper
|
||||
|
|
@ -110,22 +118,20 @@
|
|||
<!-- 无 banner 时的默认渐变背景 -->
|
||||
<view v-else class="banner-placeholder"></view>
|
||||
|
||||
<!-- 浮层:导航栏 + 搜索框 -->
|
||||
<!-- 浮层:搜索框 -->
|
||||
<view class="banner-overlay">
|
||||
<!-- 自定义导航栏 - 适配状态栏高度 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="header-title">相宜相亲</view>
|
||||
</view>
|
||||
<!-- 导航栏占位(与固定导航栏等高) -->
|
||||
<view class="navbar-placeholder" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content"></view>
|
||||
</view>
|
||||
|
||||
<!-- 搜索框 - 点击跳转搜索页 -->
|
||||
<view class="search-section">
|
||||
<!-- 搜索框 - 暂时隐藏 -->
|
||||
<!-- <view class="search-section">
|
||||
<view class="search-bar" @click="handleSearchClick">
|
||||
<text class="search-icon">🔍</text>
|
||||
<text class="search-placeholder">搜索你心目中的TA</text>
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
|
@ -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 {
|
||||
|
|
@ -1241,60 +1278,6 @@ export default {
|
|||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 99;
|
||||
|
||||
.member-ad-bar {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 110rpx;
|
||||
padding: 0 24rpx;
|
||||
|
||||
.ad-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
|
||||
.ad-icon {
|
||||
font-size: 32rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.ad-text {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.ad-btn {
|
||||
background: #fff;
|
||||
color: #ff6b6b;
|
||||
font-size: 24rpx;
|
||||
padding: 12rpx 24rpx;
|
||||
border-radius: 30rpx;
|
||||
margin-right: 16rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.ad-close {
|
||||
position: absolute;
|
||||
right: 16rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
text {
|
||||
font-size: 36rpx;
|
||||
color: #666;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ==================== 订阅消息提醒条 ==================== */
|
||||
|
|
@ -1306,4 +1289,59 @@ export default {
|
|||
bottom: 0;
|
||||
z-index: 98;
|
||||
}
|
||||
|
||||
/* 广告条/提醒条公共样式 */
|
||||
.member-ad-bar {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 110rpx;
|
||||
padding: 0 24rpx;
|
||||
|
||||
.ad-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
|
||||
.ad-icon {
|
||||
font-size: 32rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.ad-text {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.ad-btn {
|
||||
background: #fff;
|
||||
color: #ff6b6b;
|
||||
font-size: 24rpx;
|
||||
padding: 12rpx 24rpx;
|
||||
border-radius: 30rpx;
|
||||
margin-right: 16rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.ad-close {
|
||||
position: absolute;
|
||||
right: 16rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
text {
|
||||
font-size: 36rpx;
|
||||
color: #666;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@
|
|||
|
||||
<!-- 籍贯 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">籍贯</text>
|
||||
<text class="form-label required">籍贯</text>
|
||||
<picker
|
||||
mode="region"
|
||||
:value="[formData.homeProvince, formData.homeCity, formData.homeDistrict]"
|
||||
|
|
@ -637,7 +637,7 @@
|
|||
|
||||
<!-- 手机号验证 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">手机号验证</text>
|
||||
<text class="form-label required">手机号验证</text>
|
||||
<button
|
||||
v-if="!phoneVerified"
|
||||
class="verify-btn"
|
||||
|
|
@ -1358,6 +1358,10 @@ const validateStep = (step) => {
|
|||
uni.showToast({ title: '请选择出生年份', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (!formData.homeProvince || !formData.homeCity) {
|
||||
uni.showToast({ title: '请选择籍贯', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
||||
case 1: // 详细信息
|
||||
|
|
@ -1450,6 +1454,10 @@ const validateStep = (step) => {
|
|||
uni.showToast({ title: '请输入微信号', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (!phoneVerified.value) {
|
||||
uni.showToast({ title: '请先验证手机号', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
||||
default:
|
||||
|
|
@ -1546,7 +1554,7 @@ const loadProfile = async () => {
|
|||
formData.houseStatus = profile.houseStatus || 5
|
||||
formData.carStatus = profile.carStatus || 2
|
||||
formData.marriageStatus = profile.marriageStatus || 1
|
||||
formData.expectMarryTime = profile.expectMarryTime || 0
|
||||
formData.expectMarryTime = profile.expectMarryTime || 1
|
||||
formData.introduction = profile.introduction || ''
|
||||
formData.weChatNo = profile.weChatNo || ''
|
||||
|
||||
|
|
|
|||
|
|
@ -65,12 +65,20 @@
|
|||
<!-- 可滚动内容区域 -->
|
||||
<scroll-view class="content-scroll" scroll-y :style="{
|
||||
top: (statusBarHeight + 44 + 100) + 'px',
|
||||
height: 'calc(100vh - ' + (statusBarHeight + 44 + 100 + 120) + 'px)'
|
||||
bottom: (currentStep === 1 && !canSkipPayment ? 240 : 140) + 'px'
|
||||
}">
|
||||
<!-- 步骤1: 支付页面 (Requirements 12.1) -->
|
||||
<view v-if="currentStep === 1" class="step-payment">
|
||||
<view class="payment-content">
|
||||
<image class="realname-title-img" src="/static/ic_real_name.png" mode="widthFix" />
|
||||
<!-- 实名认证Banner图 -->
|
||||
<view class="realname-banner">
|
||||
<image v-if="realNameBannerUrl" :src="realNameBannerUrl" mode="widthFix" class="banner-img" />
|
||||
<view v-else class="banner-default">
|
||||
<view class="banner-default-content">
|
||||
<text class="banner-title">实名认证介绍</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 实名认证的好处 -->
|
||||
<view class="benefits-section">
|
||||
|
|
@ -95,12 +103,6 @@
|
|||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 联系客服 -->
|
||||
<view class="contact-service" @click="handleContactService">
|
||||
<!-- <image class="service-avatar" src="/static/ic_service.png" mode="aspectFill" /> -->
|
||||
<text class="service-text">联系客服</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -175,6 +175,34 @@ public class AdminConfigController : ControllerBase
|
|||
return result ? ApiResponse.Success("设置成功") : ApiResponse.Error(40001, "设置失败");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取实名认证页Banner
|
||||
/// </summary>
|
||||
[HttpGet("realNameBanner")]
|
||||
public async Task<ApiResponse<RealNameBannerResponse>> GetRealNameBanner()
|
||||
{
|
||||
var imageUrl = await _configService.GetRealNameBannerAsync();
|
||||
return ApiResponse<RealNameBannerResponse>.Success(new RealNameBannerResponse
|
||||
{
|
||||
ImageUrl = imageUrl
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置实名认证页Banner
|
||||
/// </summary>
|
||||
[HttpPost("realNameBanner")]
|
||||
public async Task<ApiResponse> 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, "设置失败");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取管家二维码
|
||||
/// </summary>
|
||||
|
|
@ -396,6 +424,28 @@ public class SetSearchBannerRequest
|
|||
public string ImageUrl { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 实名认证页Banner响应
|
||||
/// </summary>
|
||||
public class RealNameBannerResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// 图片URL
|
||||
/// </summary>
|
||||
public string? ImageUrl { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置实名认证页Banner请求
|
||||
/// </summary>
|
||||
public class SetRealNameBannerRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 图片URL
|
||||
/// </summary>
|
||||
public string ImageUrl { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 管家二维码响应
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -42,10 +42,10 @@
|
|||
"GhId": "gh_f57692c34c0e",
|
||||
"AppId": "wxa2f42b01be34b37b",
|
||||
"AppSecret": "b8cc3abd81a7cbf32ded28f92ba61627",
|
||||
"UnlockTemplateId": "dQdK2i7ZDkDGQ2Knifv82rDx9HCzR1aE71YmR8JjwBc",
|
||||
"FavoriteTemplateId": "dQdK2i7ZDkDGQ2Knifv82rDx9HCzR1aE71YmR8JjwBc",
|
||||
"MessageTemplateId": "dQdK2i7ZDkDGQ2Knifv82rDx9HCzR1aE71YmR8JjwBc",
|
||||
"DailyRecommendTemplateId": "dQdK2i7ZDkDGQ2Knifv82rDx9HCzR1aE71YmR8JjwBc",
|
||||
"UnlockTemplateId": "1WwIIY4NoPWE972HfSgjmqcjmq59ihFfrUsuqlFGMzk",
|
||||
"FavoriteTemplateId": "1WwIIY4NoPWE972HfSgjmqcjmq59ihFfrUsuqlFGMzk",
|
||||
"MessageTemplateId": "1WwIIY4NoPWE972HfSgjmqcjmq59ihFfrUsuqlFGMzk",
|
||||
"DailyRecommendTemplateId": "1WwIIY4NoPWE972HfSgjmqcjmq59ihFfrUsuqlFGMzk",
|
||||
"FollowArticleUrl": ""
|
||||
},
|
||||
"SubscribeMessage": {
|
||||
|
|
|
|||
|
|
@ -91,6 +91,11 @@ public class AppConfigResponse
|
|||
/// </summary>
|
||||
public string? SearchBanner { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 实名认证页Banner URL
|
||||
/// </summary>
|
||||
public string? RealNameBanner { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 管家指导二维码URL
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -72,6 +72,16 @@ public interface ISystemConfigService
|
|||
/// </summary>
|
||||
Task<bool> SetSearchBannerAsync(string imageUrl);
|
||||
|
||||
/// <summary>
|
||||
/// 获取实名认证页Banner图URL
|
||||
/// </summary>
|
||||
Task<string?> GetRealNameBannerAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 设置实名认证页Banner图URL
|
||||
/// </summary>
|
||||
Task<bool> SetRealNameBannerAsync(string imageUrl);
|
||||
|
||||
/// <summary>
|
||||
/// 获取管家指导二维码URL
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ public class AuthService : IAuthService
|
|||
XiangQinNo = xiangQinNo,
|
||||
Avatar = defaultAvatar,
|
||||
Status = 1,
|
||||
ContactCount = 2,
|
||||
ContactCount = 0,
|
||||
CreateTime = DateTime.Now,
|
||||
UpdateTime = DateTime.Now
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ public class RecommendService : IRecommendService
|
|||
/// <summary>
|
||||
/// 普通用户推荐数量
|
||||
/// </summary>
|
||||
public const int NormalUserRecommendCount = 10;
|
||||
public const int NormalUserRecommendCount = 4;
|
||||
|
||||
/// <summary>
|
||||
/// 不限时会员推荐数量
|
||||
|
|
@ -539,8 +539,114 @@ public class RecommendService : IRecommendService
|
|||
return await GenerateDailyRecommendForUserAsync(userId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算候选用户满足择偶要求的条件数量(静态方法,用于测试)
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取候选用户列表
|
||||
/// 推荐优先级:
|
||||
/// 1. 全部满足择偶要求的候选人
|
||||
/// 2. 逐一减少满足条件数量
|
||||
/// 3. 同城优先,已实名用户优先
|
||||
/// 4. 家乡城市较近,已实名用户优先
|
||||
/// 原则上不推荐3天内推荐过的用户
|
||||
/// </summary>
|
||||
private async Task<List<CandidateUser>> 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; }
|
||||
/// <summary>
|
||||
/// 满足择偶要求的条件数量(用于逐一减少排序)
|
||||
/// </summary>
|
||||
public int MatchedConditionCount { get; set; }
|
||||
/// <summary>
|
||||
/// 择偶要求总条件数
|
||||
/// </summary>
|
||||
public int TotalConditionCount { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,11 @@ public class SystemConfigService : ISystemConfigService
|
|||
/// </summary>
|
||||
public const string SearchBannerKey = "search_banner";
|
||||
|
||||
/// <summary>
|
||||
/// 实名认证页Banner配置键
|
||||
/// </summary>
|
||||
public const string RealNameBannerKey = "realname_banner";
|
||||
|
||||
/// <summary>
|
||||
/// 管家二维码配置键
|
||||
/// </summary>
|
||||
|
|
@ -211,6 +216,18 @@ public class SystemConfigService : ISystemConfigService
|
|||
return await SetConfigValueAsync(SearchBannerKey, imageUrl, "搜索页Banner图URL");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string?> GetRealNameBannerAsync()
|
||||
{
|
||||
return await GetConfigValueAsync(RealNameBannerKey);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> SetRealNameBannerAsync(string imageUrl)
|
||||
{
|
||||
return await SetConfigValueAsync(RealNameBannerKey, imageUrl, "实名认证页Banner图URL");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string?> GetButlerQrcodeAsync()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -97,9 +97,9 @@ public class User : SoftDeleteEntity
|
|||
public DateTime? MemberExpireTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 剩余联系次数,默认2
|
||||
/// 剩余联系次数,默认0
|
||||
/// </summary>
|
||||
public int ContactCount { get; set; } = 2;
|
||||
public int ContactCount { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 最后登录时间
|
||||
|
|
|
|||
|
|
@ -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
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// 普通用户推荐数量应为10
|
||||
/// 普通用户推荐数量应为4
|
||||
/// </summary>
|
||||
[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
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// 家庭版会员推荐数量应为24
|
||||
/// 家庭版会员推荐数量应在24-29之间
|
||||
/// </summary>
|
||||
[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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user