逻辑优化
This commit is contained in:
parent
2f18801615
commit
23a620d69d
|
|
@ -11,6 +11,7 @@ export interface MemberTier {
|
|||
benefitsImage?: string
|
||||
sort: number
|
||||
status: number
|
||||
durationMonths: number
|
||||
createTime: string
|
||||
updateTime: string
|
||||
}
|
||||
|
|
@ -25,6 +26,7 @@ export interface CreateMemberTierRequest {
|
|||
benefitsImage?: string
|
||||
sort: number
|
||||
status: number
|
||||
durationMonths: number
|
||||
}
|
||||
|
||||
export interface UpdateMemberTierRequest extends CreateMemberTierRequest {}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@
|
|||
<span class="original-price">¥{{ tier.originalPrice }}</span>
|
||||
</div>
|
||||
<div class="tier-discount" v-if="tier.discount">{{ tier.discount }}</div>
|
||||
<div class="tier-duration">
|
||||
{{ tier.durationMonths === 0 ? '永久' : tier.durationMonths + '个月' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 权益图预览 -->
|
||||
|
|
@ -103,6 +106,7 @@
|
|||
<el-option :value="1" label="等级1 - 永久会员" />
|
||||
<el-option :value="2" label="等级2 - 诚意会员" />
|
||||
<el-option :value="3" label="等级3 - 家庭版会员" />
|
||||
<el-option :value="4" label="等级4 - 限时会员" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
|
@ -139,6 +143,15 @@
|
|||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="生效时长" prop="durationMonths">
|
||||
<el-input-number v-model="formData.durationMonths" :min="0" :max="999" :controls="false" style="width: 100%" placeholder="0表示永久" />
|
||||
<div style="font-size: 12px; color: #909399; margin-top: 4px;">填0表示永久,其他数字为月数</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="权益图片" prop="benefitsImage">
|
||||
<div class="benefits-upload-wrapper">
|
||||
<el-upload
|
||||
|
|
@ -232,7 +245,8 @@ const formData = reactive({
|
|||
discount: '',
|
||||
benefitsImage: '',
|
||||
sort: 0,
|
||||
status: 1
|
||||
status: 1,
|
||||
durationMonths: 0
|
||||
})
|
||||
|
||||
const formRules: FormRules = {
|
||||
|
|
@ -275,6 +289,7 @@ const resetForm = () => {
|
|||
formData.benefitsImage = ''
|
||||
formData.sort = 0
|
||||
formData.status = 1
|
||||
formData.durationMonths = 0
|
||||
editingId.value = null
|
||||
}
|
||||
|
||||
|
|
@ -296,6 +311,7 @@ const handleEdit = (row: MemberTier) => {
|
|||
formData.benefitsImage = row.benefitsImage || ''
|
||||
formData.sort = row.sort
|
||||
formData.status = row.status
|
||||
formData.durationMonths = row.durationMonths || 0
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
|
|
@ -356,7 +372,8 @@ const handleSubmit = async () => {
|
|||
discount: formData.discount || undefined,
|
||||
benefitsImage: formData.benefitsImage || undefined,
|
||||
sort: formData.sort,
|
||||
status: formData.status
|
||||
status: formData.status,
|
||||
durationMonths: formData.durationMonths
|
||||
}
|
||||
|
||||
if (isEdit.value && editingId.value) {
|
||||
|
|
@ -506,6 +523,16 @@ onMounted(() => {
|
|||
display: inline-block;
|
||||
}
|
||||
|
||||
.tier-duration {
|
||||
font-size: 12px;
|
||||
color: #409eff;
|
||||
background: #ecf5ff;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* 权益图预览 */
|
||||
.tier-image {
|
||||
height: 120px;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,8 @@ const memberLevelOptions = [
|
|||
{ label: '全部', value: '' },
|
||||
{ label: '不限时', value: 1 },
|
||||
{ label: '诚意', value: 2 },
|
||||
{ label: '家庭版', value: 3 }
|
||||
{ label: '家庭版', value: 3 },
|
||||
{ label: '限时', value: 4 }
|
||||
]
|
||||
|
||||
// 会员状态选项
|
||||
|
|
@ -121,7 +122,8 @@ const getMemberLevelType = (level: number): 'success' | 'warning' | 'danger' | '
|
|||
const types: Record<number, 'success' | 'warning' | 'danger' | 'info' | 'primary'> = {
|
||||
1: 'success',
|
||||
2: 'warning',
|
||||
3: 'danger'
|
||||
3: 'danger',
|
||||
4: 'primary'
|
||||
}
|
||||
return types[level] || 'info'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,7 +111,8 @@ const memberLevelOptions = [
|
|||
{ value: 0, label: '非会员' },
|
||||
{ value: 1, label: '不限时会员' },
|
||||
{ value: 2, label: '诚意会员' },
|
||||
{ value: 3, label: '家庭版会员' }
|
||||
{ value: 3, label: '家庭版会员' },
|
||||
{ value: 4, label: '限时会员' }
|
||||
]
|
||||
|
||||
// 修改会员等级对话框
|
||||
|
|
|
|||
|
|
@ -59,7 +59,8 @@ const memberLevelOptions = [
|
|||
{ label: '非会员', value: 0 },
|
||||
{ label: '不限时', value: 1 },
|
||||
{ label: '诚意', value: 2 },
|
||||
{ label: '家庭版', value: 3 }
|
||||
{ label: '家庭版', value: 3 },
|
||||
{ label: '限时', value: 4 }
|
||||
]
|
||||
|
||||
// 实名认证选项
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@
|
|||
return
|
||||
}
|
||||
|
||||
// 获取未读消息数并更新badge
|
||||
chatStore.fetchAllUnreadCounts()
|
||||
|
||||
try {
|
||||
// 连接 SignalR(如果已连接会直接返回)
|
||||
await signalR.connect()
|
||||
|
|
@ -110,7 +113,7 @@
|
|||
return
|
||||
}
|
||||
|
||||
// 不在聊天页面时,增加未读数
|
||||
// 不在聊天页面时,增加未读数并更新badge
|
||||
if (message.sessionId) {
|
||||
chatStore.incrementUnreadCount(message.sessionId)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,17 @@ export async function bindFamily(bindUserId) {
|
|||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过相亲编号绑定家庭成员
|
||||
*
|
||||
* @param {string} xiangQinNo - 相亲编号
|
||||
* @returns {Promise<Object>} 绑定结果
|
||||
*/
|
||||
export async function bindFamilyByXiangQinNo(xiangQinNo) {
|
||||
const response = await post('/member/bindFamilyByXiangQinNo', { xiangQinNo })
|
||||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取家庭成员列表
|
||||
*
|
||||
|
|
@ -66,6 +77,7 @@ export default {
|
|||
getMemberInfo,
|
||||
purchase,
|
||||
bindFamily,
|
||||
bindFamilyByXiangQinNo,
|
||||
getFamilyMembers,
|
||||
unbindFamilyMember
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ const ENV = {
|
|||
}
|
||||
|
||||
// 当前环境 - 开发时使用 development,打包时改为 production
|
||||
const CURRENT_ENV = 'production'
|
||||
const CURRENT_ENV = 'development'
|
||||
|
||||
// 导出配置
|
||||
export const config = {
|
||||
|
|
|
|||
|
|
@ -67,6 +67,9 @@
|
|||
<view class="system-info">
|
||||
<text class="system-title">系统消息</text>
|
||||
</view>
|
||||
<view class="system-badge" v-if="systemUnreadCount > 0">
|
||||
<text class="badge-text">{{ systemUnreadCount > 99 ? '99+' : systemUnreadCount }}</text>
|
||||
</view>
|
||||
<view class="system-arrow">
|
||||
<text class="arrow-icon">〉</text>
|
||||
</view>
|
||||
|
|
@ -161,6 +164,9 @@
|
|||
getInteractCounts,
|
||||
markInteractAsRead
|
||||
} from '@/api/interact.js'
|
||||
import {
|
||||
getUnreadCount as getSystemUnreadCount
|
||||
} from '@/api/message.js'
|
||||
import {
|
||||
formatTimestamp
|
||||
} from '@/utils/format.js'
|
||||
|
|
@ -181,6 +187,7 @@
|
|||
|
||||
// 数据
|
||||
const sessions = ref([])
|
||||
const systemUnreadCount = ref(0)
|
||||
const interactCounts = ref({
|
||||
viewedMe: 0,
|
||||
favoritedMe: 0,
|
||||
|
|
@ -247,7 +254,10 @@
|
|||
if (!userStore.isLoggedIn) return
|
||||
|
||||
try {
|
||||
const res = await getSessions()
|
||||
const [res, sysRes] = await Promise.all([
|
||||
getSessions(),
|
||||
getSystemUnreadCount()
|
||||
])
|
||||
console.log('[Message] 加载会话列表:', res)
|
||||
if (res?.success || res?.code === 0) {
|
||||
sessions.value = res.data || []
|
||||
|
|
@ -258,6 +268,11 @@
|
|||
})))
|
||||
chatStore.setSessions(sessions.value)
|
||||
}
|
||||
// 更新系统消息未读数
|
||||
if (sysRes?.code === 0) {
|
||||
systemUnreadCount.value = sysRes.data?.unreadCount || 0
|
||||
chatStore.setSystemUnreadCount(systemUnreadCount.value)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载会话列表失败:', error)
|
||||
uni.showToast({
|
||||
|
|
@ -645,6 +660,24 @@
|
|||
}
|
||||
}
|
||||
|
||||
.system-badge {
|
||||
min-width: 36rpx;
|
||||
height: 36rpx;
|
||||
background: #FF3B30;
|
||||
border-radius: 18rpx;
|
||||
padding: 0 10rpx;
|
||||
margin-right: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.badge-text {
|
||||
font-size: 20rpx;
|
||||
color: #fff;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.system-arrow {
|
||||
.arrow-icon {
|
||||
font-size: 32rpx;
|
||||
|
|
|
|||
|
|
@ -70,7 +70,8 @@
|
|||
<script>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useUserStore } from '@/store/user.js'
|
||||
import { getSystemMessages } from '@/api/message.js'
|
||||
import { useChatStore } from '@/store/chat.js'
|
||||
import { getSystemMessages, markAllSystemMessagesRead } from '@/api/message.js'
|
||||
import { formatTimestamp } from '@/utils/format.js'
|
||||
import Loading from '@/components/Loading/index.vue'
|
||||
import Empty from '@/components/Empty/index.vue'
|
||||
|
|
@ -83,6 +84,7 @@ export default {
|
|||
},
|
||||
setup() {
|
||||
const userStore = useUserStore()
|
||||
const chatStore = useChatStore()
|
||||
|
||||
// 状态栏高度
|
||||
const statusBarHeight = ref(20)
|
||||
|
|
@ -167,6 +169,13 @@ export default {
|
|||
getSystemInfo()
|
||||
userStore.restoreFromStorage()
|
||||
await loadMessages()
|
||||
// 进入系统消息页面后标记全部已读
|
||||
try {
|
||||
await markAllSystemMessagesRead()
|
||||
chatStore.setSystemUnreadCount(0)
|
||||
} catch (e) {
|
||||
console.error('标记系统消息已读失败:', e)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('初始化页面失败:', error)
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,49 @@
|
|||
<!-- 页面加载状态 -->
|
||||
<Loading type="page" :loading="pageLoading" />
|
||||
|
||||
<!-- 绑定家庭成员弹窗 -->
|
||||
<view class="family-popup-mask" v-if="showFamilyPopup" @click="closeFamilyPopup">
|
||||
<view class="family-popup" @click.stop>
|
||||
<view class="popup-title">绑定家庭成员</view>
|
||||
|
||||
<view class="popup-input-wrapper">
|
||||
<input
|
||||
v-model="bindXiangQinNo"
|
||||
class="popup-input"
|
||||
type="text"
|
||||
placeholder="请输入相亲编号"
|
||||
maxlength="20"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<button class="popup-btn" :class="{ 'has-content': bindXiangQinNo.trim() }" @click="handleBindFamily" :disabled="bindLoading">
|
||||
{{ bindLoading ? '绑定中...' : '确定' }}
|
||||
</button>
|
||||
|
||||
<view class="popup-divider"></view>
|
||||
|
||||
<view class="popup-members-title">已绑定的用户({{ familyMembers.length }}/3)</view>
|
||||
|
||||
<view class="popup-members-list" v-if="familyMembers.length > 0">
|
||||
<view class="member-item" v-for="member in familyMembers" :key="member.bindId">
|
||||
<image class="member-avatar" :src="member.avatar || defaultAvatar" mode="aspectFill" />
|
||||
<view class="member-info">
|
||||
<text class="member-nickname">{{ member.nickname || '用户' }}</text>
|
||||
<text class="member-no">{{ member.xiangQinNo }}</text>
|
||||
</view>
|
||||
<text class="member-unbind" @click="handleUnbindFamily(member)">解绑</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="popup-empty" v-else>
|
||||
<text>暂未绑定</text>
|
||||
</view>
|
||||
|
||||
<view class="popup-close" @click="closeFamilyPopup">
|
||||
<text>⊗</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 未登录状态 -->
|
||||
<view class="login-card" v-if="!isLoggedIn && !pageLoading" :style="{ marginTop: (statusBarHeight + 35) + 'px' }">
|
||||
<text class="login-tip">登陆后帮您更精确准备</text>
|
||||
|
|
@ -120,6 +163,12 @@
|
|||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="handleFamilyBind" v-if="isLoggedIn && canBindFamily">
|
||||
<image src="/static/ic_unlock.png" mode="aspectFit" class="menu-icon" />
|
||||
<text class="menu-title">绑定家庭成员</text>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="handleUserAgreement">
|
||||
<image src="/static/ic_agreement1.png" mode="aspectFit" class="menu-icon" />
|
||||
<text class="menu-title">用户协议</text>
|
||||
|
|
@ -152,6 +201,7 @@ import { ref, computed, onMounted } from 'vue'
|
|||
import { useUserStore } from '@/store/user.js'
|
||||
import { useConfigStore } from '@/store/config.js'
|
||||
import { getInteractCounts, markInteractAsRead } from '@/api/interact.js'
|
||||
import { bindFamilyByXiangQinNo, getFamilyMembers, unbindFamilyMember, getMemberInfo } from '@/api/member.js'
|
||||
import { getFullImageUrl } from '@/utils/image.js'
|
||||
import Loading from '@/components/Loading/index.vue'
|
||||
|
||||
|
|
@ -178,6 +228,13 @@ export default {
|
|||
unlockedMe: 0
|
||||
})
|
||||
|
||||
// 家庭成员绑定相关
|
||||
const showFamilyPopup = ref(false)
|
||||
const bindXiangQinNo = ref('')
|
||||
const bindLoading = ref(false)
|
||||
const familyMembers = ref([])
|
||||
const canBindFamily = ref(false) // 是否可以绑定家庭成员(只有主账号才能绑定)
|
||||
|
||||
// 获取系统信息
|
||||
const getSystemInfo = () => {
|
||||
uni.getSystemInfo({
|
||||
|
|
@ -233,6 +290,41 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载家庭成员列表
|
||||
*/
|
||||
const loadFamilyMembers = async () => {
|
||||
if (!userStore.isLoggedIn || userStore.memberLevel !== 3) return
|
||||
|
||||
try {
|
||||
const res = await getFamilyMembers()
|
||||
if (res?.code === 0 && res.data) {
|
||||
familyMembers.value = res.data.map(item => ({
|
||||
...item,
|
||||
avatar: getFullImageUrl(item.avatar)
|
||||
}))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载家庭成员失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载会员信息(检查是否可以绑定家庭成员)
|
||||
*/
|
||||
const loadMemberInfo = async () => {
|
||||
if (!userStore.isLoggedIn) return
|
||||
|
||||
try {
|
||||
const res = await getMemberInfo()
|
||||
if (res?.code === 0 && res.data) {
|
||||
canBindFamily.value = res.data.canBindFamily || false
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载会员信息失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化页面
|
||||
const initPage = async () => {
|
||||
pageLoading.value = true
|
||||
|
|
@ -240,6 +332,8 @@ export default {
|
|||
userStore.restoreFromStorage()
|
||||
if (userStore.isLoggedIn) {
|
||||
await loadInteractCounts()
|
||||
await loadMemberInfo()
|
||||
await loadFamilyMembers()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('初始化页面失败:', error)
|
||||
|
|
@ -278,6 +372,72 @@ export default {
|
|||
uni.navigateTo({ url: '/pages/realname/index' })
|
||||
}
|
||||
|
||||
// 绑定家庭成员 - 打开弹窗
|
||||
const handleFamilyBind = () => {
|
||||
showFamilyPopup.value = true
|
||||
loadFamilyMembers()
|
||||
}
|
||||
|
||||
// 关闭家庭成员弹窗
|
||||
const closeFamilyPopup = () => {
|
||||
showFamilyPopup.value = false
|
||||
bindXiangQinNo.value = ''
|
||||
}
|
||||
|
||||
// 确认绑定家庭成员
|
||||
const handleBindFamily = async () => {
|
||||
if (!bindXiangQinNo.value.trim()) {
|
||||
uni.showToast({ title: '请输入相亲编号', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
if (familyMembers.value.length >= 3) {
|
||||
uni.showToast({ title: '最多绑定3位家庭成员', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
bindLoading.value = true
|
||||
try {
|
||||
const res = await bindFamilyByXiangQinNo(bindXiangQinNo.value.trim())
|
||||
if (res?.code === 0) {
|
||||
uni.showToast({ title: '绑定成功', icon: 'success' })
|
||||
bindXiangQinNo.value = ''
|
||||
await loadFamilyMembers()
|
||||
} else {
|
||||
uni.showToast({ title: res?.message || '绑定失败', icon: 'none' })
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('绑定家庭成员失败:', error)
|
||||
uni.showToast({ title: '绑定失败', icon: 'none' })
|
||||
} finally {
|
||||
bindLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 解绑家庭成员
|
||||
const handleUnbindFamily = (member) => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: `确定要解绑 ${member.nickname || '该用户'} 吗?`,
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
const result = await unbindFamilyMember(member.userId)
|
||||
if (result?.code === 0) {
|
||||
uni.showToast({ title: '解绑成功', icon: 'success' })
|
||||
await loadFamilyMembers()
|
||||
} else {
|
||||
uni.showToast({ title: result?.message || '解绑失败', icon: 'none' })
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解绑失败:', error)
|
||||
uni.showToast({ title: '解绑失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 用户协议
|
||||
const handleUserAgreement = () => {
|
||||
uni.navigateTo({ url: '/pages/agreement/index?type=user' })
|
||||
|
|
@ -355,6 +515,17 @@ export default {
|
|||
memberEntryImageUrl,
|
||||
avatarLoadError,
|
||||
onAvatarError,
|
||||
// 家庭成员绑定
|
||||
showFamilyPopup,
|
||||
bindXiangQinNo,
|
||||
bindLoading,
|
||||
familyMembers,
|
||||
canBindFamily,
|
||||
handleFamilyBind,
|
||||
closeFamilyPopup,
|
||||
handleBindFamily,
|
||||
handleUnbindFamily,
|
||||
// 其他方法
|
||||
handleLogin,
|
||||
handlePersonalProfile,
|
||||
handleEditProfile,
|
||||
|
|
@ -791,4 +962,157 @@ export default {
|
|||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
// 家庭成员绑定弹窗
|
||||
.family-popup-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.family-popup {
|
||||
width: 600rpx;
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
padding: 48rpx 40rpx;
|
||||
position: relative;
|
||||
|
||||
.popup-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.popup-input-wrapper {
|
||||
margin-bottom: 32rpx;
|
||||
|
||||
.popup-input {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 12rpx;
|
||||
padding: 0 24rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.popup-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
background: #A4ADB3;
|
||||
border-radius: 12rpx;
|
||||
font-size: 32rpx;
|
||||
color: #fff;
|
||||
border: none;
|
||||
margin-bottom: 32rpx;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
&.has-content {
|
||||
background: #1F97E7;
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
background: #ccc;
|
||||
}
|
||||
}
|
||||
|
||||
.popup-divider {
|
||||
height: 1rpx;
|
||||
background: #eee;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.popup-members-title {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.popup-members-list {
|
||||
.member-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.member-avatar {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.member-info {
|
||||
flex: 1;
|
||||
|
||||
.member-nickname {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.member-no {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.member-unbind {
|
||||
font-size: 26rpx;
|
||||
color: #ff6b6b;
|
||||
padding: 8rpx 16rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.popup-empty {
|
||||
text-align: center;
|
||||
padding: 40rpx 0;
|
||||
|
||||
text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.popup-close {
|
||||
position: absolute;
|
||||
bottom: -100rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
text {
|
||||
font-size: 64rpx;
|
||||
color: #fff;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 2.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.4 KiB |
|
|
@ -4,6 +4,8 @@
|
|||
*/
|
||||
|
||||
import { defineStore } from 'pinia'
|
||||
import { getUnreadCount as getChatUnreadCount } from '../api/chat.js'
|
||||
import { getUnreadCount as getSystemUnreadCount } from '../api/message.js'
|
||||
|
||||
/**
|
||||
* 消息类型枚举
|
||||
|
|
@ -68,7 +70,10 @@ export const useChatStore = defineStore('chat', {
|
|||
currentSessionId: null,
|
||||
|
||||
// 总未读消息数
|
||||
totalUnreadCount: 0
|
||||
totalUnreadCount: 0,
|
||||
|
||||
// 系统消息未读数
|
||||
systemUnreadCount: 0
|
||||
}),
|
||||
|
||||
getters: {
|
||||
|
|
@ -86,6 +91,11 @@ export const useChatStore = defineStore('chat', {
|
|||
*/
|
||||
hasUnreadMessages: (state) => state.totalUnreadCount > 0,
|
||||
|
||||
/**
|
||||
* 消息tab总未读数(聊天 + 系统消息)
|
||||
*/
|
||||
messageBadgeCount: (state) => state.totalUnreadCount + state.systemUnreadCount,
|
||||
|
||||
/**
|
||||
* 获取指定会话的未读数
|
||||
*/
|
||||
|
|
@ -123,6 +133,51 @@ export const useChatStore = defineStore('chat', {
|
|||
(total, session) => total + (session.unreadCount || 0),
|
||||
0
|
||||
)
|
||||
this.updateTabBarBadge()
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置系统消息未读数
|
||||
*/
|
||||
setSystemUnreadCount(count) {
|
||||
this.systemUnreadCount = count
|
||||
this.updateTabBarBadge()
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新底部导航栏消息tab的badge
|
||||
*/
|
||||
updateTabBarBadge() {
|
||||
const total = this.messageBadgeCount
|
||||
if (total > 0) {
|
||||
uni.setTabBarBadge({
|
||||
index: 1,
|
||||
text: total > 99 ? '99+' : String(total)
|
||||
})
|
||||
} else {
|
||||
uni.removeTabBarBadge({ index: 1 })
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 从服务器获取所有未读数并更新badge
|
||||
*/
|
||||
async fetchAllUnreadCounts() {
|
||||
try {
|
||||
const [chatRes, sysRes] = await Promise.all([
|
||||
getChatUnreadCount(),
|
||||
getSystemUnreadCount()
|
||||
])
|
||||
if (chatRes?.code === 0) {
|
||||
this.totalUnreadCount = chatRes.data || 0
|
||||
}
|
||||
if (sysRes?.code === 0) {
|
||||
this.systemUnreadCount = sysRes.data?.unreadCount || 0
|
||||
}
|
||||
this.updateTabBarBadge()
|
||||
} catch (e) {
|
||||
console.error('获取未读数失败:', e)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -286,6 +341,8 @@ export const useChatStore = defineStore('chat', {
|
|||
this.messagesBySession = {}
|
||||
this.currentSessionId = null
|
||||
this.totalUnreadCount = 0
|
||||
this.systemUnreadCount = 0
|
||||
uni.removeTabBarBadge({ index: 1 })
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ public class AdminMemberTierController : ControllerBase
|
|||
BenefitsImage = t.BenefitsImage,
|
||||
Sort = t.Sort,
|
||||
Status = t.Status,
|
||||
DurationMonths = t.DurationMonths,
|
||||
CreateTime = t.CreateTime,
|
||||
UpdateTime = t.UpdateTime
|
||||
}).ToList();
|
||||
|
|
@ -75,6 +76,7 @@ public class AdminMemberTierController : ControllerBase
|
|||
BenefitsImage = tier.BenefitsImage,
|
||||
Sort = tier.Sort,
|
||||
Status = tier.Status,
|
||||
DurationMonths = tier.DurationMonths,
|
||||
CreateTime = tier.CreateTime,
|
||||
UpdateTime = tier.UpdateTime
|
||||
});
|
||||
|
|
@ -104,6 +106,7 @@ public class AdminMemberTierController : ControllerBase
|
|||
BenefitsImage = request.BenefitsImage,
|
||||
Sort = request.Sort,
|
||||
Status = request.Status,
|
||||
DurationMonths = request.DurationMonths,
|
||||
CreateTime = DateTime.Now,
|
||||
UpdateTime = DateTime.Now
|
||||
};
|
||||
|
|
@ -123,6 +126,7 @@ public class AdminMemberTierController : ControllerBase
|
|||
BenefitsImage = tier.BenefitsImage,
|
||||
Sort = tier.Sort,
|
||||
Status = tier.Status,
|
||||
DurationMonths = tier.DurationMonths,
|
||||
CreateTime = tier.CreateTime,
|
||||
UpdateTime = tier.UpdateTime
|
||||
});
|
||||
|
|
@ -159,6 +163,7 @@ public class AdminMemberTierController : ControllerBase
|
|||
tier.BenefitsImage = request.BenefitsImage;
|
||||
tier.Sort = request.Sort;
|
||||
tier.Status = request.Status;
|
||||
tier.DurationMonths = request.DurationMonths;
|
||||
tier.UpdateTime = DateTime.Now;
|
||||
|
||||
await _tierRepository.UpdateAsync(tier);
|
||||
|
|
@ -201,6 +206,7 @@ public class MemberTierDto
|
|||
public string? BenefitsImage { get; set; }
|
||||
public int Sort { get; set; }
|
||||
public int Status { get; set; }
|
||||
public int DurationMonths { get; set; }
|
||||
public DateTime? CreateTime { get; set; }
|
||||
public DateTime? UpdateTime { get; set; }
|
||||
}
|
||||
|
|
@ -219,6 +225,7 @@ public class CreateMemberTierRequest
|
|||
public string? BenefitsImage { get; set; }
|
||||
public int Sort { get; set; }
|
||||
public int Status { get; set; } = 1;
|
||||
public int DurationMonths { get; set; } = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -235,4 +242,5 @@ public class UpdateMemberTierRequest
|
|||
public string? BenefitsImage { get; set; }
|
||||
public int Sort { get; set; }
|
||||
public int Status { get; set; }
|
||||
public int DurationMonths { get; set; } = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,24 @@ public class MemberController : ControllerBase
|
|||
return ApiResponse<FamilyBindResponse>.Success(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过相亲编号绑定家庭成员
|
||||
/// </summary>
|
||||
/// <param name="request">绑定请求(包含相亲编号)</param>
|
||||
/// <returns>绑定结果</returns>
|
||||
[HttpPost("bindFamilyByXiangQinNo")]
|
||||
public async Task<ApiResponse<FamilyBindResponse>> BindFamilyByXiangQinNo([FromBody] FamilyBindByXiangQinNoRequest request)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.XiangQinNo))
|
||||
{
|
||||
return ApiResponse<FamilyBindResponse>.Error(ErrorCodes.InvalidParameter, "请输入相亲编号");
|
||||
}
|
||||
|
||||
var userId = GetCurrentUserId();
|
||||
var result = await _memberService.BindFamilyMemberByXiangQinNoAsync(userId, request);
|
||||
return ApiResponse<FamilyBindResponse>.Success(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取家庭成员列表
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -21,3 +21,14 @@ public class FamilyBindRequest
|
|||
/// </summary>
|
||||
public long BindUserId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过相亲编号绑定家庭成员请求
|
||||
/// </summary>
|
||||
public class FamilyBindByXiangQinNoRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 相亲编号
|
||||
/// </summary>
|
||||
public string XiangQinNo { get; set; } = string.Empty;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,14 @@ public interface IMemberService
|
|||
/// <returns>绑定结果</returns>
|
||||
Task<FamilyBindResponse> BindFamilyMemberAsync(long userId, FamilyBindRequest request);
|
||||
|
||||
/// <summary>
|
||||
/// 通过相亲编号绑定家庭成员
|
||||
/// </summary>
|
||||
/// <param name="userId">主账号用户ID</param>
|
||||
/// <param name="request">绑定请求(包含相亲编号)</param>
|
||||
/// <returns>绑定结果</returns>
|
||||
Task<FamilyBindResponse> BindFamilyMemberByXiangQinNoAsync(long userId, FamilyBindByXiangQinNoRequest request);
|
||||
|
||||
/// <summary>
|
||||
/// 获取家庭成员列表
|
||||
/// </summary>
|
||||
|
|
@ -75,5 +83,5 @@ public interface IMemberService
|
|||
/// <summary>
|
||||
/// 家庭版最大绑定数量(不含主账号)
|
||||
/// </summary>
|
||||
const int MaxFamilyBindCount = 2;
|
||||
const int MaxFamilyBindCount = 3;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ public class AdminMemberService : IAdminMemberService
|
|||
{
|
||||
{ 1, "不限时会员" },
|
||||
{ 2, "诚意会员" },
|
||||
{ 3, "家庭版会员" }
|
||||
{ 3, "家庭版会员" },
|
||||
{ 4, "限时会员" }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -122,14 +123,16 @@ public class AdminMemberService : IAdminMemberService
|
|||
var familyBinds = await _memberFamilyRepository.GetListAsync(f => memberIds.Contains(f.MemberId));
|
||||
var familyBindCountDict = familyBinds.GroupBy(f => f.MemberId).ToDictionary(g => g.Key, g => g.Count());
|
||||
|
||||
// Map to DTOs
|
||||
var dtos = items.Select(m =>
|
||||
{
|
||||
userDict.TryGetValue(m.UserId, out var user);
|
||||
orderDict.TryGetValue(m.OrderId, out var order);
|
||||
familyBindCountDict.TryGetValue(m.Id, out var familyBindCount);
|
||||
return MapToMemberListDto(m, user, order, familyBindCount);
|
||||
}).ToList();
|
||||
// Map to DTOs - 过滤掉没有关联用户的会员记录
|
||||
var dtos = items
|
||||
.Where(m => userDict.ContainsKey(m.UserId)) // 只保留有关联用户的会员
|
||||
.Select(m =>
|
||||
{
|
||||
userDict.TryGetValue(m.UserId, out var user);
|
||||
orderDict.TryGetValue(m.OrderId, out var order);
|
||||
familyBindCountDict.TryGetValue(m.Id, out var familyBindCount);
|
||||
return MapToMemberListDto(m, user, order, familyBindCount);
|
||||
}).ToList();
|
||||
|
||||
return new PagedResult<AdminMemberListDto>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -315,13 +315,24 @@ public class AdminUserService : IAdminUserService
|
|||
var femaleNames = new[] { "林", "何", "郭", "马", "罗", "梁", "宋", "郑", "谢", "韩", "唐", "冯", "于", "董", "萧" };
|
||||
var cities = new[] { "北京", "上海", "广州", "深圳", "杭州", "成都", "武汉", "南京", "苏州", "西安" };
|
||||
var provinces = new[] { "北京", "上海", "广东", "广东", "浙江", "四川", "湖北", "江苏", "江苏", "陕西" };
|
||||
var districts = new[] { "朝阳区", "浦东新区", "天河区", "南山区", "西湖区", "锦江区", "武昌区", "鼓楼区", "姑苏区", "雁塔区" };
|
||||
var occupations = new[] { "程序员", "设计师", "产品经理", "教师", "医生", "律师", "会计", "销售", "工程师", "公务员" };
|
||||
var introductions = new[] {
|
||||
"性格开朗,喜欢运动和旅行",
|
||||
"工作稳定,希望找到志同道合的另一半",
|
||||
"热爱生活,喜欢美食和电影",
|
||||
"踏实上进,有责任心",
|
||||
"温柔体贴,顾家爱家"
|
||||
"性格开朗,喜欢运动和旅行,希望找到一个志同道合的伴侣",
|
||||
"工作稳定,希望找到志同道合的另一半,一起经营美好生活",
|
||||
"热爱生活,喜欢美食和电影,周末喜欢去探店",
|
||||
"踏实上进,有责任心,对未来有清晰的规划",
|
||||
"温柔体贴,顾家爱家,喜欢做饭和养花"
|
||||
};
|
||||
var parentStatuses = new[] { "父母健在", "父母健在,身体健康", "父亲已故,母亲健在", "父母均已退休" };
|
||||
var parentRetireStatuses = new[] { "均已退休", "父亲退休,母亲在职", "均在职", "父亲在职,母亲退休" };
|
||||
var parentHousingStatuses = new[] { "自有住房", "自有住房(已还清贷款)", "租房", "与子女同住" };
|
||||
var pensionStatuses = new[] { "城镇职工养老保险", "城乡居民养老保险", "无养老保险" };
|
||||
var medicalStatuses = new[] { "城镇职工医保", "城乡居民医保", "商业医疗保险" };
|
||||
// 默认头像列表
|
||||
var defaultAvatars = new[] {
|
||||
"/uploads/avatars/default_male.png",
|
||||
"/uploads/avatars/default_female.png"
|
||||
};
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
|
|
@ -336,12 +347,29 @@ public class AdminUserService : IAdminUserService
|
|||
// 生成相亲编号
|
||||
var xiangQinNo = $"{random.Next(100000, 999999)}";
|
||||
|
||||
// 确定关系:1父亲 2母亲 3本人
|
||||
var relationship = random.Next(1, 4);
|
||||
|
||||
// 根据关系生成昵称
|
||||
var nickname = relationship switch
|
||||
{
|
||||
1 => $"{surname}家长(父亲)",
|
||||
2 => $"{surname}家长(母亲)",
|
||||
_ => userGender == 1 ? $"{surname}先生" : $"{surname}女士"
|
||||
};
|
||||
|
||||
// 生成手机号
|
||||
var phonePrefixes = new[] { "138", "139", "150", "151", "152", "158", "159", "186", "187", "188" };
|
||||
var phone = $"{phonePrefixes[random.Next(phonePrefixes.Length)]}{random.Next(10000000, 99999999)}";
|
||||
|
||||
// 生成用户
|
||||
var cityIndex = random.Next(cities.Length);
|
||||
var user = new User
|
||||
{
|
||||
OpenId = $"test_openid_{Guid.NewGuid():N}",
|
||||
Nickname = $"{surname}先生" + (userGender == 1 ? "" : "女士").Replace("先生女士", "女士"),
|
||||
Nickname = nickname,
|
||||
Avatar = defaultAvatars[userGender == 1 ? 0 : 1],
|
||||
Phone = phone,
|
||||
XiangQinNo = xiangQinNo,
|
||||
Gender = userGender,
|
||||
City = cities[cityIndex],
|
||||
|
|
@ -356,19 +384,20 @@ public class AdminUserService : IAdminUserService
|
|||
LastLoginTime = DateTime.Now.AddHours(-random.Next(72))
|
||||
};
|
||||
|
||||
// 如果是会员,设置会员等级
|
||||
// 如果是会员,设置会员等级(包含新增的限时会员等级4)
|
||||
if (user.IsMember)
|
||||
{
|
||||
user.MemberLevel = random.Next(1, 4);
|
||||
if (user.MemberLevel != 1) // 非不限时会员
|
||||
user.MemberLevel = random.Next(1, 5); // 1-4
|
||||
if (user.MemberLevel == 1) // 不限时会员
|
||||
{
|
||||
user.MemberExpireTime = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
user.MemberExpireTime = DateTime.Now.AddMonths(random.Next(1, 12));
|
||||
}
|
||||
}
|
||||
|
||||
// 修正昵称
|
||||
user.Nickname = userGender == 1 ? $"{surname}先生" : $"{surname}女士";
|
||||
|
||||
await _userRepository.AddAsync(user);
|
||||
|
||||
// 生成用户资料
|
||||
|
|
@ -376,13 +405,14 @@ public class AdminUserService : IAdminUserService
|
|||
var profile = new UserProfile
|
||||
{
|
||||
UserId = user.Id,
|
||||
Relationship = random.Next(1, 4), // 1父亲 2母亲 3本人
|
||||
Relationship = relationship,
|
||||
Surname = surname,
|
||||
ChildGender = userGender,
|
||||
BirthYear = birthYear,
|
||||
Education = random.Next(3, 7), // 3大专 4本科 5研究生 6博士
|
||||
WorkProvince = provinces[cityIndex],
|
||||
WorkCity = cities[cityIndex],
|
||||
WorkDistrict = districts[cityIndex],
|
||||
Occupation = occupations[random.Next(occupations.Length)],
|
||||
MonthlyIncome = random.Next(2, 5), // 2:5k-1w 3:1w-2w 4:2w-5w
|
||||
Height = userGender == 1 ? random.Next(168, 186) : random.Next(158, 172),
|
||||
|
|
@ -396,6 +426,14 @@ public class AdminUserService : IAdminUserService
|
|||
HomeProvince = provinces[random.Next(provinces.Length)],
|
||||
HomeCity = cities[random.Next(cities.Length)],
|
||||
WeChatNo = $"wx_test_{random.Next(100000, 999999)}",
|
||||
IsLivingAlone = random.Next(2) == 1,
|
||||
ParentStatus = parentStatuses[random.Next(parentStatuses.Length)],
|
||||
ParentRetireStatus = parentRetireStatuses[random.Next(parentRetireStatuses.Length)],
|
||||
ParentProvince = provinces[random.Next(provinces.Length)],
|
||||
ParentCity = cities[random.Next(cities.Length)],
|
||||
ParentHousingStatus = parentHousingStatuses[random.Next(parentHousingStatuses.Length)],
|
||||
ParentPensionStatus = pensionStatuses[random.Next(pensionStatuses.Length)],
|
||||
ParentMedicalStatus = medicalStatuses[random.Next(medicalStatuses.Length)],
|
||||
AuditStatus = 1, // 已通过审核
|
||||
AuditTime = DateTime.Now,
|
||||
CreateTime = user.CreateTime,
|
||||
|
|
@ -404,6 +442,27 @@ public class AdminUserService : IAdminUserService
|
|||
|
||||
await _profileRepository.AddAsync(profile);
|
||||
|
||||
// 生成择偶要求
|
||||
var reqCityIndex = random.Next(cities.Length);
|
||||
var requirement = new UserRequirement
|
||||
{
|
||||
UserId = user.Id,
|
||||
AgeMin = random.Next(22, 28),
|
||||
AgeMax = random.Next(30, 38),
|
||||
HeightMin = userGender == 1 ? random.Next(155, 165) : random.Next(170, 178),
|
||||
HeightMax = userGender == 1 ? random.Next(168, 175) : random.Next(180, 190),
|
||||
Education = "[3,4,5]", // 大专、本科、研究生
|
||||
City1Province = provinces[reqCityIndex],
|
||||
City1City = cities[reqCityIndex],
|
||||
MonthlyIncomeMin = random.Next(2, 4),
|
||||
MonthlyIncomeMax = random.Next(4, 6),
|
||||
MarriageStatus = "[1]", // 未婚
|
||||
CreateTime = user.CreateTime,
|
||||
UpdateTime = DateTime.Now
|
||||
};
|
||||
|
||||
await _requirementRepository.AddAsync(requirement);
|
||||
|
||||
createdIds.Add(user.Id);
|
||||
|
||||
_logger.LogInformation("创建测试用户成功: UserId={UserId}, Nickname={Nickname}",
|
||||
|
|
@ -475,7 +534,7 @@ public class AdminUserService : IAdminUserService
|
|||
}
|
||||
|
||||
// 验证会员等级
|
||||
if (memberLevel < 0 || memberLevel > 3)
|
||||
if (memberLevel < 0 || memberLevel > 4)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.InvalidParameter, "无效的会员等级");
|
||||
}
|
||||
|
|
@ -750,6 +809,7 @@ public class AdminUserService : IAdminUserService
|
|||
1 => "不限时会员",
|
||||
2 => "诚意会员",
|
||||
3 => "家庭版会员",
|
||||
4 => "限时会员",
|
||||
_ => "未知"
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ public class MemberService : IMemberService
|
|||
{
|
||||
{ 1, 1299m }, // 不限时会员
|
||||
{ 2, 1999m }, // 诚意会员
|
||||
{ 3, 2999m } // 家庭版会员
|
||||
{ 3, 2999m }, // 家庭版会员
|
||||
{ 4, 999m } // 限时会员
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -43,7 +44,8 @@ public class MemberService : IMemberService
|
|||
{ 0, "非会员" },
|
||||
{ 1, "不限时会员" },
|
||||
{ 2, "诚意会员" },
|
||||
{ 3, "家庭版会员" }
|
||||
{ 3, "家庭版会员" },
|
||||
{ 4, "限时会员" }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -54,7 +56,8 @@ public class MemberService : IMemberService
|
|||
{ 0, 10 }, // 非会员
|
||||
{ 1, 24 }, // 不限时会员
|
||||
{ 2, 29 }, // 诚意会员(24-29人)
|
||||
{ 3, 24 } // 家庭版会员
|
||||
{ 3, 24 }, // 家庭版会员
|
||||
{ 4, 24 } // 限时会员(与不限时会员相同)
|
||||
};
|
||||
|
||||
public MemberService(
|
||||
|
|
@ -99,6 +102,17 @@ public class MemberService : IMemberService
|
|||
BenefitsImage = t.BenefitsImage
|
||||
}).ToList();
|
||||
|
||||
// 判断是否可以绑定家庭成员
|
||||
// 条件:是家庭版会员 且 不是被别人绑定的用户
|
||||
var canBindFamily = false;
|
||||
var familyBindCount = 0;
|
||||
|
||||
if (user.MemberLevel == 3)
|
||||
{
|
||||
var isBoundByOthers = await _memberFamilyRepository.ExistsAsync(f => f.BindUserId == userId);
|
||||
canBindFamily = !isBoundByOthers;
|
||||
}
|
||||
|
||||
var response = new MemberInfoResponse
|
||||
{
|
||||
IsMember = user.IsMember,
|
||||
|
|
@ -107,7 +121,8 @@ public class MemberService : IMemberService
|
|||
ExpireTime = user.MemberExpireTime,
|
||||
ContactCount = user.ContactCount,
|
||||
DailyRecommendCount = GetDailyRecommendCount(user.MemberLevel),
|
||||
CanBindFamily = user.MemberLevel == 3, // 家庭版可绑定
|
||||
CanBindFamily = canBindFamily,
|
||||
FamilyBindCount = familyBindCount,
|
||||
MaxFamilyBindCount = IMemberService.MaxFamilyBindCount,
|
||||
Tiers = tiers
|
||||
};
|
||||
|
|
@ -125,12 +140,10 @@ public class MemberService : IMemberService
|
|||
response.StartTime = member.StartTime;
|
||||
response.ExpireTime = member.ExpireTime;
|
||||
|
||||
// 家庭版获取绑定数量
|
||||
if (user.MemberLevel == 3)
|
||||
// 主账号获取已绑定数量
|
||||
if (canBindFamily)
|
||||
{
|
||||
var bindCount = await _memberFamilyRepository.CountAsync(f =>
|
||||
f.MemberId == member.Id);
|
||||
response.FamilyBindCount = (int)bindCount;
|
||||
response.FamilyBindCount = (int)await _memberFamilyRepository.CountAsync(f => f.MemberId == member.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -320,6 +333,27 @@ public class MemberService : IMemberService
|
|||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<FamilyBindResponse> BindFamilyMemberByXiangQinNoAsync(long userId, FamilyBindByXiangQinNoRequest request)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.XiangQinNo))
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.InvalidParameter, "请输入相亲编号");
|
||||
}
|
||||
|
||||
// 通过相亲编号查找用户
|
||||
var bindUsers = await _userRepository.GetListAsync(u => u.XiangQinNo == request.XiangQinNo.Trim());
|
||||
var bindUser = bindUsers.FirstOrDefault();
|
||||
|
||||
if (bindUser == null)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.UserNotFound, "未找到该相亲编号对应的用户");
|
||||
}
|
||||
|
||||
// 调用原有的绑定方法
|
||||
return await BindFamilyMemberAsync(userId, new FamilyBindRequest { BindUserId = bindUser.Id });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<List<FamilyMemberDto>> GetFamilyMembersAsync(long userId)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -396,6 +396,11 @@ public class OrderService : IOrderService
|
|||
return;
|
||||
}
|
||||
|
||||
// 获取会员等级配置,读取生效时长
|
||||
var tierConfigs = await _tierConfigRepository.GetListAsync(t => t.Level == memberLevel && t.Status == 1);
|
||||
var tierConfig = tierConfigs.FirstOrDefault();
|
||||
var durationMonths = tierConfig?.DurationMonths ?? 0;
|
||||
|
||||
// 创建会员记录
|
||||
var member = new Member
|
||||
{
|
||||
|
|
@ -403,7 +408,7 @@ public class OrderService : IOrderService
|
|||
MemberLevel = memberLevel,
|
||||
OrderId = order.Id,
|
||||
StartTime = DateTime.Now,
|
||||
ExpireTime = memberLevel == 1 ? null : DateTime.Now.AddYears(1), // 不限时会员无过期时间
|
||||
ExpireTime = durationMonths == 0 ? null : DateTime.Now.AddMonths(durationMonths),
|
||||
Status = 1,
|
||||
CreateTime = DateTime.Now,
|
||||
UpdateTime = DateTime.Now
|
||||
|
|
|
|||
|
|
@ -176,12 +176,10 @@ public class PaymentService : IPaymentService
|
|||
var existingMembers = await _memberRepository.GetListAsync(m => m.UserId == order.UserId && m.Status == 1);
|
||||
var existingMember = existingMembers.FirstOrDefault();
|
||||
|
||||
DateTime? expireTime = memberLevel switch
|
||||
DateTime? expireTime = tierConfig?.DurationMonths switch
|
||||
{
|
||||
1 => null, // 永久会员
|
||||
2 => DateTime.Now.AddMonths(1), // 诚意会员1个月
|
||||
3 => DateTime.Now.AddYears(1), // 家庭版1年
|
||||
_ => DateTime.Now.AddMonths(1)
|
||||
null or 0 => null, // 永久会员
|
||||
var months => DateTime.Now.AddMonths(months.Value)
|
||||
};
|
||||
|
||||
if (existingMember != null)
|
||||
|
|
|
|||
|
|
@ -58,4 +58,9 @@ public class MemberTierConfig : BaseEntity
|
|||
/// 状态:1启用 2禁用
|
||||
/// </summary>
|
||||
public int Status { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// 会员生效时长(月),0表示永久
|
||||
/// </summary>
|
||||
public int DurationMonths { get; set; } = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -157,9 +157,9 @@ public class FamilyBindLimitPropertyTests
|
|||
/// 家庭版绑定 - 最大绑定数量应该是2
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void FamilyBind_MaxBindCount_ShouldBeTwo()
|
||||
public void FamilyBind_MaxBindCount_ShouldBeThree()
|
||||
{
|
||||
Assert.Equal(2, IMemberService.MaxFamilyBindCount);
|
||||
Assert.Equal(3, IMemberService.MaxFamilyBindCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user