diff --git a/gym-manage-api/docs/groupcourse-recommend-api.md b/gym-manage-api/docs/groupcourse-recommend-api.md new file mode 100644 index 0000000..ffcb613 --- /dev/null +++ b/gym-manage-api/docs/groupcourse-recommend-api.md @@ -0,0 +1,530 @@ +# 团课推荐模块 API 文档 + +> **文档版本**: v1.0 +> **创建日期**: 2026-06-15 +> **作者**: 张翔 +> **状态**: 正式发布 + +--- + +## 📋 目录 + +1. [概述](#概述) +2. [基础路径](#基础路径) +3. [团课推荐管理接口](#团课推荐管理接口) + - [获取所有团课推荐](#获取所有团课推荐) + - [获取所有启用的团课推荐](#获取所有启用的团课推荐) + - [根据ID获取团课推荐](#根据ID获取团课推荐) + - [根据团课ID获取推荐](#根据团课ID获取推荐) + - [创建团课推荐](#创建团课推荐) + - [更新团课推荐](#更新团课推荐) + - [删除团课推荐](#删除团课推荐) + - [启用团课推荐](#启用团课推荐) + - [禁用团课推荐](#禁用团课推荐) +4. [数据模型](#数据模型) + - [GroupCourseRecommend(团课推荐)](#GroupCourseRecommend团课推荐) +5. [状态码说明](#状态码说明) +6. [业务规则](#业务规则) + +--- + +## 概述 + +团课推荐模块提供团课推荐信息的创建、编辑、查询、删除和状态管理功能。推荐信息包含团课ID、推荐标题、推荐内容、推荐理由、优先级等必要信息,支持按优先级排序展示。 + +## 基础路径 + +所有接口的基础路径为: `http://{host}:{port}/api/groupCourse/recommend` + +--- + +## 团课推荐管理接口 + +### 获取所有团课推荐 + +| 属性 | 值 | +|------|-----| +| **HTTP方法** | GET | +| **接口路径** | `/api/groupCourse/recommend/list` | +| **所属文件** | `GroupCourseRecommendHandler.java` | + +**请求参数**: + +| 参数名 | 类型 | 必填 | 默认值 | 说明 | +|--------|------|------|--------|------| +| sortBy | string | 否 | priority | 排序字段(支持:priority、createdAt、updatedAt) | +| sortOrder | string | 否 | desc | 排序方式(asc-升序,desc-降序) | + +**成功响应** (200 OK): + +```json +[ + { + "id": 1, + "courseId": 1, + "recommendTitle": "本周热门课程", + "recommendContent": "这是一门非常棒的课程,快来参加吧!", + "recommendReason": "教练专业,课程内容丰富", + "priority": 10, + "isActive": true, + "groupCourse": { + "id": 1, + "courseName": "瑜伽入门", + "coachId": 1, + "courseType": 1, + "startTime": "2026-06-15T09:00:00", + "endTime": "2026-06-15T10:00:00", + "maxMembers": 20, + "currentMembers": 15, + "status": 0, + "location": "健身房A区", + "coverImage": "https://example.com/yoga.jpg", + "description": "适合初学者的瑜伽课程" + }, + "createdAt": "2026-06-15T10:00:00", + "updatedAt": "2026-06-15T10:00:00" + } +] +``` + +--- + +### 获取所有启用的团课推荐 + +| 属性 | 值 | +|------|-----| +| **HTTP方法** | GET | +| **接口路径** | `/api/groupCourse/recommend/active` | +| **所属文件** | `GroupCourseRecommendHandler.java` | + +**功能说明**: 获取系统中所有已启用的团课推荐列表,按优先级从高到低排序。 + +**成功响应** (200 OK): + +```json +[ + { + "id": 2, + "courseId": 3, + "recommendTitle": "新学员推荐", + "recommendContent": "专为新学员设计的入门课程,轻松上手", + "recommendReason": "零基础友好,教练耐心指导", + "priority": 20, + "isActive": true, + "groupCourse": { + "id": 3, + "courseName": "基础有氧", + "coachId": 2, + "courseType": 2, + "startTime": "2026-06-16T18:00:00", + "endTime": "2026-06-16T19:00:00", + "maxMembers": 30, + "currentMembers": 8, + "status": 0, + "location": "健身房B区", + "coverImage": "https://example.com/aerobic.jpg", + "description": "适合所有健身水平的有氧课程" + }, + "createdAt": "2026-06-15T11:00:00", + "updatedAt": "2026-06-15T11:00:00" + } +] +``` + +--- + +### 根据ID获取团课推荐 + +| 属性 | 值 | +|------|-----| +| **HTTP方法** | GET | +| **接口路径** | `/api/groupCourse/recommend/{id}` | +| **所属文件** | `GroupCourseRecommendHandler.java` | + +**路径参数**: + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| id | Long | 是 | 团课推荐ID | + +**成功响应** (200 OK): + +```json +{ + "id": 1, + "courseId": 1, + "recommendTitle": "本周热门课程", + "recommendContent": "这是一门非常棒的课程,快来参加吧!", + "recommendReason": "教练专业,课程内容丰富", + "priority": 10, + "isActive": true, + "groupCourse": { + "id": 1, + "courseName": "瑜伽入门", + "coachId": 1, + "courseType": 1, + "startTime": "2026-06-15T09:00:00", + "endTime": "2026-06-15T10:00:00", + "maxMembers": 20, + "currentMembers": 15, + "status": 0, + "location": "健身房A区", + "coverImage": "https://example.com/yoga.jpg", + "description": "适合初学者的瑜伽课程" + }, + "createdAt": "2026-06-15T10:00:00", + "updatedAt": "2026-06-15T10:00:00" +} +``` + +**失败响应** (404 Not Found): + +```json +{} +``` + +--- + +### 根据团课ID获取推荐 + +| 属性 | 值 | +|------|-----| +| **HTTP方法** | GET | +| **接口路径** | `/api/groupCourse/recommend/course/{courseId}` | +| **所属文件** | `GroupCourseRecommendHandler.java` | + +**路径参数**: + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| courseId | Long | 是 | 团课ID | + +**成功响应** (200 OK): + +```json +[ + { + "id": 1, + "courseId": 1, + "recommendTitle": "本周热门课程", + "recommendContent": "这是一门非常棒的课程,快来参加吧!", + "recommendReason": "教练专业,课程内容丰富", + "priority": 10, + "isActive": true, + "groupCourse": { + "id": 1, + "courseName": "瑜伽入门", + "coachId": 1, + "courseType": 1, + "startTime": "2026-06-15T09:00:00", + "endTime": "2026-06-15T10:00:00", + "maxMembers": 20, + "currentMembers": 15, + "status": 0, + "location": "健身房A区" + }, + "createdAt": "2026-06-15T10:00:00", + "updatedAt": "2026-06-15T10:00:00" + } +] +``` + +--- + +### 创建团课推荐 + +| 属性 | 值 | +|------|-----| +| **HTTP方法** | POST | +| **接口路径** | `/api/groupCourse/recommend` | +| **所属文件** | `GroupCourseRecommendHandler.java` | + +**请求体**: + +```json +{ + "courseId": 1, + "recommendTitle": "本周热门课程", + "recommendContent": "这是一门非常棒的课程,快来参加吧!", + "recommendReason": "教练专业,课程内容丰富", + "priority": 10, + "isActive": true +} +``` + +| 参数名 | 类型 | 必填 | 默认值 | 说明 | +|--------|------|------|--------|------| +| courseId | Long | **是** | - | 团课ID(必须是有效的团课) | +| recommendTitle | String | 否 | - | 推荐标题 | +| recommendContent | String | 否 | - | 推荐内容 | +| recommendReason | String | 否 | - | 推荐理由 | +| priority | Integer | 否 | 0 | 优先级(数字越大优先级越高) | +| isActive | Boolean | 否 | true | 是否启用 | + +**成功响应** (200 OK): + +```json +{ + "success": true, + "message": "团课推荐创建成功", + "data": { + "id": 1, + "courseId": 1, + "recommendTitle": "本周热门课程", + "recommendContent": "这是一门非常棒的课程,快来参加吧!", + "recommendReason": "教练专业,课程内容丰富", + "priority": 10, + "isActive": true, + "createdAt": "2026-06-15T10:00:00", + "updatedAt": "2026-06-15T10:00:00" + } +} +``` + +**失败响应** (400 Bad Request): + +```json +{ + "success": false, + "message": "团课ID不能为空" +} +``` + +--- + +### 更新团课推荐 + +| 属性 | 值 | +|------|-----| +| **HTTP方法** | PUT | +| **接口路径** | `/api/groupCourse/recommend/{id}` | +| **所属文件** | `GroupCourseRecommendHandler.java` | + +**路径参数**: + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| id | Long | 是 | 团课推荐ID | + +**请求体**: + +```json +{ + "recommendTitle": "本周热门课程(更新)", + "recommendContent": "更新后的推荐内容", + "recommendReason": "更新后的推荐理由", + "priority": 15, + "isActive": true +} +``` + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| courseId | Long | 否 | 团课ID | +| recommendTitle | String | 否 | 推荐标题 | +| recommendContent | String | 否 | 推荐内容 | +| recommendReason | String | 否 | 推荐理由 | +| priority | Integer | 否 | 优先级 | +| isActive | Boolean | 否 | 是否启用 | + +**成功响应** (200 OK): + +```json +{ + "success": true, + "message": "团课推荐更新成功", + "data": { + "id": 1, + "courseId": 1, + "recommendTitle": "本周热门课程(更新)", + "recommendContent": "更新后的推荐内容", + "recommendReason": "更新后的推荐理由", + "priority": 15, + "isActive": true, + "updatedAt": "2026-06-15T12:00:00" + } +} +``` + +**失败响应** (400 Bad Request): + +```json +{ + "success": false, + "message": "团课推荐不存在" +} +``` + +--- + +### 删除团课推荐 + +| 属性 | 值 | +|------|-----| +| **HTTP方法** | DELETE | +| **接口路径** | `/api/groupCourse/recommend/{id}` | +| **所属文件** | `GroupCourseRecommendHandler.java` | + +**路径参数**: + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| id | Long | 是 | 团课推荐ID | + +**成功响应** (200 OK): + +```json +{ + "success": true, + "message": "团课推荐删除成功" +} +``` + +**失败响应** (400 Bad Request): + +```json +{ + "success": false, + "message": "团课推荐不存在" +} +``` + +--- + +### 启用团课推荐 + +| 属性 | 值 | +|------|-----| +| **HTTP方法** | POST | +| **接口路径** | `/api/groupCourse/recommend/{id}/enable` | +| **所属文件** | `GroupCourseRecommendHandler.java` | + +**路径参数**: + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| id | Long | 是 | 团课推荐ID | + +**成功响应** (200 OK): + +```json +{ + "success": true, + "message": "团课推荐启用成功", + "data": { + "id": 1, + "courseId": 1, + "recommendTitle": "本周热门课程", + "isActive": true + } +} +``` + +**失败响应** (400 Bad Request): + +```json +{ + "success": false, + "message": "团课推荐不存在" +} +``` + +--- + +### 禁用团课推荐 + +| 属性 | 值 | +|------|-----| +| **HTTP方法** | POST | +| **接口路径** | `/api/groupCourse/recommend/{id}/disable` | +| **所属文件** | `GroupCourseRecommendHandler.java` | + +**路径参数**: + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| id | Long | 是 | 团课推荐ID | + +**成功响应** (200 OK): + +```json +{ + "success": true, + "message": "团课推荐禁用成功", + "data": { + "id": 1, + "courseId": 1, + "recommendTitle": "本周热门课程", + "isActive": false + } +} +``` + +**失败响应** (400 Bad Request): + +```json +{ + "success": false, + "message": "团课推荐不存在" +} +``` + +--- + +## 数据模型 + +### GroupCourseRecommend(团课推荐) + +| 字段名 | 类型 | 说明 | +|--------|------|------| +| id | Long | 主键ID | +| courseId | Long | 团课ID(关联group_course.id) | +| recommendTitle | String | 推荐标题 | +| recommendContent | String | 推荐内容 | +| recommendReason | String | 推荐理由 | +| priority | Integer | 优先级(数字越大优先级越高),默认0 | +| isActive | Boolean | 是否启用,默认true | +| groupCourse | GroupCourse | 关联的团课信息(查询时自动填充) | +| createdBy | String | 创建人 | +| updatedBy | String | 更新人 | +| createdAt | LocalDateTime | 创建时间 | +| updatedAt | LocalDateTime | 更新时间 | +| deletedAt | LocalDateTime | 删除时间(软删除) | + +--- + +## 状态码说明 + +### 推荐状态 + +| 状态值 | 含义 | +|--------|------| +| true | 启用 | +| false | 禁用 | + +--- + +## 业务规则 + +### 团课推荐管理 +1. **创建推荐**:团课ID为必填项,且必须是有效的团课 +2. **优先级排序**:获取启用的推荐列表时,按优先级从高到低排序 +3. **删除推荐**:采用软删除机制,数据保留可恢复 +4. **状态管理**:支持启用/禁用推荐状态,禁用的推荐不会在推荐列表中显示 + +--- + +## 附录:错误响应格式 + +所有接口的错误响应统一格式: + +```json +{ + "success": false, + "message": "错误描述信息" +} +``` + +--- + +*文档结束* \ No newline at end of file diff --git a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/dao/GroupCourseRecommendDao.java b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/dao/GroupCourseRecommendDao.java new file mode 100644 index 0000000..138a6ff --- /dev/null +++ b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/dao/GroupCourseRecommendDao.java @@ -0,0 +1,42 @@ +package cn.novalon.gym.manage.groupcourse.dao; + +import cn.novalon.gym.manage.groupcourse.entity.GroupCourseRecommendEntity; +import org.springframework.data.domain.Sort; +import org.springframework.data.r2dbc.repository.Modifying; +import org.springframework.data.r2dbc.repository.Query; +import org.springframework.data.r2dbc.repository.R2dbcRepository; +import org.springframework.stereotype.Repository; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.time.LocalDateTime; + +@Repository +public interface GroupCourseRecommendDao extends R2dbcRepository { + + Mono findByIdAndDeletedAtIsNull(Long id); + + Flux findAllByDeletedAtIsNull(); + + Flux findAllByDeletedAtIsNull(Sort sort); + + Flux findByCourseIdAndDeletedAtIsNull(Long courseId); + + Mono findByCourseIdAndDeletedAtIsNullAndIsActiveTrue(Long courseId); + + Flux findByIsActiveTrueAndDeletedAtIsNull(); + + Flux findByIsActiveTrueAndDeletedAtIsNull(Sort sort); + + @Modifying + @Query("UPDATE group_course_recommend SET is_active = :isActive, updated_at = :updatedAt WHERE id = :id AND deleted_at IS NULL") + Mono updateActiveStatus(Long id, Boolean isActive, LocalDateTime updatedAt); + + @Modifying + @Query("UPDATE group_course_recommend SET deleted_at = :deletedAt WHERE id = :id") + Mono softDelete(Long id, LocalDateTime deletedAt); + + @Modifying + @Query("UPDATE group_course_recommend SET deleted_at = :deletedAt WHERE course_id = :courseId") + Mono softDeleteByCourseId(Long courseId, LocalDateTime deletedAt); +} \ No newline at end of file diff --git a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/domain/GroupCourseRecommend.java b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/domain/GroupCourseRecommend.java new file mode 100644 index 0000000..624f7a1 --- /dev/null +++ b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/domain/GroupCourseRecommend.java @@ -0,0 +1,84 @@ +package cn.novalon.gym.manage.groupcourse.domain; + +import cn.novalon.gym.manage.sys.core.domain.BaseDomain; +import io.swagger.v3.oas.annotations.media.Schema; + +public class GroupCourseRecommend extends BaseDomain { + + @Schema(description = "团课ID", example = "1") + private Long courseId; + + @Schema(description = "推荐标题", example = "本周热门课程") + private String recommendTitle; + + @Schema(description = "推荐内容", example = "这是一门非常棒的课程,快来参加吧!") + private String recommendContent; + + @Schema(description = "推荐理由", example = "教练专业,课程内容丰富") + private String recommendReason; + + @Schema(description = "优先级(数字越大优先级越高)", example = "10") + private Integer priority; + + @Schema(description = "是否启用", example = "true") + private Boolean isActive; + + @Schema(description = "团课信息") + private GroupCourse groupCourse; + + public Long getCourseId() { + return courseId; + } + + public void setCourseId(Long courseId) { + this.courseId = courseId; + } + + public String getRecommendTitle() { + return recommendTitle; + } + + public void setRecommendTitle(String recommendTitle) { + this.recommendTitle = recommendTitle; + } + + public String getRecommendContent() { + return recommendContent; + } + + public void setRecommendContent(String recommendContent) { + this.recommendContent = recommendContent; + } + + public String getRecommendReason() { + return recommendReason; + } + + public void setRecommendReason(String recommendReason) { + this.recommendReason = recommendReason; + } + + public Integer getPriority() { + return priority; + } + + public void setPriority(Integer priority) { + this.priority = priority; + } + + public Boolean getIsActive() { + return isActive; + } + + public void setIsActive(Boolean isActive) { + this.isActive = isActive; + } + + public GroupCourse getGroupCourse() { + return groupCourse; + } + + public void setGroupCourse(GroupCourse groupCourse) { + this.groupCourse = groupCourse; + } +} \ No newline at end of file diff --git a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/entity/GroupCourseRecommendEntity.java b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/entity/GroupCourseRecommendEntity.java new file mode 100644 index 0000000..3d9ac83 --- /dev/null +++ b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/entity/GroupCourseRecommendEntity.java @@ -0,0 +1,77 @@ +package cn.novalon.gym.manage.groupcourse.entity; + +import cn.novalon.gym.manage.db.entity.BaseEntity; +import org.springframework.data.relational.core.mapping.Column; +import org.springframework.data.relational.core.mapping.Table; + +import java.time.LocalDateTime; + +@Table("group_course_recommend") +public class GroupCourseRecommendEntity extends BaseEntity { + + @Column("course_id") + private Long courseId; + + @Column("recommend_title") + private String recommendTitle; + + @Column("recommend_content") + private String recommendContent; + + @Column("recommend_reason") + private String recommendReason; + + @Column("priority") + private Integer priority; + + @Column("is_active") + private Boolean isActive; + + public Long getCourseId() { + return courseId; + } + + public void setCourseId(Long courseId) { + this.courseId = courseId; + } + + public String getRecommendTitle() { + return recommendTitle; + } + + public void setRecommendTitle(String recommendTitle) { + this.recommendTitle = recommendTitle; + } + + public String getRecommendContent() { + return recommendContent; + } + + public void setRecommendContent(String recommendContent) { + this.recommendContent = recommendContent; + } + + public String getRecommendReason() { + return recommendReason; + } + + public void setRecommendReason(String recommendReason) { + this.recommendReason = recommendReason; + } + + public Integer getPriority() { + return priority; + } + + public void setPriority(Integer priority) { + this.priority = priority; + } + + public Boolean getIsActive() { + return isActive; + } + + public void setIsActive(Boolean isActive) { + this.isActive = isActive; + } +} \ No newline at end of file diff --git a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/handler/GroupCourseRecommendHandler.java b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/handler/GroupCourseRecommendHandler.java new file mode 100644 index 0000000..85b8f66 --- /dev/null +++ b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/handler/GroupCourseRecommendHandler.java @@ -0,0 +1,164 @@ +package cn.novalon.gym.manage.groupcourse.handler; + +import cn.novalon.gym.manage.groupcourse.domain.GroupCourseRecommend; +import cn.novalon.gym.manage.groupcourse.service.IGroupCourseRecommendService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; + +import java.util.HashMap; +import java.util.Map; + +@Component +@Tag(name = "团课推荐管理", description = "团课推荐相关操作") +public class GroupCourseRecommendHandler { + + private final IGroupCourseRecommendService recommendService; + + public GroupCourseRecommendHandler(IGroupCourseRecommendService recommendService) { + this.recommendService = recommendService; + } + + @Operation(summary = "获取所有团课推荐", description = "获取系统中所有团课推荐列表,支持按优先级排序") + public Mono getAllRecommendations(ServerRequest request) { + String sortBy = request.queryParam("sortBy").orElse("priority"); + String sortOrder = request.queryParam("sortOrder").orElse("desc"); + + return ServerResponse.ok() + .body(recommendService.findAll(sortBy, sortOrder), GroupCourseRecommend.class); + } + + @Operation(summary = "获取所有启用的团课推荐", description = "获取系统中所有已启用的团课推荐列表(按优先级排序)") + public Mono getAllActiveRecommendations(ServerRequest request) { + return ServerResponse.ok() + .body(recommendService.findAllActive(), GroupCourseRecommend.class); + } + + @Operation(summary = "根据ID获取团课推荐", description = "根据ID获取团课推荐详情") + public Mono getRecommendationById(ServerRequest request) { + Long id = Long.valueOf(request.pathVariable("id")); + return recommendService.findById(id) + .flatMap(recommend -> ServerResponse.ok().bodyValue(recommend)) + .switchIfEmpty(ServerResponse.notFound().build()); + } + + @Operation(summary = "根据团课ID获取推荐", description = "根据团课ID获取该团课的推荐信息") + public Mono getRecommendationsByCourseId(ServerRequest request) { + Long courseId = Long.valueOf(request.pathVariable("courseId")); + return ServerResponse.ok() + .body(recommendService.findByCourseId(courseId), GroupCourseRecommend.class); + } + + @Operation(summary = "创建团课推荐", description = "创建新的团课推荐") + public Mono createRecommendation(ServerRequest request) { + return request.bodyToMono(GroupCourseRecommend.class) + .flatMap(recommend -> { + if (recommend.getCourseId() == null) { + Map error = new HashMap<>(); + error.put("success", false); + error.put("message", "团课ID不能为空"); + return ServerResponse.badRequest().bodyValue(error); + } + + return recommendService.create(recommend) + .flatMap(r -> { + Map response = new HashMap<>(); + response.put("success", true); + response.put("message", "团课推荐创建成功"); + response.put("data", r); + return ServerResponse.ok().bodyValue(response); + }) + .onErrorResume(error -> { + Map response = new HashMap<>(); + response.put("success", false); + response.put("message", error.getMessage()); + return ServerResponse.badRequest().bodyValue(response); + }); + }); + } + + @Operation(summary = "更新团课推荐", description = "更新指定团课推荐信息") + public Mono updateRecommendation(ServerRequest request) { + Long id = Long.valueOf(request.pathVariable("id")); + + return request.bodyToMono(GroupCourseRecommend.class) + .flatMap(recommend -> { + return recommendService.update(id, recommend) + .flatMap(r -> { + Map response = new HashMap<>(); + response.put("success", true); + response.put("message", "团课推荐更新成功"); + response.put("data", r); + return ServerResponse.ok().bodyValue(response); + }) + .onErrorResume(error -> { + Map response = new HashMap<>(); + response.put("success", false); + response.put("message", error.getMessage()); + return ServerResponse.badRequest().bodyValue(response); + }); + }); + } + + @Operation(summary = "删除团课推荐", description = "删除指定团课推荐(软删除)") + public Mono deleteRecommendation(ServerRequest request) { + Long id = Long.valueOf(request.pathVariable("id")); + + return recommendService.delete(id) + .then(Mono.defer(() -> { + Map response = new HashMap<>(); + response.put("success", true); + response.put("message", "团课推荐删除成功"); + return ServerResponse.ok().bodyValue(response); + })) + .onErrorResume(error -> { + Map response = new HashMap<>(); + response.put("success", false); + response.put("message", error.getMessage()); + return ServerResponse.badRequest().bodyValue(response); + }); + } + + @Operation(summary = "启用团课推荐", description = "启用指定团课推荐") + public Mono enableRecommendation(ServerRequest request) { + Long id = Long.valueOf(request.pathVariable("id")); + + return recommendService.enable(id) + .flatMap(r -> { + Map response = new HashMap<>(); + response.put("success", true); + response.put("message", "团课推荐启用成功"); + response.put("data", r); + return ServerResponse.ok().bodyValue(response); + }) + .onErrorResume(error -> { + Map response = new HashMap<>(); + response.put("success", false); + response.put("message", error.getMessage()); + return ServerResponse.badRequest().bodyValue(response); + }); + } + + @Operation(summary = "禁用团课推荐", description = "禁用指定团课推荐") + public Mono disableRecommendation(ServerRequest request) { + Long id = Long.valueOf(request.pathVariable("id")); + + return recommendService.disable(id) + .flatMap(r -> { + Map response = new HashMap<>(); + response.put("success", true); + response.put("message", "团课推荐禁用成功"); + response.put("data", r); + return ServerResponse.ok().bodyValue(response); + }) + .onErrorResume(error -> { + Map response = new HashMap<>(); + response.put("success", false); + response.put("message", error.getMessage()); + return ServerResponse.badRequest().bodyValue(response); + }); + } +} \ No newline at end of file diff --git a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/repository/IGroupCourseRecommendRepository.java b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/repository/IGroupCourseRecommendRepository.java new file mode 100644 index 0000000..d8495e7 --- /dev/null +++ b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/repository/IGroupCourseRecommendRepository.java @@ -0,0 +1,30 @@ +package cn.novalon.gym.manage.groupcourse.repository; + +import cn.novalon.gym.manage.groupcourse.domain.GroupCourseRecommend; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public interface IGroupCourseRecommendRepository { + + Mono findById(Long id); + + Flux findAll(); + + Flux findAll(String sortBy, String sortOrder); + + Flux findAllActive(); + + Flux findByCourseId(Long courseId); + + Mono findActiveByCourseId(Long courseId); + + Mono save(GroupCourseRecommend recommend); + + Mono update(GroupCourseRecommend recommend); + + Mono deleteById(Long id); + + Mono deleteByCourseId(Long courseId); + + Mono updateActiveStatus(Long id, Boolean isActive); +} \ No newline at end of file diff --git a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/repository/impl/GroupCourseRecommendRepository.java b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/repository/impl/GroupCourseRecommendRepository.java new file mode 100644 index 0000000..3056279 --- /dev/null +++ b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/repository/impl/GroupCourseRecommendRepository.java @@ -0,0 +1,136 @@ +package cn.novalon.gym.manage.groupcourse.repository.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.novalon.gym.manage.groupcourse.dao.GroupCourseRecommendDao; +import cn.novalon.gym.manage.groupcourse.domain.GroupCourseRecommend; +import cn.novalon.gym.manage.groupcourse.entity.GroupCourseRecommendEntity; +import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseRecommendRepository; +import org.springframework.data.domain.Sort; +import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; +import org.springframework.stereotype.Repository; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.time.LocalDateTime; + +@Repository +public class GroupCourseRecommendRepository implements IGroupCourseRecommendRepository { + + private final GroupCourseRecommendDao recommendDao; + private final R2dbcEntityTemplate r2dbcEntityTemplate; + + public GroupCourseRecommendRepository(GroupCourseRecommendDao recommendDao, + R2dbcEntityTemplate r2dbcEntityTemplate) { + this.recommendDao = recommendDao; + this.r2dbcEntityTemplate = r2dbcEntityTemplate; + } + + @Override + public Mono findById(Long id) { + return recommendDao.findByIdAndDeletedAtIsNull(id) + .map(this::toDomain); + } + + @Override + public Flux findAll() { + return recommendDao.findAllByDeletedAtIsNull() + .map(this::toDomain); + } + + @Override + public Flux findAll(String sortBy, String sortOrder) { + Sort.Direction direction = "asc".equalsIgnoreCase(sortOrder) + ? Sort.Direction.ASC + : Sort.Direction.DESC; + Sort sort = Sort.by(direction, sortBy); + return recommendDao.findAllByDeletedAtIsNull(sort) + .map(this::toDomain); + } + + @Override + public Flux findAllActive() { + return recommendDao.findByIsActiveTrueAndDeletedAtIsNull(Sort.by(Sort.Direction.DESC, "priority")) + .map(this::toDomain); + } + + @Override + public Flux findByCourseId(Long courseId) { + return recommendDao.findByCourseIdAndDeletedAtIsNull(courseId) + .map(this::toDomain); + } + + @Override + public Mono findActiveByCourseId(Long courseId) { + return recommendDao.findByCourseIdAndDeletedAtIsNullAndIsActiveTrue(courseId) + .map(this::toDomain); + } + + @Override + public Mono save(GroupCourseRecommend recommend) { + GroupCourseRecommendEntity entity = toEntity(recommend); + entity.setCreatedAt(LocalDateTime.now()); + entity.setUpdatedAt(LocalDateTime.now()); + if (entity.getPriority() == null) { + entity.setPriority(0); + } + if (entity.getIsActive() == null) { + entity.setIsActive(true); + } + + return recommendDao.save(entity) + .map(this::toDomain); + } + + @Override + public Mono update(GroupCourseRecommend recommend) { + GroupCourseRecommendEntity entity = toEntity(recommend); + entity.setUpdatedAt(LocalDateTime.now()); + + return r2dbcEntityTemplate.update(entity) + .then(findById(recommend.getId())); + } + + @Override + public Mono deleteById(Long id) { + return recommendDao.softDelete(id, LocalDateTime.now()) + .then(); + } + + @Override + public Mono deleteByCourseId(Long courseId) { + return recommendDao.softDeleteByCourseId(courseId, LocalDateTime.now()) + .then(); + } + + @Override + public Mono updateActiveStatus(Long id, Boolean isActive) { + return recommendDao.updateActiveStatus(id, isActive, LocalDateTime.now()) + .flatMap(updated -> { + if (updated > 0) { + return findById(id); + } + return Mono.empty(); + }); + } + + private GroupCourseRecommend toDomain(GroupCourseRecommendEntity entity) { + if (entity == null) { + return null; + } + GroupCourseRecommend recommend = new GroupCourseRecommend(); + BeanUtil.copyProperties(entity, recommend); + return recommend; + } + + private GroupCourseRecommendEntity toEntity(GroupCourseRecommend domain) { + if (domain == null) { + return null; + } + GroupCourseRecommendEntity entity = new GroupCourseRecommendEntity(); + BeanUtil.copyProperties(domain, entity); + if (domain.getId() != null) { + entity.markNotNew(); + } + return entity; + } +} \ No newline at end of file diff --git a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/IGroupCourseRecommendService.java b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/IGroupCourseRecommendService.java new file mode 100644 index 0000000..dddfa4c --- /dev/null +++ b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/IGroupCourseRecommendService.java @@ -0,0 +1,28 @@ +package cn.novalon.gym.manage.groupcourse.service; + +import cn.novalon.gym.manage.groupcourse.domain.GroupCourseRecommend; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public interface IGroupCourseRecommendService { + + Mono findById(Long id); + + Flux findAll(); + + Flux findAll(String sortBy, String sortOrder); + + Flux findAllActive(); + + Flux findByCourseId(Long courseId); + + Mono create(GroupCourseRecommend recommend); + + Mono update(Long id, GroupCourseRecommend recommend); + + Mono delete(Long id); + + Mono enable(Long id); + + Mono disable(Long id); +} \ No newline at end of file diff --git a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseRecommendService.java b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseRecommendService.java new file mode 100644 index 0000000..6f8c8db --- /dev/null +++ b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseRecommendService.java @@ -0,0 +1,142 @@ +package cn.novalon.gym.manage.groupcourse.service.impl; + +import cn.novalon.gym.manage.groupcourse.domain.GroupCourse; +import cn.novalon.gym.manage.groupcourse.domain.GroupCourseRecommend; +import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseRecommendRepository; +import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseRepository; +import cn.novalon.gym.manage.groupcourse.service.IGroupCourseRecommendService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@Service +public class GroupCourseRecommendService implements IGroupCourseRecommendService { + + private static final Logger logger = LoggerFactory.getLogger(GroupCourseRecommendService.class); + + private final IGroupCourseRecommendRepository recommendRepository; + private final IGroupCourseRepository groupCourseRepository; + + public GroupCourseRecommendService(IGroupCourseRecommendRepository recommendRepository, + IGroupCourseRepository groupCourseRepository) { + this.recommendRepository = recommendRepository; + this.groupCourseRepository = groupCourseRepository; + } + + @Override + public Mono findById(Long id) { + return recommendRepository.findById(id) + .flatMap(this::fillGroupCourseInfo); + } + + @Override + public Flux findAll() { + return recommendRepository.findAll() + .flatMap(this::fillGroupCourseInfo); + } + + @Override + public Flux findAll(String sortBy, String sortOrder) { + return recommendRepository.findAll(sortBy, sortOrder) + .flatMap(this::fillGroupCourseInfo); + } + + @Override + public Flux findAllActive() { + return recommendRepository.findAllActive() + .flatMap(this::fillGroupCourseInfo); + } + + @Override + public Flux findByCourseId(Long courseId) { + return recommendRepository.findByCourseId(courseId) + .flatMap(this::fillGroupCourseInfo); + } + + @Override + public Mono create(GroupCourseRecommend recommend) { + if (recommend.getCourseId() == null) { + return Mono.error(new RuntimeException("团课ID不能为空")); + } + + return groupCourseRepository.findByIdAndDeletedAtIsNull(recommend.getCourseId()) + .switchIfEmpty(Mono.error(new RuntimeException("团课不存在"))) + .flatMap(course -> { + return recommendRepository.save(recommend) + .doOnSuccess(r -> logger.info("团课推荐创建成功 - id={}, courseId={}", r.getId(), r.getCourseId())) + .doOnError(error -> logger.error("团课推荐创建失败 - error: {}", error.getMessage())); + }); + } + + @Override + public Mono update(Long id, GroupCourseRecommend recommend) { + return recommendRepository.findById(id) + .switchIfEmpty(Mono.error(new RuntimeException("团课推荐不存在"))) + .flatMap(existing -> { + if (recommend.getRecommendTitle() != null) { + existing.setRecommendTitle(recommend.getRecommendTitle()); + } + if (recommend.getRecommendContent() != null) { + existing.setRecommendContent(recommend.getRecommendContent()); + } + if (recommend.getRecommendReason() != null) { + existing.setRecommendReason(recommend.getRecommendReason()); + } + if (recommend.getPriority() != null) { + existing.setPriority(recommend.getPriority()); + } + if (recommend.getIsActive() != null) { + existing.setIsActive(recommend.getIsActive()); + } + if (recommend.getCourseId() != null) { + existing.setCourseId(recommend.getCourseId()); + } + + return recommendRepository.update(existing); + }) + .doOnSuccess(r -> logger.info("团课推荐更新成功 - id={}", id)) + .doOnError(error -> logger.error("团课推荐更新失败 - id={}, error: {}", id, error.getMessage())); + } + + @Override + public Mono delete(Long id) { + return recommendRepository.findById(id) + .switchIfEmpty(Mono.error(new RuntimeException("团课推荐不存在"))) + .flatMap(recommend -> { + return recommendRepository.deleteById(id) + .doOnSuccess(v -> logger.info("团课推荐删除成功 - id={}", id)) + .doOnError(error -> logger.error("团课推荐删除失败 - id={}, error: {}", id, error.getMessage())); + }); + } + + @Override + public Mono enable(Long id) { + return recommendRepository.updateActiveStatus(id, true) + .switchIfEmpty(Mono.error(new RuntimeException("团课推荐不存在"))) + .doOnSuccess(r -> logger.info("团课推荐启用成功 - id={}", id)) + .doOnError(error -> logger.error("团课推荐启用失败 - id={}, error: {}", id, error.getMessage())); + } + + @Override + public Mono disable(Long id) { + return recommendRepository.updateActiveStatus(id, false) + .switchIfEmpty(Mono.error(new RuntimeException("团课推荐不存在"))) + .doOnSuccess(r -> logger.info("团课推荐禁用成功 - id={}", id)) + .doOnError(error -> logger.error("团课推荐禁用失败 - id={}, error: {}", id, error.getMessage())); + } + + private Mono fillGroupCourseInfo(GroupCourseRecommend recommend) { + if (recommend.getCourseId() == null) { + return Mono.just(recommend); + } + + return groupCourseRepository.findByIdAndDeletedAtIsNull(recommend.getCourseId()) + .map(course -> { + recommend.setGroupCourse(course); + return recommend; + }) + .defaultIfEmpty(recommend); + } +} \ No newline at end of file diff --git a/gym-manage-api/manage-app/src/main/java/cn/novalon/gym/manage/app/config/SystemRouter.java b/gym-manage-api/manage-app/src/main/java/cn/novalon/gym/manage/app/config/SystemRouter.java index 5faa8ba..d8d05e8 100644 --- a/gym-manage-api/manage-app/src/main/java/cn/novalon/gym/manage/app/config/SystemRouter.java +++ b/gym-manage-api/manage-app/src/main/java/cn/novalon/gym/manage/app/config/SystemRouter.java @@ -6,6 +6,7 @@ import cn.novalon.gym.manage.datacount.handler.DataStatisticsHandler; import cn.novalon.gym.manage.file.handler.SysFileHandler; import cn.novalon.gym.manage.groupcourse.handler.GroupCourseBookingHandler; import cn.novalon.gym.manage.groupcourse.handler.GroupCourseHandler; +import cn.novalon.gym.manage.groupcourse.handler.GroupCourseRecommendHandler; import cn.novalon.gym.manage.groupcourse.handler.GroupCourseTypeHandler; import cn.novalon.gym.manage.groupcourse.handler.CourseLabelHandler; import cn.novalon.gym.manage.member.handler.MemberCardHandler; @@ -71,6 +72,7 @@ public class SystemRouter { MemberCardTransactionHandler memberCardTransactionHandler, GroupCourseHandler groupCourseHandler, GroupCourseBookingHandler groupCourseBookingHandler, + GroupCourseRecommendHandler groupCourseRecommendHandler, GroupCourseTypeHandler groupCourseTypeHandler, CourseLabelHandler courseLabelHandler, CheckInHandler checkInHandler, @@ -299,6 +301,17 @@ public class SystemRouter { .GET("/api/groupCourse/bookings/course/{courseId}", groupCourseBookingHandler::getBookingsByCourseId) .GET("/api/groupCourse/bookings/{bookingId}", groupCourseBookingHandler::getBookingById) + // ===== 团课推荐管理 ===== + .GET("/api/groupCourse/recommend/list", groupCourseRecommendHandler::getAllRecommendations) + .GET("/api/groupCourse/recommend/active", groupCourseRecommendHandler::getAllActiveRecommendations) + .GET("/api/groupCourse/recommend/{id}", groupCourseRecommendHandler::getRecommendationById) + .GET("/api/groupCourse/recommend/course/{courseId}", groupCourseRecommendHandler::getRecommendationsByCourseId) + .POST("/api/groupCourse/recommend", groupCourseRecommendHandler::createRecommendation) + .PUT("/api/groupCourse/recommend/{id}", groupCourseRecommendHandler::updateRecommendation) + .DELETE("/api/groupCourse/recommend/{id}", groupCourseRecommendHandler::deleteRecommendation) + .POST("/api/groupCourse/recommend/{id}/enable", groupCourseRecommendHandler::enableRecommendation) + .POST("/api/groupCourse/recommend/{id}/disable", groupCourseRecommendHandler::disableRecommendation) + // ===== 团课课程管理(需要放在具体路由之后)===== .GET("/api/groupCourse/{id}", groupCourseHandler::getGroupCourseById) .GET("/api/groupCourse/{id}/detail", groupCourseHandler::getGroupCourseDetailById) diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V17__Create_GroupCourse_Recommend_table.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V17__Create_GroupCourse_Recommend_table.sql new file mode 100644 index 0000000..7768864 --- /dev/null +++ b/gym-manage-api/manage-db/src/main/resources/db/migration/V17__Create_GroupCourse_Recommend_table.sql @@ -0,0 +1,38 @@ +-- ============================================ +-- 团课推荐表 +-- ============================================ + +-- 团课推荐表 +CREATE TABLE IF NOT EXISTS group_course_recommend ( + id BIGSERIAL PRIMARY KEY, + course_id BIGINT NOT NULL, + recommend_title VARCHAR(200), + recommend_content TEXT, + recommend_reason VARCHAR(500), + priority INTEGER DEFAULT 0, + is_active BOOLEAN DEFAULT TRUE, + create_by VARCHAR(50), + update_by VARCHAR(50), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP +); + +-- 创建索引 +CREATE INDEX IF NOT EXISTS idx_group_course_recommend_course_id ON group_course_recommend(course_id); +CREATE INDEX IF NOT EXISTS idx_group_course_recommend_priority ON group_course_recommend(priority); +CREATE INDEX IF NOT EXISTS idx_group_course_recommend_is_active ON group_course_recommend(is_active); + +COMMENT ON TABLE group_course_recommend IS '团课推荐表'; +COMMENT ON COLUMN group_course_recommend.id IS '主键ID'; +COMMENT ON COLUMN group_course_recommend.course_id IS '团课ID(关联group_course.id)'; +COMMENT ON COLUMN group_course_recommend.recommend_title IS '推荐标题'; +COMMENT ON COLUMN group_course_recommend.recommend_content IS '推荐内容'; +COMMENT ON COLUMN group_course_recommend.recommend_reason IS '推荐理由'; +COMMENT ON COLUMN group_course_recommend.priority IS '优先级(数字越大优先级越高)'; +COMMENT ON COLUMN group_course_recommend.is_active IS '是否启用'; +COMMENT ON COLUMN group_course_recommend.create_by IS '创建人'; +COMMENT ON COLUMN group_course_recommend.update_by IS '更新人'; +COMMENT ON COLUMN group_course_recommend.created_at IS '创建时间'; +COMMENT ON COLUMN group_course_recommend.updated_at IS '更新时间'; +COMMENT ON COLUMN group_course_recommend.deleted_at IS '删除时间(软删除)'; \ No newline at end of file diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V18__Insert_GroupCourse_Recommend_test_data.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V18__Insert_GroupCourse_Recommend_test_data.sql new file mode 100644 index 0000000..87f53c6 --- /dev/null +++ b/gym-manage-api/manage-db/src/main/resources/db/migration/V18__Insert_GroupCourse_Recommend_test_data.sql @@ -0,0 +1,43 @@ +-- ============================================ +-- 团课推荐测试数据 +-- ============================================ + +-- 推荐数据1: 极速燃脂单车 - 高优先级推荐(热门课程) +INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES + (1, '本周热门推荐', '极速燃脂单车课程,跟随音乐节奏变换阻力和速度,体验爬坡与冲刺的快感,一节课消耗800大卡!', '教练专业,课程内容丰富,深受学员喜爱,燃脂效果显著', 20, true, 'admin', '2026-06-15 10:00:00', '2026-06-15 10:00:00'); + +-- 推荐数据2: 清晨流瑜伽 - 中等优先级推荐(适合新手) +INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES + (2, '新手友好推荐', '清晨流瑜伽课程,适合有一定基础的学员,通过流畅的体式连接呼吸,唤醒身体能量。', '适合新手入门,教练耐心指导,课程节奏适中', 15, true, 'admin', '2026-06-15 11:00:00', '2026-06-15 11:00:00'); + +-- 推荐数据3: 燃脂搏击 - 低优先级推荐(满员课程,已禁用) +INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES + (3, '高强度燃脂', '燃脂搏击课程,高强度间歇训练,配合音乐快速燃脂,释放压力。', '高强度训练,适合进阶学员,快速燃脂塑形', 10, false, 'admin', '2026-06-15 12:00:00', '2026-06-15 12:00:00'); + +-- 推荐数据4: 哈他瑜伽 - 中等优先级推荐(基础课程) +INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES + (4, '基础瑜伽推荐', '基础哈他瑜伽课程,适合所有级别学员,通过基础体式练习提升身体柔韧性和平衡能力。', '零基础友好,适合所有健身水平,放松身心', 12, true, 'coach_li', '2026-06-15 13:00:00', '2026-06-15 13:00:00'); + +-- 推荐数据5: 蜜桃臀塑造 - 高优先级推荐(热门课程,但课程已结束) +INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES + (6, '塑形热门课程', '蜜桃臀塑造课程,针对性训练臀部肌肉群,打造完美曲线。', '专业私教指导,动作标准,效果显著,深受女性学员喜爱', 18, true, 'coach_li', '2026-05-25 09:15:00', '2026-05-25 09:15:00'); + +-- 推荐数据6: 午间冥想放松 - 低优先级推荐(放松课程) +INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES + (7, '午间放松推荐', '午间冥想放松课程,通过呼吸和正念冥想,深度放松身心,缓解工作压力。', '适合上班族,午间放松充电,提升下午工作效率', 8, true, 'admin', '2026-05-25 09:00:00', '2026-05-25 09:00:00'); + +-- 推荐数据7: 极速燃脂单车 - 第二个推荐(不同角度推荐) +INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES + (1, '减脂首选课程', '想要快速减脂?极速燃脂单车是你的最佳选择!专业教练带领,科学训练计划。', '减脂效果最佳,课程强度适中,适合想要快速瘦身的学员', 16, true, 'coach_zhang', '2026-06-15 14:00:00', '2026-06-15 14:00:00'); + +-- 推荐数据8: 清晨流瑜伽 - 第二个推荐(不同角度推荐) +INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES + (2, '晨练优选', '清晨流瑜伽,唤醒身体能量,开启活力一天!适合晨练爱好者。', '晨练最佳选择,提升身体活力,改善精神状态', 14, true, 'coach_wang', '2026-06-15 15:00:00', '2026-06-15 15:00:00'); + +-- 推荐数据9: 哈他瑜伽 - 第二个推荐(不同角度推荐) +INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES + (4, '身心平衡推荐', '哈他瑜伽课程,通过体式练习和呼吸控制,达到身心平衡,提升整体健康水平。', '改善身体柔韧性,增强核心力量,提升身体协调性', 11, true, 'coach_li', '2026-06-15 16:00:00', '2026-06-15 16:00:00'); + +-- 推荐数据10: 午间冥想放松 - 第二个推荐(不同角度推荐,已禁用) +INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES + (7, '职场减压课程', '午间冥想放松,专为职场人士设计,快速缓解工作压力,提升工作状态。', '职场减压首选,课程时间短,效果显著', 9, false, 'admin', '2026-05-25 10:00:00', '2026-05-25 10:00:00'); \ No newline at end of file