From 0e84a4fe845403c52c14cc7370f9376de458cf0d Mon Sep 17 00:00:00 2001
From: 18631081161 <2088094923@qq.com>
Date: Sat, 7 Feb 2026 21:50:03 +0800
Subject: [PATCH] =?UTF-8?q?=E8=81=8A=E5=A4=A9,=E4=BC=9A=E5=91=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
admin/src/views/content/popup.vue | 3 +-
admin/src/views/user/detail.vue | 30 +-
admin/src/views/user/list.vue | 38 +-
miniapp/components/VoiceRecorder/index.vue | 324 ++++++++++--------
miniapp/config/index.js | 2 +-
miniapp/pages/chat/index.vue | 43 ++-
miniapp/pages/index/index.vue | 56 +--
.../Controllers/AdminUserController.cs | 4 +-
.../Controllers/OrderController.cs | 2 +-
.../Controllers/PayController.cs | 2 +-
.../Services/AdminUserService.cs | 12 +-
.../Services/OrderService.cs | 2 +-
.../Services/PaymentService.cs | 30 +-
.../Services/RecommendService.cs | 7 +-
14 files changed, 308 insertions(+), 247 deletions(-)
diff --git a/admin/src/views/content/popup.vue b/admin/src/views/content/popup.vue
index 0e67861..f211c2f 100644
--- a/admin/src/views/content/popup.vue
+++ b/admin/src/views/content/popup.vue
@@ -16,7 +16,8 @@ import type { PopupItem, UpdatePopupRequest } from '@/types/popup.d'
const popupTypes = [
{ type: 1, name: '每日首次弹窗', description: '用户每天首次打开小程序时展示' },
{ type: 2, name: '服务号关注弹窗', description: '引导用户关注公众号' },
- { type: 3, name: '会员广告弹窗', description: '会员推广广告条,显示在首页底部', showDisplayMode: true }
+ { type: 3, name: '会员广告弹窗', description: '会员推广广告条,显示在首页底部', showDisplayMode: true },
+ { type: 4, name: '订阅消息提醒条', description: '引导用户开启消息通知,显示在首页底部' }
]
// 显示模式选项
diff --git a/admin/src/views/user/detail.vue b/admin/src/views/user/detail.vue
index c344e80..797bad7 100644
--- a/admin/src/views/user/detail.vue
+++ b/admin/src/views/user/detail.vue
@@ -9,6 +9,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
import { ArrowLeft } from '@element-plus/icons-vue'
import StatusTag from '@/components/StatusTag/index.vue'
import { getUserDetail, updateUserStatus, updateContactCount, updateMemberLevel, cancelRealName } from '@/api/user'
+import { getMemberTierList } from '@/api/memberTier'
import { getFullImageUrl } from '@/utils/image'
import type { UserDetail } from '@/types/user.d'
@@ -106,14 +107,26 @@ const handleEditContactCount = async () => {
}
}
-// 会员等级选项
-const memberLevelOptions = [
- { value: 0, label: '非会员' },
- { value: 1, label: '不限时会员' },
- { value: 2, label: '诚意会员' },
- { value: 3, label: '家庭版会员' },
- { value: 4, label: '限时会员' }
-]
+// 会员等级选项 - 从后台配置动态获取
+const memberLevelOptions = ref([
+ { value: 0, label: '非会员' }
+])
+
+// 加载会员等级配置
+const loadMemberTierOptions = async () => {
+ try {
+ const res = await getMemberTierList()
+ const tiers = res.data || res || []
+ if (Array.isArray(tiers) && tiers.length > 0) {
+ memberLevelOptions.value = [
+ { value: 0, label: '非会员' },
+ ...tiers.map((t: any) => ({ value: t.level, label: t.name }))
+ ]
+ }
+ } catch (error) {
+ console.error('加载会员等级配置失败:', error)
+ }
+}
// 修改会员等级对话框
const memberLevelDialogVisible = ref(false)
@@ -257,6 +270,7 @@ const formatIncome = (min: number | undefined, max: number | undefined) => {
onMounted(() => {
fetchUserDetail()
+ loadMemberTierOptions()
})
diff --git a/admin/src/views/user/list.vue b/admin/src/views/user/list.vue
index dd4c4ba..4586661 100644
--- a/admin/src/views/user/list.vue
+++ b/admin/src/views/user/list.vue
@@ -11,6 +11,7 @@ import SearchForm from '@/components/SearchForm/index.vue'
import Pagination from '@/components/Pagination/index.vue'
import StatusTag from '@/components/StatusTag/index.vue'
import { getUserList, updateUserStatus, createTestUsers, deleteUser } from '@/api/user'
+import { getMemberTierList } from '@/api/memberTier'
import { getFullImageUrl } from '@/utils/image'
import type { UserListItem, UserQueryParams } from '@/types/user.d'
@@ -54,15 +55,28 @@ const memberOptions = [
{ label: '否', value: false }
]
-// 会员等级选项
-const memberLevelOptions = [
+// 会员等级选项 - 从后台配置动态获取
+const memberLevelOptions = ref([
{ label: '全部', value: '' },
- { label: '非会员', value: 0 },
- { label: '不限时', value: 1 },
- { label: '诚意', value: 2 },
- { label: '家庭版', value: 3 },
- { label: '限时', value: 4 }
-]
+ { label: '非会员', value: 0 }
+])
+
+// 加载会员等级配置
+const loadMemberTierOptions = async () => {
+ try {
+ const res = await getMemberTierList()
+ const tiers = res.data || res || []
+ if (Array.isArray(tiers) && tiers.length > 0) {
+ memberLevelOptions.value = [
+ { label: '全部', value: '' },
+ { label: '非会员', value: 0 },
+ ...tiers.map((t: any) => ({ label: t.name, value: t.level }))
+ ]
+ }
+ } catch (error) {
+ console.error('加载会员等级配置失败:', error)
+ }
+}
// 实名认证选项
const realNameOptions = [
@@ -213,8 +227,8 @@ const handleOpenTestUserDialog = () => {
// 创建测试用户
const handleCreateTestUsers = async () => {
- if (testUserForm.count < 1 || testUserForm.count > 50) {
- ElMessage.warning('创建数量必须在1-50之间')
+ if (testUserForm.count < 1 || testUserForm.count > 100) {
+ ElMessage.warning('创建数量必须在1-100之间')
return
}
@@ -233,6 +247,7 @@ const handleCreateTestUsers = async () => {
onMounted(() => {
fetchUserList()
+ loadMemberTierOptions()
})
@@ -559,9 +574,10 @@ onMounted(() => {
+
单次最多创建100个
diff --git a/miniapp/components/VoiceRecorder/index.vue b/miniapp/components/VoiceRecorder/index.vue
index e69ee16..d6dc9a9 100644
--- a/miniapp/components/VoiceRecorder/index.vue
+++ b/miniapp/components/VoiceRecorder/index.vue
@@ -4,107 +4,137 @@
- {{ isRecording ? '松开发送' : '按住说话' }}
+ {{ btnText }}
-
-
+
+
-
- 🎤
+
+ {{ isCancelling ? '↩' : '🎤' }}
{{ recordingTip }}
- {{ recordingTime }}s
+ {{ recordingTime }}s
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/chat/index.vue b/miniapp/pages/chat/index.vue
index 9986813..c8f84c5 100644
--- a/miniapp/pages/chat/index.vue
+++ b/miniapp/pages/chat/index.vue
@@ -39,7 +39,7 @@
scroll-y
scroll-with-animation
:scroll-into-view="scrollToId"
- :style="{ paddingTop: (statusBarHeight + 44) + 'px' }"
+ :style="{ paddingTop: (statusBarHeight + 44) + 'px', paddingBottom: (140 + keyboardHeight) + 'px' }"
@scrolltoupper="loadMoreMessages"
>
@@ -270,7 +270,7 @@
-
+
@@ -388,6 +390,9 @@ const hasMore = ref(true)
const pageIndex = ref(1)
const isInputFocused = ref(false)
+// 键盘高度
+const keyboardHeight = ref(0)
+
// 交换状态 - 记录是否已成功交换过微信/照片
const hasExchangedWeChat = ref(false)
const hasExchangedPhoto = ref(false)
@@ -1045,10 +1050,7 @@ const handleInputFocus = () => {
}
const handleInputBlur = () => {
- // 延迟隐藏,避免点击发送按钮时按钮消失
- setTimeout(() => {
- isInputFocused.value = false
- }, 100)
+ isInputFocused.value = false
}
const shouldShowTime = (message, index) => {
@@ -1100,6 +1102,14 @@ const handleMore = () => {
onMounted(async () => {
getSystemInfo()
+ // 监听键盘高度变化
+ uni.onKeyboardHeightChange((res) => {
+ keyboardHeight.value = res.height || 0
+ if (res.height > 0) {
+ nextTick(() => scrollToBottom())
+ }
+ })
+
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
const options = currentPage?.options || {}
@@ -1373,6 +1383,11 @@ const handleMessagesRead = (data) => {
onUnmounted(() => {
chatStore.clearCurrentSession()
+ // 移除键盘高度监听
+ // #ifdef MP-WEIXIN
+ uni.offKeyboardHeightChange()
+ // #endif
+
// 停止语音播放
stopVoice()
if (innerAudioContext.value) {
@@ -1455,7 +1470,6 @@ onUnmounted(() => {
.content-scroll {
flex: 1;
height: 0;
- padding-bottom: 280rpx;
background-color: #f8f8f8;
}
@@ -1877,6 +1891,8 @@ onUnmounted(() => {
right: 0;
background: #f5f6fa;
padding-bottom: env(safe-area-inset-bottom);
+ transition: bottom 0.15s ease-out;
+ z-index: 100;
.action-buttons {
display: flex;
@@ -2004,9 +2020,10 @@ onUnmounted(() => {
color: #fff;
border: none;
flex-shrink: 0;
+ text-align: center;
- &::after {
- border: none;
+ &:active {
+ opacity: 0.8;
}
&.active {
diff --git a/miniapp/pages/index/index.vue b/miniapp/pages/index/index.vue
index 4ac1037..53dcade 100644
--- a/miniapp/pages/index/index.vue
+++ b/miniapp/pages/index/index.vue
@@ -66,12 +66,8 @@
-
-
- 开启消息通知,不错过任何心动对象
-
- 打开
-
+
+
×
@@ -1304,53 +1300,5 @@ export default {
right: 0;
bottom: 0;
z-index: 98;
-
- .subscribe-reminder-bar {
- position: relative;
- display: flex;
- align-items: center;
- min-height: 100rpx;
- padding: 0 24rpx;
-
- .reminder-content {
- flex: 1;
- display: flex;
- align-items: center;
-
- .reminder-text {
- font-size: 26rpx;
- color: #fff;
- font-weight: 500;
- }
- }
-
- .reminder-btn {
- background: #fff;
- color: #667eea;
- font-size: 24rpx;
- padding: 12rpx 32rpx;
- border-radius: 30rpx;
- margin-right: 50rpx;
- font-weight: 500;
- }
-
- .reminder-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: rgba(255, 255, 255, 0.8);
- line-height: 1;
- }
- }
- }
}
diff --git a/server/src/XiangYi.AdminApi/Controllers/AdminUserController.cs b/server/src/XiangYi.AdminApi/Controllers/AdminUserController.cs
index 895a078..312bc4f 100644
--- a/server/src/XiangYi.AdminApi/Controllers/AdminUserController.cs
+++ b/server/src/XiangYi.AdminApi/Controllers/AdminUserController.cs
@@ -86,9 +86,9 @@ public class AdminUserController : ControllerBase
[OperationLog("用户管理", "新增", Description = "创建测试用户")]
public async Task>> CreateTestUsers([FromBody] CreateTestUsersRequest request)
{
- if (request.Count <= 0 || request.Count > 50)
+ if (request.Count <= 0 || request.Count > 100)
{
- return ApiResponse>.Error(40001, "创建数量必须在1-50之间");
+ return ApiResponse>.Error(40001, "创建数量必须在1-100之间");
}
var result = await _adminUserService.CreateTestUsersAsync(request.Count, request.Gender);
diff --git a/server/src/XiangYi.AppApi/Controllers/OrderController.cs b/server/src/XiangYi.AppApi/Controllers/OrderController.cs
index c64ba3d..82fbf8a 100644
--- a/server/src/XiangYi.AppApi/Controllers/OrderController.cs
+++ b/server/src/XiangYi.AppApi/Controllers/OrderController.cs
@@ -90,7 +90,7 @@ public class OrderController : ControllerBase
return ApiResponse.Error(ErrorCodes.InvalidParameter, "订单类型无效");
}
- if (request.OrderType == 1 && (!request.MemberLevel.HasValue || request.MemberLevel < 1 || request.MemberLevel > 3))
+ if (request.OrderType == 1 && (!request.MemberLevel.HasValue || request.MemberLevel < 1 || request.MemberLevel > 4))
{
return ApiResponse.Error(ErrorCodes.InvalidParameter, "会员等级无效");
}
diff --git a/server/src/XiangYi.AppApi/Controllers/PayController.cs b/server/src/XiangYi.AppApi/Controllers/PayController.cs
index 09767da..3c74136 100644
--- a/server/src/XiangYi.AppApi/Controllers/PayController.cs
+++ b/server/src/XiangYi.AppApi/Controllers/PayController.cs
@@ -49,7 +49,7 @@ public class PayController : ControllerBase
[HttpPost("createOrder")]
public async Task> CreateOrder([FromBody] CreateMemberOrderRequest request)
{
- if (request.MemberLevel < 1 || request.MemberLevel > 3)
+ if (request.MemberLevel < 1 || request.MemberLevel > 4)
{
return ApiResponse.Error(ErrorCodes.InvalidParameter, "无效的会员等级");
}
diff --git a/server/src/XiangYi.Application/Services/AdminUserService.cs b/server/src/XiangYi.Application/Services/AdminUserService.cs
index 1a453ef..59b7c54 100644
--- a/server/src/XiangYi.Application/Services/AdminUserService.cs
+++ b/server/src/XiangYi.Application/Services/AdminUserService.cs
@@ -19,6 +19,7 @@ public class AdminUserService : IAdminUserService
private readonly IRepository _photoRepository;
private readonly IRepository _requirementRepository;
private readonly IRepository _orderRepository;
+ private readonly ISystemConfigService _systemConfigService;
private readonly ILogger _logger;
public AdminUserService(
@@ -27,6 +28,7 @@ public class AdminUserService : IAdminUserService
IRepository photoRepository,
IRepository requirementRepository,
IRepository orderRepository,
+ ISystemConfigService systemConfigService,
ILogger logger)
{
_userRepository = userRepository;
@@ -34,6 +36,7 @@ public class AdminUserService : IAdminUserService
_photoRepository = photoRepository;
_requirementRepository = requirementRepository;
_orderRepository = orderRepository;
+ _systemConfigService = systemConfigService;
_logger = logger;
}
@@ -342,11 +345,8 @@ public class AdminUserService : IAdminUserService
var parentHousingStatuses = new[] { "自有住房", "自有住房(已还清贷款)", "租房", "与子女同住" };
var pensionStatuses = new[] { "城镇职工养老保险", "城乡居民养老保险", "无养老保险" };
var medicalStatuses = new[] { "城镇职工医保", "城乡居民医保", "商业医疗保险" };
- // 默认头像列表
- var defaultAvatars = new[] {
- "/uploads/avatars/default_male.png",
- "/uploads/avatars/default_female.png"
- };
+ // 从系统配置获取默认头像
+ var defaultAvatar = await _systemConfigService.GetDefaultAvatarAsync() ?? "/uploads/avatars/default.png";
for (int i = 0; i < count; i++)
{
@@ -381,7 +381,7 @@ public class AdminUserService : IAdminUserService
{
OpenId = $"test_openid_{Guid.NewGuid():N}",
Nickname = nickname,
- Avatar = defaultAvatars[userGender == 1 ? 0 : 1],
+ Avatar = defaultAvatar,
Phone = phone,
XiangQinNo = xiangQinNo,
Gender = userGender,
diff --git a/server/src/XiangYi.Application/Services/OrderService.cs b/server/src/XiangYi.Application/Services/OrderService.cs
index 9a25523..ec8a437 100644
--- a/server/src/XiangYi.Application/Services/OrderService.cs
+++ b/server/src/XiangYi.Application/Services/OrderService.cs
@@ -85,7 +85,7 @@ public class OrderService : IOrderService
if (request.OrderType == (int)OrderType.Membership)
{
// 会员订单 - 从数据库读取价格配置
- if (!request.MemberLevel.HasValue || request.MemberLevel < 1 || request.MemberLevel > 3)
+ if (!request.MemberLevel.HasValue || request.MemberLevel < 1 || request.MemberLevel > 4)
{
throw new BusinessException(ErrorCodes.InvalidParameter, "无效的会员等级");
}
diff --git a/server/src/XiangYi.Application/Services/PaymentService.cs b/server/src/XiangYi.Application/Services/PaymentService.cs
index a5633df..ec4b6bc 100644
--- a/server/src/XiangYi.Application/Services/PaymentService.cs
+++ b/server/src/XiangYi.Application/Services/PaymentService.cs
@@ -85,6 +85,7 @@ public class PaymentService : IPaymentService
Amount = tierConfig.Price,
PayAmount = tierConfig.Price,
Status = 1, // 待支付
+ Remark = $"会员等级:{request.MemberLevel}",
ExpireTime = DateTime.Now.AddMinutes(30), // 30分钟过期
CreateTime = DateTime.Now,
UpdateTime = DateTime.Now
@@ -167,10 +168,31 @@ public class PaymentService : IPaymentService
///
private async Task ActivateMembershipAsync(Order order)
{
- // 获取会员等级配置
- var tierConfigs = await _tierConfigRepository.GetListAsync(t => t.Name == order.ProductName);
- var tierConfig = tierConfigs.FirstOrDefault();
- var memberLevel = tierConfig?.Level ?? 1;
+ // 从订单备注解析会员等级(格式:"会员等级:3")
+ int? parsedLevel = null;
+ if (!string.IsNullOrEmpty(order.Remark) && order.Remark.StartsWith("会员等级:"))
+ {
+ var levelStr = order.Remark.Replace("会员等级:", "");
+ if (int.TryParse(levelStr, out var lv))
+ {
+ parsedLevel = lv;
+ }
+ }
+
+ // 优先用解析出的等级查配置,fallback 用商品名称匹配
+ MemberTierConfig? tierConfig = null;
+ if (parsedLevel.HasValue)
+ {
+ var tierConfigs = await _tierConfigRepository.GetListAsync(t => t.Level == parsedLevel.Value);
+ tierConfig = tierConfigs.FirstOrDefault();
+ }
+ if (tierConfig == null)
+ {
+ var tierConfigs = await _tierConfigRepository.GetListAsync(t => t.Name == order.ProductName);
+ tierConfig = tierConfigs.FirstOrDefault();
+ }
+
+ var memberLevel = tierConfig?.Level ?? parsedLevel ?? 1;
// 检查是否已有会员记录
var existingMembers = await _memberRepository.GetListAsync(m => m.UserId == order.UserId && m.Status == 1);
diff --git a/server/src/XiangYi.Application/Services/RecommendService.cs b/server/src/XiangYi.Application/Services/RecommendService.cs
index 9f0fa69..038cf6b 100644
--- a/server/src/XiangYi.Application/Services/RecommendService.cs
+++ b/server/src/XiangYi.Application/Services/RecommendService.cs
@@ -278,8 +278,11 @@ public class RecommendService : IRecommendService
// 确定推荐数量
var recommendCount = GetRecommendCountByMemberLevel(user.MemberLevel);
+ // 多获取一些候选用户,弥补查询时可能被过滤掉的(用户被禁用、资料未审核等)
+ var candidateCount = (int)Math.Ceiling(recommendCount * 1.5);
+
// 获取候选用户
- var candidates = await GetCandidateUsersAsync(userId, userProfile, userRequirement, recommendCount);
+ var candidates = await GetCandidateUsersAsync(userId, userProfile, userRequirement, candidateCount);
// 删除今日已有的推荐
var today = DateTime.Today;
@@ -349,6 +352,7 @@ public class RecommendService : IRecommendService
1 => UnlimitedMemberRecommendCount, // 不限时会员:24人
2 => _random.Next(SincereMemberMinRecommendCount, SincereMemberMaxRecommendCount + 1), // 诚意会员:24-29人
3 => FamilyMemberRecommendCount, // 家庭版会员:24人
+ 4 => UnlimitedMemberRecommendCount, // 限时会员:24人
_ => NormalUserRecommendCount
};
}
@@ -364,6 +368,7 @@ public class RecommendService : IRecommendService
1 => (UnlimitedMemberRecommendCount, UnlimitedMemberRecommendCount),
2 => (SincereMemberMinRecommendCount, SincereMemberMaxRecommendCount),
3 => (FamilyMemberRecommendCount, FamilyMemberRecommendCount),
+ 4 => (UnlimitedMemberRecommendCount, UnlimitedMemberRecommendCount),
_ => (NormalUserRecommendCount, NormalUserRecommendCount)
};
}