3 次代码提交 caefe0d646 ... 7f30b5ebfb

作者 SHA1 备注 提交日期
  yuxy 7f30b5ebfb Merge branch 'yxy' into test_dev 1 月之前
  yuxy 25994d875e Merge remote-tracking branch 'origin/test_dev' into test_dev 1 月之前
  YH_0525 9f0d8a33c3 红娘课程 1 月之前

+ 14 - 0
LiangZhiYUMao/pages.json

@@ -56,6 +56,20 @@
 				"navigationStyle": "custom"
 			}
 		},
+		{
+			"path": "pages/matchmaker-workbench/courses",
+			"style": {
+				"navigationBarTitleText": "培训课程",
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path": "pages/matchmaker-workbench/course-detail",
+			"style": {
+				"navigationBarTitleText": "课程详情",
+				"navigationStyle": "custom"
+			}
+		},
 		{
 			"path": "pages/matchmaker-workbench/ranking",
 			"style": {

+ 29 - 17
LiangZhiYUMao/pages/courses/list.vue

@@ -9,20 +9,20 @@
 			<view class="navbar-right"></view>
 		</view>
 
-		<!-- 课程分类标签 -->
-		<view class="course-tabs">
-			<view class="tab-item" :class="{ active: activeTab === 'all' }" @click="switchTab('all')">全部课程</view>
-			<view class="tab-item" :class="{ active: activeTab === 'basic' }" @click="switchTab('basic')">基础课程</view>
-			<view class="tab-item" :class="{ active: activeTab === 'advanced' }" @click="switchTab('advanced')">进阶课程</view>
-			<view class="tab-item" :class="{ active: activeTab === 'premium' }" @click="switchTab('premium')">精品课程</view>
-		</view>
-
-		<!-- 排序选项 -->
-		<view class="sort-options">
-			<view class="sort-item" :class="{ active: activeSort === 'recommend' }" @click="switchSort('recommend')">推荐排序</view>
-			<view class="sort-item" :class="{ active: activeSort === 'latest' }" @click="switchSort('latest')">最新课程</view>
-			<view class="sort-item" :class="{ active: activeSort === 'popular' }" @click="switchSort('popular')">热门课程</view>
-		</view>
+		<!-- 课程分类标签 - 横向滚动 -->
+		<scroll-view scroll-x class="category-scroll" show-scrollbar="false">
+			<view class="category-list">
+				<view 
+					v-for="(tab, index) in tabs" 
+					:key="index"
+					class="category-item" 
+					:class="{ active: activeTab === tab.type }"
+					@click="switchTab(tab.type)"
+				>
+					{{ tab.name }}
+				</view>
+			</view>
+		</scroll-view>
 
 		<!-- 课程列表 -->
 		<view class="course-grid">
@@ -71,8 +71,15 @@
 				hasMore: true,
 				loading: false,
 				DEFAULT_IMAGES,
+				// 顶部分类标签:全部 / 基础 / 进阶 / 精品
+				tabs: [
+					{ name: '全部课程', type: 'all' },
+					{ name: '基础课程', type: 'basic' },
+					{ name: '进阶课程', type: 'advanced' },
+					{ name: '精品课程', type: 'premium' }
+				],
 				activeTab: 'all', // 当前选中的标签页
-				activeSort: 'recommend', // 当前排序方式
+				activeSort: 'recommend', // 当前排序方式(暂保留,可根据需要扩展)
 				// 模拟数据,当API返回为空时使用
 				mockCourses: [
 					{
@@ -135,11 +142,16 @@
 
 				try {
 					console.log('尝试加载精品课程API数据...')
-					const response = await api.course.getList({
+					// 基础分页参数
+					const params = {
 						page: this.pageNum,
 						pageSize: this.pageSize,
 						status: 1
-					})
+					}
+					// 根据当前标签附加一个分类参数,后端可按需使用
+					// all / basic / advanced / premium
+					params.categoryType = this.activeTab
+					const response = await api.course.getList(params)
 
 					console.log('API返回数据:', response)
 

+ 548 - 0
LiangZhiYUMao/pages/matchmaker-workbench/course-detail.vue

@@ -0,0 +1,548 @@
+<template>
+	<view class="course-detail-page">
+		<!-- 自定义导航栏 -->
+		<view class="custom-navbar">
+			<view class="navbar-left" @click="goBack">
+				<text class="back-icon">←</text>
+			</view>
+			<view class="navbar-title">课程详情</view>
+			<view class="navbar-right">
+				<view class="points-info" @click="goToPointsDetail">
+					<text class="points-icon">💎</text>
+					<text class="points-value">{{ currentPoints }}</text>
+				</view>
+			</view>
+		</view>
+
+		<!-- 课程封面 -->
+		<view class="course-cover">
+			<image :src="course.cover_image" class="cover-image" mode="aspectFill"></image>
+			<view class="cover-mask">
+				<view class="points-tag">💎 {{ course.points_price || course.price * 10 }}积分</view>
+			</view>
+		</view>
+
+		<!-- 课程信息 -->
+		<view class="course-content">
+			<view class="course-title">{{ course.name }}</view>
+
+			<view class="teacher-info">
+				<image :src="course.teacher_avatar || DEFAULT_IMAGES.avatar" class="teacher-avatar" mode="aspectFill"></image>
+				<view class="teacher-detail">
+					<text class="teacher-name">{{ course.teacher_name }}</text>
+					<text class="teacher-desc">{{ course.teacher_desc || '资深情感导师' }}</text>
+				</view>
+			</view>
+
+			<view class="course-stats">
+				<view class="stat-item">
+					<text class="stat-icon">👥</text>
+					<text class="stat-text">{{ course.student_count || 0 }}人学习</text>
+				</view>
+				<view class="stat-item">
+					<text class="stat-icon">⭐</text>
+					<text class="stat-text">{{ course.rating || '5.0' }}分</text>
+				</view>
+				<view class="stat-item">
+					<text class="stat-icon">📚</text>
+					<text class="stat-text">{{ course.chapter_count || 0 }}个章节</text>
+				</view>
+			</view>
+
+			<view class="divider"></view>
+
+			<view class="description-section">
+				<view class="section-title">课程介绍</view>
+				<view class="description-text">{{ course.description || '暂无介绍' }}</view>
+			</view>
+
+			<view class="divider"></view>
+
+			<view class="outline-section">
+				<view class="section-title">课程大纲</view>
+				<view class="outline-text">{{ course.outline || '敬请期待' }}</view>
+			</view>
+		</view>
+
+		<!-- 底部兑换按钮 -->
+		<view class="bottom-bar">
+			<view class="price-info">
+				<text class="price-label">兑换所需</text>
+				<view class="price-value">
+					<text class="points-icon">💎</text>
+					<text class="price-num">{{ course.points_price || course.price * 10 }}</text>
+					<text class="price-unit">积分</text>
+				</view>
+			</view>
+			<view class="purchase-btn" :class="{ purchased: isPurchased }" @click="handleExchange">
+				<text class="btn-text">{{ isPurchased ? '立即学习' : '积分兑换' }}</text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import api from '@/utils/api.js'
+	import { DEFAULT_IMAGES } from '@/config/index.js'
+
+	export default {
+		data() {
+			return {
+				courseId: null,
+				course: {},
+				DEFAULT_IMAGES,
+				currentPoints: 0,
+				makerId: null,
+				isPurchased: false
+			}
+		},
+
+		onLoad(options) {
+			console.log('红娘课程详情页面加载, 参数:', options)
+			
+			if (options.id) {
+				this.courseId = options.id
+				this.loadMakerInfo()
+			}
+		},
+
+		onShow() {
+			this.loadCurrentPoints()
+		},
+
+		methods: {
+			// 加载红娘信息
+			async loadMakerInfo() {
+				try {
+					const userInfo = uni.getStorageSync('userInfo')
+					if (userInfo && userInfo.userId) {
+						const matchmakerInfo = await api.matchmaker.getByUserId(userInfo.userId)
+						if (matchmakerInfo) {
+							this.makerId = matchmakerInfo.matchmakerId || matchmakerInfo.matchmaker_id
+							this.loadCurrentPoints()
+							this.loadCourseDetail()
+							this.checkPurchaseStatus()
+						}
+					}
+				} catch (error) {
+					console.error('获取红娘信息失败:', error)
+					this.loadCourseDetail()
+				}
+			},
+
+			// 加载当前积分
+			async loadCurrentPoints() {
+				if (!this.makerId) return
+				try {
+					const res = await api.pointsMall.getBalance(this.makerId)
+					this.currentPoints = res || 0
+				} catch (error) {
+					console.error('获取积分失败:', error)
+				}
+			},
+
+			// 加载课程详情
+			async loadCourseDetail() {
+				try {
+					// 使用红娘课程独立API
+					const data = await api.matchmakerCourse.getDetail(this.courseId)
+					if (data) {
+						this.course = {
+							...data,
+							points_price: data.points || (data.price ? data.price * 10 : 100),
+							cover_image: data.cover_image || data.coverImage || this.DEFAULT_IMAGES.course,
+							teacher_name: data.teacher_name || data.instructor || '专业导师'
+						}
+					} else {
+						this.setMockCourse()
+					}
+				} catch (error) {
+					console.error('加载课程详情失败:', error)
+					this.setMockCourse()
+				}
+			},
+
+			// 设置模拟数据
+			setMockCourse() {
+				this.course = {
+					id: this.courseId,
+					name: '红娘专业培训课程',
+					teacher_name: '资深导师',
+					teacher_desc: '10年情感咨询经验',
+					rating: 4.9,
+					student_count: 128,
+					chapter_count: 12,
+					points_price: 1990,
+					description: '本课程专为红娘设计,涵盖相亲流程、沟通技巧、客户心理分析等核心内容,帮助您成为专业的婚恋顾问。',
+					outline: '第一章:红娘职业概述\n第二章:客户需求分析\n第三章:沟通技巧训练\n第四章:相亲活动组织\n第五章:成功案例分享',
+					cover_image: this.DEFAULT_IMAGES.course
+				}
+			},
+
+			// 检查是否已兑换
+			async checkPurchaseStatus() {
+				if (!this.makerId || !this.courseId) return
+				try {
+					// 使用红娘课程独立API
+					const res = await api.matchmakerCourse.checkExchanged(this.makerId, this.courseId)
+					if (res && res.exchanged !== undefined) {
+						this.isPurchased = res.exchanged
+					}
+				} catch (error) {
+					console.log('检查兑换状态失败:', error)
+				}
+			},
+
+			// 处理兑换
+			handleExchange() {
+				if (this.isPurchased) {
+					uni.showToast({
+						title: '学习功能开发中',
+						icon: 'none'
+					})
+					return
+				}
+
+				const pointsNeeded = this.course.points_price || this.course.price * 10
+
+				if (this.currentPoints < pointsNeeded) {
+					uni.showModal({
+						title: '积分不足',
+						content: `兑换该课程需要${pointsNeeded}积分,您当前只有${this.currentPoints}积分`,
+						showCancel: true,
+						cancelText: '取消',
+						confirmText: '去赚积分',
+						success: (res) => {
+							if (res.confirm) {
+								uni.navigateTo({
+									url: '/pages/matchmaker-workbench/earn-points'
+								})
+							}
+						}
+					})
+					return
+				}
+
+				uni.showModal({
+					title: '确认兑换',
+					content: `确定使用${pointsNeeded}积分兑换课程"${this.course.name}"吗?`,
+					success: async (res) => {
+						if (res.confirm) {
+							await this.doExchange(pointsNeeded)
+						}
+					}
+				})
+			},
+
+			// 执行兑换
+			async doExchange(pointsNeeded) {
+				uni.showLoading({ title: '兑换中...' })
+				try {
+					// 使用红娘课程独立API
+					const res = await api.matchmakerCourse.exchange({
+						makerId: this.makerId,
+						courseId: this.courseId,
+						points: pointsNeeded
+					})
+
+					if (res) {
+						uni.hideLoading()
+						uni.showToast({
+							title: '兑换成功',
+							icon: 'success'
+						})
+
+						this.isPurchased = true
+						this.currentPoints -= pointsNeeded
+						this.course.student_count = (this.course.student_count || 0) + 1
+
+						setTimeout(() => {
+							uni.showModal({
+								title: '兑换成功',
+								content: '您已成功兑换此课程,现在可以开始学习了!',
+								showCancel: false,
+								confirmText: '开始学习',
+								success: () => {
+									uni.showToast({
+										title: '学习功能开发中',
+										icon: 'none'
+									})
+								}
+							})
+						}, 1500)
+					}
+				} catch (error) {
+					uni.hideLoading()
+					console.error('兑换失败:', error)
+					uni.showToast({
+						title: error.msg || '兑换失败',
+						icon: 'none'
+					})
+				}
+			},
+
+			// 跳转积分明细
+			goToPointsDetail() {
+				uni.navigateTo({
+					url: '/pages/matchmaker-workbench/points-detail'
+				})
+			},
+
+			// 返回
+			goBack() {
+				uni.navigateBack({
+					fail: () => {
+						uni.navigateTo({
+							url: '/pages/matchmaker-workbench/courses'
+						})
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.course-detail-page {
+		min-height: 100vh;
+		background-color: #FFF9F9;
+		padding-top: 90rpx;
+		padding-bottom: 140rpx;
+	}
+
+	/* 自定义导航栏 */
+	.custom-navbar {
+		position: fixed;
+		top: 0;
+		left: 0;
+		right: 0;
+		height: 90rpx;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		padding: 0 20rpx;
+		background: linear-gradient(135deg, #FCE4EC 0%, #F8BBD0 100%);
+		z-index: 999;
+
+		.navbar-left,
+		.navbar-right {
+			width: 120rpx;
+		}
+
+		.back-icon {
+			font-size: 40rpx;
+			color: #333333;
+			font-weight: bold;
+		}
+
+		.navbar-title {
+			flex: 1;
+			text-align: center;
+			font-size: 32rpx;
+			font-weight: bold;
+			color: #333333;
+		}
+
+		.points-info {
+			display: flex;
+			align-items: center;
+			background: rgba(255, 255, 255, 0.8);
+			padding: 8rpx 16rpx;
+			border-radius: 30rpx;
+
+			.points-icon {
+				font-size: 24rpx;
+				margin-right: 6rpx;
+			}
+
+			.points-value {
+				font-size: 26rpx;
+				color: #E91E63;
+				font-weight: bold;
+			}
+		}
+	}
+
+	/* 课程封面 */
+	.course-cover {
+		position: relative;
+		width: 100%;
+		height: 400rpx;
+
+		.cover-image {
+			width: 100%;
+			height: 100%;
+		}
+
+		.cover-mask {
+			position: absolute;
+			bottom: 0;
+			left: 0;
+			right: 0;
+			padding: 20rpx;
+			background: linear-gradient(transparent, rgba(0, 0, 0, 0.5));
+
+			.points-tag {
+				display: inline-block;
+				background: linear-gradient(135deg, #E91E63 0%, #9C27B0 100%);
+				color: #FFFFFF;
+				font-size: 28rpx;
+				font-weight: bold;
+				padding: 10rpx 24rpx;
+				border-radius: 30rpx;
+			}
+		}
+	}
+
+	/* 课程内容 */
+	.course-content {
+		padding: 30rpx;
+
+		.course-title {
+			font-size: 36rpx;
+			font-weight: bold;
+			color: #333333;
+			margin-bottom: 30rpx;
+		}
+
+		.teacher-info {
+			display: flex;
+			align-items: center;
+			margin-bottom: 30rpx;
+
+			.teacher-avatar {
+				width: 80rpx;
+				height: 80rpx;
+				border-radius: 50%;
+				margin-right: 20rpx;
+			}
+
+			.teacher-detail {
+				display: flex;
+				flex-direction: column;
+
+				.teacher-name {
+					font-size: 30rpx;
+					font-weight: bold;
+					color: #333333;
+				}
+
+				.teacher-desc {
+					font-size: 24rpx;
+					color: #999999;
+					margin-top: 6rpx;
+				}
+			}
+		}
+
+		.course-stats {
+			display: flex;
+			justify-content: space-around;
+			padding: 20rpx 0;
+			background-color: #FFF5F8;
+			border-radius: 16rpx;
+			margin-bottom: 30rpx;
+
+			.stat-item {
+				display: flex;
+				align-items: center;
+
+				.stat-icon {
+					font-size: 28rpx;
+					margin-right: 8rpx;
+				}
+
+				.stat-text {
+					font-size: 26rpx;
+					color: #666666;
+				}
+			}
+		}
+
+		.divider {
+			height: 1rpx;
+			background-color: #F0F0F0;
+			margin: 30rpx 0;
+		}
+
+		.section-title {
+			font-size: 32rpx;
+			font-weight: bold;
+			color: #333333;
+			margin-bottom: 20rpx;
+		}
+
+		.description-text,
+		.outline-text {
+			font-size: 28rpx;
+			color: #666666;
+			line-height: 1.8;
+			white-space: pre-wrap;
+		}
+	}
+
+	/* 底部栏 */
+	.bottom-bar {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		height: 120rpx;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		padding: 0 30rpx;
+		background-color: #FFFFFF;
+		box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.1);
+
+		.price-info {
+			.price-label {
+				font-size: 24rpx;
+				color: #999999;
+			}
+
+			.price-value {
+				display: flex;
+				align-items: center;
+				margin-top: 6rpx;
+
+				.points-icon {
+					font-size: 28rpx;
+					margin-right: 6rpx;
+				}
+
+				.price-num {
+					font-size: 40rpx;
+					font-weight: bold;
+					color: #E91E63;
+				}
+
+				.price-unit {
+					font-size: 24rpx;
+					color: #E91E63;
+					margin-left: 6rpx;
+				}
+			}
+		}
+
+		.purchase-btn {
+			width: 280rpx;
+			height: 80rpx;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			background: linear-gradient(135deg, #E91E63 0%, #9C27B0 100%);
+			border-radius: 40rpx;
+
+			&.purchased {
+				background: linear-gradient(135deg, #4CAF50 0%, #8BC34A 100%);
+			}
+
+			.btn-text {
+				font-size: 30rpx;
+				font-weight: bold;
+				color: #FFFFFF;
+			}
+		}
+	}
+</style>

+ 621 - 0
LiangZhiYUMao/pages/matchmaker-workbench/courses.vue

@@ -0,0 +1,621 @@
+<template>
+	<view class="courses-page">
+		<!-- 自定义导航栏 -->
+		<view class="custom-navbar">
+			<view class="navbar-left" @click="goBack">
+				<text class="back-icon">←</text>
+			</view>
+			<view class="navbar-title">培训课程</view>
+			<view class="navbar-right">
+				<view class="points-info" @click="goToPointsDetail">
+					<text class="points-icon">💎</text>
+					<text class="points-value">{{ currentPoints }}</text>
+				</view>
+			</view>
+		</view>
+
+		<!-- 课程分类标签 -->
+		<view class="course-tabs">
+			<view class="tab-item" :class="{ active: activeTab === 'all' }" @click="switchTab('all')">全部课程</view>
+			<view class="tab-item" :class="{ active: activeTab === 'basic' }" @click="switchTab('basic')">基础课程</view>
+			<view class="tab-item" :class="{ active: activeTab === 'advanced' }" @click="switchTab('advanced')">进阶课程</view>
+			<view class="tab-item" :class="{ active: activeTab === 'premium' }" @click="switchTab('premium')">精品课程</view>
+		</view>
+
+		<!-- 排序选项 -->
+		<view class="sort-options">
+			<view class="sort-item" :class="{ active: activeSort === 'recommend' }" @click="switchSort('recommend')">推荐排序</view>
+			<view class="sort-item" :class="{ active: activeSort === 'latest' }" @click="switchSort('latest')">最新课程</view>
+			<view class="sort-item" :class="{ active: activeSort === 'popular' }" @click="switchSort('popular')">热门课程</view>
+		</view>
+
+		<!-- 课程列表 -->
+		<view class="course-grid">
+			<view class="course-card" v-for="(item, index) in courseList" :key="index"
+				@click="goToDetail(item.id)">
+				<image :src="item.cover_image" class="course-image" mode="aspectFill"></image>
+				<!-- 已兑换标签 -->
+				<view class="purchased-tag" v-if="item.isPurchased">已兑换</view>
+				<view class="course-info">
+					<view class="course-name">{{ item.name }}</view>
+					<view class="course-meta">
+						<text class="course-teacher">{{ item.teacher_name }}</text>
+						<text class="course-rating">⭐ {{ item.rating || 4.5 }}</text>
+					</view>
+					<view class="course-footer">
+						<view class="course-price">
+							<text class="points-icon">💎</text>
+							<text class="price-value">{{ item.points_price || item.price * 10 }}</text>
+							<text class="price-unit">积分</text>
+						</view>
+						<text class="course-students">{{ item.student_count || 0 }}人学习</text>
+					</view>
+					<view class="course-btn" :class="{ purchased: item.isPurchased }" @click.stop="handleExchange(item)">
+						{{ item.isPurchased ? '立即学习' : '积分兑换' }}
+					</view>
+				</view>
+			</view>
+		</view>
+
+		<!-- 加载更多 -->
+		<view class="load-more" v-if="hasMore">
+			<text class="load-text">{{ loading ? '加载中...' : '上拉加载更多' }}</text>
+		</view>
+		<view class="no-more" v-else-if="courseList.length > 0">
+			<text class="no-more-text">没有更多了</text>
+		</view>
+		<view class="empty-state" v-else-if="!loading">
+			<text class="empty-text">暂无课程</text>
+		</view>
+	</view>
+</template>
+
+<script>
+	import api from '@/utils/api.js'
+	import { DEFAULT_IMAGES } from '@/config/index.js'
+
+	export default {
+		data() {
+			return {
+				courseList: [],
+				pageNum: 1,
+				pageSize: 10,
+				total: 0,
+				hasMore: true,
+				loading: false,
+				DEFAULT_IMAGES,
+				activeTab: 'all',
+				activeSort: 'recommend',
+				currentPoints: 0,
+				makerId: null,
+				purchasedCourseIds: []
+			}
+		},
+
+		onLoad() {
+			console.log('红娘培训课程页面加载')
+			this.loadMakerInfo()
+		},
+
+		onShow() {
+			// 每次显示页面时刷新积分
+			this.loadCurrentPoints()
+		},
+
+		onReachBottom() {
+			if (this.hasMore && !this.loading) {
+				this.pageNum++
+				this.loadCourseList()
+			}
+		},
+
+		methods: {
+			// 加载红娘信息
+			async loadMakerInfo() {
+				try {
+					const userInfo = uni.getStorageSync('userInfo')
+					if (userInfo && userInfo.userId) {
+						const matchmakerInfo = await api.matchmaker.getByUserId(userInfo.userId)
+						if (matchmakerInfo) {
+							this.makerId = matchmakerInfo.matchmakerId || matchmakerInfo.matchmaker_id
+							console.log('红娘ID:', this.makerId)
+							this.loadCurrentPoints()
+							this.loadPurchasedCourses()
+							this.loadCourseList()
+						}
+					}
+				} catch (error) {
+					console.error('获取红娘信息失败:', error)
+					this.loadCourseList()
+				}
+			},
+
+			// 加载当前积分
+			async loadCurrentPoints() {
+				if (!this.makerId) return
+				try {
+					const res = await api.pointsMall.getBalance(this.makerId)
+					this.currentPoints = res || 0
+					console.log('当前积分:', this.currentPoints)
+				} catch (error) {
+					console.error('获取积分失败:', error)
+				}
+			},
+
+			// 加载已兑换的课程
+			async loadPurchasedCourses() {
+				if (!this.makerId) return
+				try {
+					// 调用红娘课程API获取已兑换的课程ID列表
+					const res = await api.matchmakerCourse.getPurchasedList(this.makerId)
+					if (res && Array.isArray(res)) {
+						this.purchasedCourseIds = res.map(item => item.course_id || item.courseId || item.id)
+					}
+					console.log('已兑换课程ID:', this.purchasedCourseIds)
+				} catch (error) {
+					console.error('获取已兑换课程失败:', error)
+				}
+			},
+
+			// 加载课程列表
+			async loadCourseList() {
+				if (this.loading) return
+				this.loading = true
+
+				try {
+					console.log('加载红娘培训课程列表...', this.activeTab)
+					// 使用红娘课程独立API,根据当前tab传入分类类型
+					const response = await api.matchmakerCourse.getList({
+						categoryType: this.activeTab
+					})
+
+					let courseData = []
+					let totalCount = 0
+
+					if (response && response.list) {
+						courseData = response.list
+						totalCount = response.total || 0
+					} else if (Array.isArray(response)) {
+						courseData = response
+						totalCount = response.length
+					}
+
+					if (courseData && courseData.length > 0) {
+						const processedData = courseData.map(item => ({
+							...item,
+							cover_image: item.cover_image || item.coverImage || this.DEFAULT_IMAGES.course,
+							teacher_name: item.teacher_name || item.instructor || '专业导师',
+							student_count: item.student_count || item.participants || 0,
+							points_price: item.points || (item.price ? item.price * 10 : 100),
+							isPurchased: this.purchasedCourseIds.includes(item.id)
+						}))
+
+						if (this.pageNum === 1) {
+							this.courseList = processedData
+							this.total = totalCount
+						} else {
+							this.courseList = [...this.courseList, ...processedData]
+						}
+
+						this.hasMore = this.courseList.length < totalCount
+					} else {
+						this.hasMore = false
+						if (this.pageNum === 1) {
+							this.courseList = this.getMockCourses()
+							this.total = this.courseList.length
+						}
+					}
+				} catch (error) {
+					console.error('加载课程列表失败:', error)
+					if (this.pageNum === 1) {
+						this.courseList = this.getMockCourses()
+						this.total = this.courseList.length
+					}
+					this.hasMore = false
+				} finally {
+					this.loading = false
+				}
+			},
+
+			// 模拟数据
+			getMockCourses() {
+				return [
+					{
+						id: 1,
+						name: '非暴力沟通:让问题更...',
+						teacher_name: '王老师',
+						rating: 4.9,
+						points_price: 2990,
+						student_count: 54,
+						cover_image: 'https://images.unsplash.com/photo-1506744038136-46273834b3fb?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60',
+						isPurchased: false
+					},
+					{
+						id: 2,
+						name: '红娘新手入门:相亲场...',
+						teacher_name: '张老师',
+						rating: 4.5,
+						points_price: 890,
+						student_count: 33,
+						cover_image: 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60',
+						isPurchased: false
+					},
+					{
+						id: 3,
+						name: '从零做红娘:牵线必备...',
+						teacher_name: '高老师',
+						rating: 4.9,
+						points_price: 1990,
+						student_count: 45,
+						cover_image: 'https://images.unsplash.com/photo-1516321318423-f06f85e504b3?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60',
+						isPurchased: false
+					},
+					{
+						id: 4,
+						name: '新手红娘必学:相亲沟...',
+						teacher_name: '魏老师',
+						rating: 4.4,
+						points_price: 990,
+						student_count: 54,
+						cover_image: 'https://images.unsplash.com/photo-1502672260266-1c1ef2d93688?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60',
+						isPurchased: false
+					}
+				]
+			},
+
+			// 切换标签页
+			switchTab(tab) {
+				this.activeTab = tab
+				this.pageNum = 1
+				this.courseList = []
+				this.hasMore = true
+				this.loadCourseList()
+			},
+
+			// 切换排序
+			switchSort(sort) {
+				this.activeSort = sort
+				if (this.activeSort === 'popular') {
+					this.courseList.sort((a, b) => (b.student_count || 0) - (a.student_count || 0))
+				} else if (this.activeSort === 'latest') {
+					this.courseList.sort((a, b) => b.id - a.id)
+				}
+			},
+
+			// 处理兑换
+			handleExchange(item) {
+				if (item.isPurchased) {
+					// 已兑换,跳转学习
+					this.goToDetail(item.id)
+					return
+				}
+
+				const pointsNeeded = item.points_price || item.price * 10
+				
+				if (this.currentPoints < pointsNeeded) {
+					uni.showModal({
+						title: '积分不足',
+						content: `兑换该课程需要${pointsNeeded}积分,您当前只有${this.currentPoints}积分`,
+						showCancel: true,
+						cancelText: '取消',
+						confirmText: '去赚积分',
+						success: (res) => {
+							if (res.confirm) {
+								uni.navigateTo({
+									url: '/pages/matchmaker-workbench/earn-points'
+								})
+							}
+						}
+					})
+					return
+				}
+
+				uni.showModal({
+					title: '确认兑换',
+					content: `确定使用${pointsNeeded}积分兑换课程"${item.name}"吗?`,
+					success: async (res) => {
+						if (res.confirm) {
+							await this.doExchange(item, pointsNeeded)
+						}
+					}
+				})
+			},
+
+			// 执行兑换
+			async doExchange(item, pointsNeeded) {
+				uni.showLoading({ title: '兑换中...' })
+				try {
+					// 调用红娘课程兑换API
+					const res = await api.matchmakerCourse.exchange({
+						makerId: this.makerId,
+						courseId: item.id,
+						points: pointsNeeded
+					})
+
+					if (res) {
+						uni.hideLoading()
+						uni.showToast({
+							title: '兑换成功',
+							icon: 'success'
+						})
+						
+						// 更新状态
+						item.isPurchased = true
+						this.purchasedCourseIds.push(item.id)
+						this.currentPoints -= pointsNeeded
+						
+						// 跳转到课程详情
+						setTimeout(() => {
+							this.goToDetail(item.id)
+						}, 1500)
+					}
+				} catch (error) {
+					uni.hideLoading()
+					console.error('兑换失败:', error)
+					uni.showToast({
+						title: error.msg || '兑换失败',
+						icon: 'none'
+					})
+				}
+			},
+
+			// 跳转到详情
+			goToDetail(id) {
+				uni.navigateTo({
+					url: `/pages/matchmaker-workbench/course-detail?id=${id}`
+				})
+			},
+
+			// 跳转积分明细
+			goToPointsDetail() {
+				uni.navigateTo({
+					url: '/pages/matchmaker-workbench/points-detail'
+				})
+			},
+
+			// 返回
+			goBack() {
+				uni.navigateBack({
+					fail: () => {
+						uni.navigateTo({
+							url: '/pages/matchmaker-workbench/mine'
+						})
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.courses-page {
+		min-height: 100vh;
+		background-color: #FFF9F9;
+		padding-top: 90rpx;
+	}
+
+	/* 自定义导航栏 */
+	.custom-navbar {
+		position: fixed;
+		top: 0;
+		left: 0;
+		right: 0;
+		height: 90rpx;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		padding: 0 20rpx;
+		background: linear-gradient(135deg, #FCE4EC 0%, #F8BBD0 100%);
+		z-index: 999;
+
+		.navbar-left,
+		.navbar-right {
+			width: 120rpx;
+		}
+
+		.back-icon {
+			font-size: 40rpx;
+			color: #333333;
+			font-weight: bold;
+		}
+
+		.navbar-title {
+			flex: 1;
+			text-align: center;
+			font-size: 32rpx;
+			font-weight: bold;
+			color: #333333;
+		}
+
+		.points-info {
+			display: flex;
+			align-items: center;
+			background: rgba(255, 255, 255, 0.8);
+			padding: 8rpx 16rpx;
+			border-radius: 30rpx;
+
+			.points-icon {
+				font-size: 24rpx;
+				margin-right: 6rpx;
+			}
+
+			.points-value {
+				font-size: 26rpx;
+				color: #E91E63;
+				font-weight: bold;
+			}
+		}
+	}
+
+	/* 课程分类标签 */
+	.course-tabs {
+		display: flex;
+		align-items: center;
+		justify-content: space-around;
+		padding: 20rpx 0;
+		background-color: #FFFFFF;
+		border-bottom: 1rpx solid #F0F0F0;
+
+		.tab-item {
+			padding: 12rpx 24rpx;
+			font-size: 28rpx;
+			color: #666666;
+			border-radius: 30rpx;
+			transition: all 0.3s;
+
+			&.active {
+				background-color: #9C27B0;
+				color: #FFFFFF;
+			}
+		}
+	}
+
+	/* 排序选项 */
+	.sort-options {
+		display: flex;
+		align-items: center;
+		justify-content: space-around;
+		padding: 20rpx 0;
+		background-color: #FFFFFF;
+		border-bottom: 1rpx solid #F0F0F0;
+
+		.sort-item {
+			font-size: 26rpx;
+			color: #666666;
+			transition: all 0.3s;
+
+			&.active {
+				color: #9C27B0;
+				font-weight: bold;
+			}
+		}
+	}
+
+	/* 课程列表 - 两列布局 */
+	.course-grid {
+		display: grid;
+		grid-template-columns: 1fr 1fr;
+		gap: 20rpx;
+		padding: 20rpx;
+
+		.course-card {
+			position: relative;
+			background-color: #FFFFFF;
+			border-radius: 20rpx;
+			overflow: hidden;
+			box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
+
+			.course-image {
+				width: 100%;
+				height: 240rpx;
+				background-color: #F5F5F5;
+			}
+
+			.purchased-tag {
+				position: absolute;
+				top: 16rpx;
+				right: 16rpx;
+				background: linear-gradient(135deg, #4CAF50 0%, #8BC34A 100%);
+				color: #FFFFFF;
+				font-size: 22rpx;
+				padding: 6rpx 16rpx;
+				border-radius: 20rpx;
+			}
+
+			.course-info {
+				padding: 20rpx;
+
+				.course-name {
+					font-size: 28rpx;
+					font-weight: bold;
+					color: #333333;
+					margin-bottom: 10rpx;
+					display: -webkit-box;
+					-webkit-box-orient: vertical;
+					-webkit-line-clamp: 2;
+					overflow: hidden;
+				}
+
+				.course-meta {
+					display: flex;
+					justify-content: space-between;
+					font-size: 24rpx;
+					color: #666666;
+					margin-bottom: 10rpx;
+				}
+
+				.course-footer {
+					display: flex;
+					justify-content: space-between;
+					align-items: center;
+					margin-bottom: 15rpx;
+
+					.course-price {
+						display: flex;
+						align-items: center;
+						color: #E91E63;
+						font-weight: bold;
+
+						.points-icon {
+							font-size: 22rpx;
+							margin-right: 4rpx;
+						}
+
+						.price-value {
+							font-size: 32rpx;
+						}
+
+						.price-unit {
+							font-size: 22rpx;
+							margin-left: 4rpx;
+						}
+					}
+
+					.course-students {
+						font-size: 22rpx;
+						color: #999999;
+					}
+				}
+
+				.course-btn {
+					width: 100%;
+					height: 60rpx;
+					display: flex;
+					align-items: center;
+					justify-content: center;
+					background-color: #9C27B0;
+					color: #FFFFFF;
+					border-radius: 30rpx;
+					font-size: 26rpx;
+
+					&.purchased {
+						background: linear-gradient(135deg, #4CAF50 0%, #8BC34A 100%);
+					}
+				}
+			}
+		}
+	}
+
+	/* 加载更多 */
+	.load-more,
+	.no-more {
+		padding: 30rpx 0;
+		text-align: center;
+
+		.load-text,
+		.no-more-text {
+			font-size: 24rpx;
+			color: #999999;
+		}
+	}
+
+	.empty-state {
+		padding: 100rpx 0;
+		text-align: center;
+
+		.empty-text {
+			font-size: 28rpx;
+			color: #999999;
+		}
+	}
+</style>

+ 2 - 2
LiangZhiYUMao/pages/matchmaker-workbench/index.vue

@@ -271,10 +271,10 @@ export default {
 					url: '/pages/matchmaker-workbench/quality-resources'
 				})
 			},
-			// 导航到课程培训
+			// 导航到课程培训(红娘专属)
 			navigateToCourses() {
 				uni.navigateTo({
-					url: '/pages/courses/list'
+					url: '/pages/matchmaker-workbench/courses'
 				})
 			},
 			// 导航到积分商城

+ 60 - 0
LiangZhiYUMao/utils/api.js

@@ -256,6 +256,66 @@ export default {
       url: `/course/purchase/${courseId}`, 
       method: 'POST', 
       data 
+    }),
+    
+    // 积分兑换课程(红娘端 - 旧接口,已废弃)
+    exchange: (data) => request({
+      url: '/course/exchange',
+      method: 'POST',
+      data
+    }),
+    
+    // 获取已兑换的课程列表(红娘端 - 旧接口,已废弃)
+    getPurchasedList: (makerId) => request({
+      url: `/course/purchased?makerId=${makerId}`,
+      method: 'GET'
+    })
+  },
+
+  // 红娘课程相关(独立于用户课程)
+  matchmakerCourse: {
+    // 获取红娘课程列表
+    getList: (params = {}) => request({
+      url: '/matchmaker-course/list',
+      method: 'GET',
+      data: params
+    }),
+    
+    // 根据分类获取红娘课程列表
+    getListByCategory: (categoryName) => request({
+      url: `/matchmaker-course/list/category?categoryName=${encodeURIComponent(categoryName)}`,
+      method: 'GET'
+    }),
+    
+    // 获取所有课程分类
+    getCategories: () => request({
+      url: '/matchmaker-course/categories',
+      method: 'GET'
+    }),
+    
+    // 获取红娘课程详情
+    getDetail: (id) => request({
+      url: `/matchmaker-course/detail/${id}`,
+      method: 'GET'
+    }),
+    
+    // 检查是否已兑换
+    checkExchanged: (makerId, courseId) => request({
+      url: `/matchmaker-course/check-exchanged?makerId=${makerId}&courseId=${courseId}`,
+      method: 'GET'
+    }),
+    
+    // 积分兑换课程
+    exchange: (data) => request({
+      url: '/matchmaker-course/exchange',
+      method: 'POST',
+      data
+    }),
+    
+    // 获取已兑换的课程列表
+    getPurchasedList: (makerId) => request({
+      url: `/matchmaker-course/purchased?makerId=${makerId}`,
+      method: 'GET'
     })
   },
 

+ 9 - 0
gateway/src/main/resources/application.yml

@@ -170,6 +170,15 @@ spring:
           filters:
             - StripPrefix=0
 
+        # 红娘课程服务路由(homePage服务,端口8081)
+        # 红娘专属课程,独立于用户课程
+        - id: matchmaker-course-route
+          uri: http://localhost:8081
+          predicates:
+            - Path=/api/matchmaker-course/**
+          filters:
+            - StripPrefix=0
+
         # 推荐服务路由(Recommend服务,端口8089)
         # 注意:必须在homepage-route之前,避免被兜底路由覆盖
         - id: recommend-route

+ 156 - 0
service/homePage/src/main/java/com/zhentao/controller/MatchmakerCourseController.java

@@ -0,0 +1,156 @@
+package com.zhentao.controller;
+
+import com.zhentao.entity.MatchmakerCourse;
+import com.zhentao.service.MatchmakerCourseService;
+import com.zhentao.common.Result;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 红娘课程控制器(独立于用户课程)
+ */
+@RestController
+@RequestMapping("/api/matchmaker-course")
+public class MatchmakerCourseController {
+    
+    @Autowired
+    private MatchmakerCourseService matchmakerCourseService;
+    
+    /**
+     * 获取所有启用的红娘课程列表,可按分类类型筛选
+     */
+    @GetMapping("/list")
+    public Result<List<MatchmakerCourse>> getCourseList(@RequestParam(required = false, defaultValue = "all") String categoryType) {
+        try {
+            List<MatchmakerCourse> courses = matchmakerCourseService.getActiveCourses(categoryType);
+            return Result.success(courses);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return Result.error("获取课程列表失败:" + e.getMessage());
+        }
+    }
+    
+    /**
+     * 根据分类获取红娘课程列表
+     */
+    @GetMapping("/list/category")
+    public Result<List<MatchmakerCourse>> getCoursesByCategory(@RequestParam String categoryName) {
+        try {
+            List<MatchmakerCourse> courses = matchmakerCourseService.getCoursesByCategory(categoryName);
+            return Result.success(courses);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return Result.error("获取课程列表失败:" + e.getMessage());
+        }
+    }
+    
+    /**
+     * 获取所有课程分类
+     */
+    @GetMapping("/categories")
+    public Result<List<String>> getCategories() {
+        try {
+            List<String> categories = matchmakerCourseService.getAllCategories();
+            return Result.success(categories);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return Result.error("获取分类列表失败:" + e.getMessage());
+        }
+    }
+    
+    /**
+     * 根据ID获取红娘课程详情
+     */
+    @GetMapping("/detail/{id}")
+    public Result<MatchmakerCourse> getCourseDetail(@PathVariable Integer id) {
+        try {
+            MatchmakerCourse course = matchmakerCourseService.getCourseById(id);
+            if (course == null) {
+                return Result.error("课程不存在");
+            }
+            return Result.success(course);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return Result.error("获取课程详情失败:" + e.getMessage());
+        }
+    }
+    
+    /**
+     * 检查红娘是否已兑换某课程
+     */
+    @GetMapping("/check-exchanged")
+    public Result<Map<String, Object>> checkExchanged(@RequestParam Integer makerId, @RequestParam Integer courseId) {
+        try {
+            boolean exchanged = matchmakerCourseService.hasExchanged(makerId, courseId);
+            Map<String, Object> data = new HashMap<>();
+            data.put("exchanged", exchanged);
+            return Result.success(data);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return Result.error("检查兑换状态失败:" + e.getMessage());
+        }
+    }
+    
+    /**
+     * 积分兑换课程
+     */
+    @PostMapping("/exchange")
+    public Result<Map<String, Object>> exchangeCourse(@RequestBody Map<String, Object> requestBody) {
+        try {
+            Integer makerId = requestBody.get("makerId") != null ? Integer.valueOf(requestBody.get("makerId").toString()) : null;
+            Integer courseId = requestBody.get("courseId") != null ? Integer.valueOf(requestBody.get("courseId").toString()) : null;
+            Integer points = requestBody.get("points") != null ? Integer.valueOf(requestBody.get("points").toString()) : null;
+            
+            if (makerId == null || courseId == null || points == null) {
+                return Result.error("参数不完整");
+            }
+            
+            // 检查课程是否存在
+            MatchmakerCourse course = matchmakerCourseService.getCourseById(courseId);
+            if (course == null) {
+                return Result.error("课程不存在");
+            }
+            
+            // 检查是否已兑换
+            if (matchmakerCourseService.hasExchanged(makerId, courseId)) {
+                return Result.error("您已兑换过该课程");
+            }
+            
+            // 调用服务层进行兑换
+            boolean success = matchmakerCourseService.exchangeCourse(makerId, courseId, points);
+            if (success) {
+                Map<String, Object> data = new HashMap<>();
+                data.put("courseId", courseId);
+                data.put("courseName", course.getName());
+                data.put("pointsUsed", points);
+                return Result.success("兑换成功", data);
+            }
+            return Result.error("兑换失败,请检查积分余额");
+        } catch (Exception e) {
+            e.printStackTrace();
+            return Result.error("兑换失败:" + e.getMessage());
+        }
+    }
+    
+    /**
+     * 获取已兑换的课程列表
+     */
+    @GetMapping("/purchased")
+    public Result<List<Map<String, Object>>> getPurchasedCourses(@RequestParam Integer makerId) {
+        try {
+            if (makerId == null) {
+                return Result.error("红娘ID不能为空");
+            }
+            
+            List<Map<String, Object>> purchasedList = matchmakerCourseService.getPurchasedCourses(makerId);
+            return Result.success(purchasedList);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return Result.error("获取已兑换课程失败:" + e.getMessage());
+        }
+    }
+}

+ 122 - 0
service/homePage/src/main/java/com/zhentao/entity/MatchmakerCourse.java

@@ -0,0 +1,122 @@
+package com.zhentao.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 红娘课程实体类(独立于用户课程)
+ */
+@Data
+@TableName("matchmaker_courses")
+public class MatchmakerCourse implements Serializable {
+    
+    private static final long serialVersionUID = 1L;
+    
+    /**
+     * 课程ID
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    @JsonProperty("id")
+    private Integer id;
+    
+    /**
+     * 课程分类名称(如:销售技巧、客户沟通等)
+     */
+    @TableField("category_name")
+    @JsonProperty("category_name")
+    private String categoryName;
+    
+    /**
+     * 课程名称
+     */
+    private String name;
+    
+    /**
+     * 讲师姓名
+     */
+    @TableField("instructor")
+    @JsonProperty("teacher_name")
+    private String instructor;
+    
+    /**
+     * 课程描述
+     */
+    private String description;
+    
+    /**
+     * 课程详细内容
+     */
+    private String content;
+    
+    /**
+     * 所需积分(红娘用积分兑换)
+     */
+    private Integer points;
+    
+    /**
+     * 课程时长(分钟)
+     */
+    private Integer duration;
+    
+    /**
+     * 课程评分(如:4.9)
+     */
+    private Float rating;
+    
+    /**
+     * 已兑换人数
+     */
+    @TableField("participants")
+    @JsonProperty("student_count")
+    private Integer participants;
+    
+    /**
+     * 课程封面图URL
+     */
+    @TableField("cover_image")
+    @JsonProperty("cover_image")
+    private String coverImage;
+    
+    /**
+     * 是否推荐(1:推荐,0:不推荐)
+     */
+    private Integer isRecommended;
+    
+    /**
+     * 难度等级:1-基础,2-进阶,3-高级
+     */
+    private Integer difficulty;
+    
+    /**
+     * 状态:0-禁用,1-启用
+     */
+    private Integer status;
+    
+    /**
+     * 课程创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createdTime;
+    
+    /**
+     * 课程更新时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updatedTime;
+    
+    public Integer getStatus() {
+        return status;
+    }
+    
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+}

+ 46 - 0
service/homePage/src/main/java/com/zhentao/mapper/MatchmakerCourseMapper.java

@@ -0,0 +1,46 @@
+package com.zhentao.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zhentao.entity.MatchmakerCourse;
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 红娘课程Mapper接口
+ */
+@Mapper
+public interface MatchmakerCourseMapper extends BaseMapper<MatchmakerCourse> {
+    
+    /**
+     * 插入课程兑换记录
+     */
+    @Insert("INSERT INTO matchmaker_course_exchange (maker_id, course_id, points_used, exchange_time, create_time) " +
+            "VALUES (#{makerId}, #{courseId}, #{points}, #{exchangeTime}, NOW())")
+    void insertCourseExchange(@Param("makerId") Integer makerId, 
+                              @Param("courseId") Integer courseId, 
+                              @Param("points") Integer points,
+                              @Param("exchangeTime") Date exchangeTime);
+    
+    /**
+     * 查询红娘已兑换的课程列表
+     */
+    @Select("SELECT ce.course_id, ce.points_used, ce.exchange_time, c.name as course_name, " +
+            "c.cover_image, c.instructor, c.duration, c.rating, c.description " +
+            "FROM matchmaker_course_exchange ce " +
+            "LEFT JOIN matchmaker_courses c ON ce.course_id = c.id " +
+            "WHERE ce.maker_id = #{makerId} " +
+            "ORDER BY ce.exchange_time DESC")
+    List<Map<String, Object>> selectPurchasedCourses(@Param("makerId") Integer makerId);
+    
+    /**
+     * 检查红娘是否已兑换某课程
+     */
+    @Select("SELECT COUNT(*) FROM matchmaker_course_exchange WHERE maker_id = #{makerId} AND course_id = #{courseId}")
+    int checkExchanged(@Param("makerId") Integer makerId, @Param("courseId") Integer courseId);
+}

+ 58 - 0
service/homePage/src/main/java/com/zhentao/service/MatchmakerCourseService.java

@@ -0,0 +1,58 @@
+package com.zhentao.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zhentao.entity.MatchmakerCourse;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 红娘课程服务接口
+ */
+public interface MatchmakerCourseService extends IService<MatchmakerCourse> {
+    
+    /**
+     * 获取所有启用的红娘课程列表,可按分类类型筛选
+     * 
+     * @param categoryType 分类类型:all/basic/advanced/premium
+     */
+    List<MatchmakerCourse> getActiveCourses(String categoryType);
+    
+    /**
+     * 根据分类获取红娘课程列表
+     */
+    List<MatchmakerCourse> getCoursesByCategory(String categoryName);
+    
+    /**
+     * 根据ID获取红娘课程
+     */
+    MatchmakerCourse getCourseById(Integer id);
+    
+    /**
+     * 积分兑换课程
+     * 
+     * @param makerId 红娘ID
+     * @param courseId 课程ID
+     * @param points 所需积分
+     * @return 是否兑换成功
+     */
+    boolean exchangeCourse(Integer makerId, Integer courseId, Integer points);
+    
+    /**
+     * 获取红娘已兑换的课程列表
+     * 
+     * @param makerId 红娘ID
+     * @return 已兑换的课程列表
+     */
+    List<Map<String, Object>> getPurchasedCourses(Integer makerId);
+    
+    /**
+     * 检查红娘是否已兑换某课程
+     */
+    boolean hasExchanged(Integer makerId, Integer courseId);
+    
+    /**
+     * 获取所有课程分类
+     */
+    List<String> getAllCategories();
+}

+ 150 - 0
service/homePage/src/main/java/com/zhentao/service/impl/MatchmakerCourseServiceImpl.java

@@ -0,0 +1,150 @@
+package com.zhentao.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zhentao.entity.MatchmakerCourse;
+import com.zhentao.mapper.MatchmakerCourseMapper;
+import com.zhentao.service.MatchmakerCourseService;
+import com.zhentao.service.PointsManageService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 红娘课程服务实现类
+ */
+@Service
+public class MatchmakerCourseServiceImpl extends ServiceImpl<MatchmakerCourseMapper, MatchmakerCourse> implements MatchmakerCourseService {
+    
+    @Autowired(required = false)
+    private PointsManageService pointsManageService;
+    
+    @Autowired
+    private MatchmakerCourseMapper matchmakerCourseMapper;
+    
+    @Override
+    public List<MatchmakerCourse> getActiveCourses(String categoryType) {
+        LambdaQueryWrapper<MatchmakerCourse> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(MatchmakerCourse::getStatus, 1);
+
+        // basic -> 基础课程(difficulty = 1)
+        // advanced -> 进阶课程(difficulty = 2)
+        // premium -> 精品课程(is_recommended = 1)
+        if ("basic".equalsIgnoreCase(categoryType)) {
+            wrapper.eq(MatchmakerCourse::getDifficulty, 1);
+        } else if ("advanced".equalsIgnoreCase(categoryType)) {
+            wrapper.eq(MatchmakerCourse::getDifficulty, 2);
+        } else if ("premium".equalsIgnoreCase(categoryType)) {
+            wrapper.eq(MatchmakerCourse::getIsRecommended, 1);
+        }
+
+        wrapper.orderByDesc(MatchmakerCourse::getIsRecommended)
+               .orderByDesc(MatchmakerCourse::getRating)
+               .orderByDesc(MatchmakerCourse::getCreatedTime);
+        return list(wrapper);
+    }
+    
+    @Override
+    public List<MatchmakerCourse> getCoursesByCategory(String categoryName) {
+        LambdaQueryWrapper<MatchmakerCourse> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(MatchmakerCourse::getStatus, 1)
+               .eq(MatchmakerCourse::getCategoryName, categoryName)
+               .orderByDesc(MatchmakerCourse::getCreatedTime);
+        return list(wrapper);
+    }
+    
+    @Override
+    public MatchmakerCourse getCourseById(Integer id) {
+        return getById(id);
+    }
+    
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean exchangeCourse(Integer makerId, Integer courseId, Integer points) {
+        if (makerId == null || courseId == null || points == null || points <= 0) {
+            return false;
+        }
+        
+        // 检查课程是否存在
+        MatchmakerCourse course = getById(courseId);
+        if (course == null) {
+            return false;
+        }
+        
+        // 检查是否已兑换过该课程
+        if (hasExchanged(makerId, courseId)) {
+            System.out.println("红娘已兑换过该课程,makerId=" + makerId + ", courseId=" + courseId);
+            return false;
+        }
+        
+        // 扣除积分
+        if (pointsManageService != null) {
+            boolean deducted = pointsManageService.deductPoints(makerId, points, "兑换红娘课程: " + course.getName());
+            if (!deducted) {
+                System.out.println("积分扣除失败,makerId=" + makerId + ", points=" + points);
+                return false;
+            }
+        } else {
+            System.out.println("警告:PointsManageService未注入,跳过积分扣除");
+        }
+        
+        // 记录兑换信息到数据库
+        try {
+            matchmakerCourseMapper.insertCourseExchange(makerId, courseId, points, new Date());
+            
+            // 更新课程参与人数
+            course.setParticipants(course.getParticipants() != null ? course.getParticipants() + 1 : 1);
+            updateById(course);
+            
+            System.out.println("红娘课程兑换成功,makerId=" + makerId + ", courseId=" + courseId + ", points=" + points);
+            return true;
+        } catch (Exception e) {
+            System.err.println("记录兑换信息失败: " + e.getMessage());
+            e.printStackTrace();
+            throw new RuntimeException("记录兑换信息失败", e);
+        }
+    }
+    
+    @Override
+    public List<Map<String, Object>> getPurchasedCourses(Integer makerId) {
+        if (makerId == null) {
+            return new ArrayList<>();
+        }
+        
+        try {
+            List<Map<String, Object>> result = matchmakerCourseMapper.selectPurchasedCourses(makerId);
+            return result != null ? result : new ArrayList<>();
+        } catch (Exception e) {
+            System.err.println("查询已兑换课程失败: " + e.getMessage());
+            return new ArrayList<>();
+        }
+    }
+    
+    @Override
+    public boolean hasExchanged(Integer makerId, Integer courseId) {
+        if (makerId == null || courseId == null) {
+            return false;
+        }
+        return matchmakerCourseMapper.checkExchanged(makerId, courseId) > 0;
+    }
+    
+    @Override
+    public List<String> getAllCategories() {
+        LambdaQueryWrapper<MatchmakerCourse> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(MatchmakerCourse::getStatus, 1)
+               .select(MatchmakerCourse::getCategoryName)
+               .groupBy(MatchmakerCourse::getCategoryName);
+        List<MatchmakerCourse> courses = list(wrapper);
+        return courses.stream()
+                .map(MatchmakerCourse::getCategoryName)
+                .filter(name -> name != null && !name.isEmpty())
+                .distinct()
+                .collect(Collectors.toList());
+    }
+}