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 c869da3..98c3859 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
@@ -5,6 +5,7 @@ import cn.novalon.gym.manage.sys.handler.auth.PasswordDiagnosticHandler;
import cn.novalon.gym.manage.sys.handler.config.SysConfigHandler;
import cn.novalon.gym.manage.sys.handler.dictionary.DictionaryHandler;
import cn.novalon.gym.manage.sys.handler.dict.SysDictHandler;
+import cn.novalon.gym.manage.sys.handler.groupCourse.GroupCourseHandler;
import cn.novalon.gym.manage.sys.handler.log.SysLogHandler;
import cn.novalon.gym.manage.sys.handler.log.OperationLogHandler;
import cn.novalon.gym.manage.sys.handler.menu.MenuHandler;
@@ -51,7 +52,8 @@ public class SystemRouter {
SysUserMessageHandler messageHandler,
SysFileHandler fileHandler,
SysPermissionHandler permissionHandler,
- PasswordDiagnosticHandler passwordDiagnosticHandler) {
+ PasswordDiagnosticHandler passwordDiagnosticHandler,
+ GroupCourseHandler groupCourseHandler) {
return route()
// ========== 诊断路由 ==========
@@ -192,6 +194,9 @@ public class SystemRouter {
.POST("/api/permissions", permissionHandler::createPermission)
.PUT("/api/permissions/{id}", permissionHandler::updatePermission)
.DELETE("/api/permissions/{id}", permissionHandler::deletePermission)
+
+ // ========== 团课路由 ==========
+ .GET("/api/groupCourse", groupCourseHandler::getAllGroupCourse)
.build();
}
diff --git a/gym-manage-api/manage-app/src/main/resources/application-dev.yml b/gym-manage-api/manage-app/src/main/resources/application-dev.yml
index 8f5e8dc..9cfe50c 100644
--- a/gym-manage-api/manage-app/src/main/resources/application-dev.yml
+++ b/gym-manage-api/manage-app/src/main/resources/application-dev.yml
@@ -12,6 +12,9 @@ spring:
max-life-time: 30m
acquire-timeout: 3s
flyway:
+ url: jdbc:postgresql://localhost:55432/manage_system
+ user: novalon
+ password: novalon123
enabled: true
locations: classpath:db/migration
baseline-on-migrate: true
diff --git a/gym-manage-api/manage-app/src/main/resources/application.yml b/gym-manage-api/manage-app/src/main/resources/application.yml
index 9305b16..0d7edda 100644
--- a/gym-manage-api/manage-app/src/main/resources/application.yml
+++ b/gym-manage-api/manage-app/src/main/resources/application.yml
@@ -25,8 +25,8 @@ spring:
acquire-timeout: 5s
datasource:
url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:55432}/${DB_NAME:manage_system}
- username: ${DB_USERNAME:postgres}
- password: ${DB_PASSWORD:postgres}
+ username: ${DB_USERNAME:novalon}
+ password: ${DB_PASSWORD:novalon123}
driver-class-name: org.postgresql.Driver
flyway:
enabled: true
@@ -38,6 +38,8 @@ spring:
user:
name: disabled
password: disabled
+ profiles:
+ active: dev
management:
endpoints:
diff --git a/gym-manage-api/manage-db/pom.xml b/gym-manage-api/manage-db/pom.xml
index 392f5e3..2c54f5b 100644
--- a/gym-manage-api/manage-db/pom.xml
+++ b/gym-manage-api/manage-db/pom.xml
@@ -99,6 +99,8 @@
spring-boot-starter-test
test
+
+
diff --git a/gym-manage-api/manage-db/src/main/java/cn/novalon/gym/manage/db/converter/GroupCourseConverter.java b/gym-manage-api/manage-db/src/main/java/cn/novalon/gym/manage/db/converter/GroupCourseConverter.java
new file mode 100644
index 0000000..87b66ad
--- /dev/null
+++ b/gym-manage-api/manage-db/src/main/java/cn/novalon/gym/manage/db/converter/GroupCourseConverter.java
@@ -0,0 +1,55 @@
+package cn.novalon.gym.manage.db.converter;/*
+ * @author:liwentao
+ * @date:2026/4/26-04-26-13:18
+ */
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.novalon.gym.manage.db.entity.GroupCourseEntity;
+import cn.novalon.gym.manage.sys.core.domain.GroupCourse;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Component
+@Slf4j
+public class GroupCourseConverter {
+ public GroupCourse toDomain(GroupCourseEntity entity){
+ if(entity == null){
+ return null;
+ }
+ GroupCourse groupCourse = new GroupCourse();
+ BeanUtil.copyProperties(entity,groupCourse);
+ log.info("转换bean,entity-domain:",groupCourse);
+ return groupCourse;
+ }
+
+ public GroupCourseEntity toEntity(GroupCourse domain){
+ if(domain == null){
+ return null;
+ }
+ GroupCourseEntity entity = new GroupCourseEntity();
+ BeanUtil.copyProperties(domain,entity);
+ log.info("转换bean,domain-entity:",entity);
+ return entity;
+ }
+
+ public List toDomainList(List entities){
+ if (entities == null) {
+ return null;
+ }
+ return entities.stream()
+ .map(this::toDomain)
+ .collect(Collectors.toList());
+ }
+
+ public List toEntityList(List groupCourses){
+ if (groupCourses == null) {
+ return null;
+ }
+ return groupCourses.stream()
+ .map(this::toEntity)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/gym-manage-api/manage-db/src/main/java/cn/novalon/gym/manage/db/dao/GroupCourseDao.java b/gym-manage-api/manage-db/src/main/java/cn/novalon/gym/manage/db/dao/GroupCourseDao.java
new file mode 100644
index 0000000..0855162
--- /dev/null
+++ b/gym-manage-api/manage-db/src/main/java/cn/novalon/gym/manage/db/dao/GroupCourseDao.java
@@ -0,0 +1,23 @@
+package cn.novalon.gym.manage.db.dao;
+
+import cn.novalon.gym.manage.db.entity.GroupCourseEntity;
+import cn.novalon.gym.manage.db.entity.SysUserEntity;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.r2dbc.repository.R2dbcRepository;
+import org.springframework.stereotype.Repository;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+@Repository
+public interface GroupCourseDao extends R2dbcRepository {
+
+ Mono findByIdIsAndDeletedAtIsNull(Long id);
+
+ Flux findAll();
+
+ Flux findAll(Sort sort);
+
+ Flux findAllByDeletedAtIsNull();
+
+ Flux findAllByDeletedAtIsNull(Sort sort);
+}
diff --git a/gym-manage-api/manage-db/src/main/java/cn/novalon/gym/manage/db/entity/GroupCourseBookingEntity.java b/gym-manage-api/manage-db/src/main/java/cn/novalon/gym/manage/db/entity/GroupCourseBookingEntity.java
new file mode 100644
index 0000000..f414735
--- /dev/null
+++ b/gym-manage-api/manage-db/src/main/java/cn/novalon/gym/manage/db/entity/GroupCourseBookingEntity.java
@@ -0,0 +1,75 @@
+package cn.novalon.gym.manage.db.entity;
+
+import org.springframework.data.relational.core.mapping.Column;
+import org.springframework.data.relational.core.mapping.Table;
+
+import java.util.Date;
+
+/**
+ * @author:liwentao
+ * @date:2026/4/25-04-25-17:50
+ * 团课预约记录表
+ */
+
+@Table("group_course_booking")
+public class GroupCourseBookingEntity extends BaseEntity {
+
+ //团课id
+ @Column("course_id")
+ private Long courseId;
+
+ //用户id
+ @Column("user_id")
+ private Long userId;
+
+ //预约时间
+ @Column("booking_time")
+ private Date bookingTime;
+
+ //状态:0-已预约,1-已取消,2-已出席,3-缺席
+ @Column("status")
+ private Long status;
+ //取消时间
+ @Column("cancel_time")
+ private Date cancelTime;
+
+ public Long getCourseId() {
+ return courseId;
+ }
+
+ public void setCourseId(Long courseId) {
+ this.courseId = courseId;
+ }
+
+ public Long getUserId() {
+ return userId;
+ }
+
+ public void setUserId(Long userId) {
+ this.userId = userId;
+ }
+
+ public Date getBookingTime() {
+ return bookingTime;
+ }
+
+ public void setBookingTime(Date bookingTime) {
+ this.bookingTime = bookingTime;
+ }
+
+ public Long getStatus() {
+ return status;
+ }
+
+ public void setStatus(Long status) {
+ this.status = status;
+ }
+
+ public Date getCancelTime() {
+ return cancelTime;
+ }
+
+ public void setCancelTime(Date cancelTime) {
+ this.cancelTime = cancelTime;
+ }
+}
diff --git a/gym-manage-api/manage-db/src/main/java/cn/novalon/gym/manage/db/entity/GroupCourseEntity.java b/gym-manage-api/manage-db/src/main/java/cn/novalon/gym/manage/db/entity/GroupCourseEntity.java
new file mode 100644
index 0000000..67cc612
--- /dev/null
+++ b/gym-manage-api/manage-db/src/main/java/cn/novalon/gym/manage/db/entity/GroupCourseEntity.java
@@ -0,0 +1,148 @@
+package cn.novalon.gym.manage.db.entity;
+/**
+ * @author:liwentao
+ * @date:2026/4/25-04-25-17:34
+ * 团课表
+ */
+
+import org.springframework.data.relational.core.mapping.Column;
+import org.springframework.data.relational.core.mapping.Table;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+
+@Table("group_course")
+public class GroupCourseEntity extends BaseEntity {
+
+ //课程名称
+ @Column("course_name")
+ private String courseName;
+
+ //教练id
+ @Column("coach_id")
+ private Long coachId;
+
+ //课程类型
+ @Column("course_type")
+ private Long courseType;
+
+ //开始时间
+ @Column("start_time")
+ private LocalDateTime startTime;
+
+ //结束时间
+ @Column("end_time")
+ private LocalDateTime endTime;
+
+ //最大参与人数
+ @Column("max_members")
+ private Integer maxMembers;
+
+ //当前参与人数
+ @Column("current_members")
+ private Integer currentMembers;
+
+ //课程状态:0-正常,1-已取消,2-已结束
+ @Column("status")
+ private Long status;
+
+ //上课地点
+ @Column("location")
+ private String location;
+
+ //封面图URL
+ @Column("cover_image")
+ private String coverImage;
+
+ //课程描述
+ @Column("description")
+ private String description;
+
+ public String getCourseName() {
+ return courseName;
+ }
+
+ public void setCourseName(String courseName) {
+ this.courseName = courseName;
+ }
+
+ public Long getCoachId() {
+ return coachId;
+ }
+
+ public void setCoachId(Long coachId) {
+ this.coachId = coachId;
+ }
+
+ public Long getCourseType() {
+ return courseType;
+ }
+
+ public void setCourseType(Long courseType) {
+ this.courseType = courseType;
+ }
+
+ public LocalDateTime getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(LocalDateTime startTime) {
+ this.startTime = startTime;
+ }
+
+ public LocalDateTime getEndTime() {
+ return endTime;
+ }
+
+ public void setEndTime(LocalDateTime endTime) {
+ this.endTime = endTime;
+ }
+
+ public Integer getMaxMembers() {
+ return maxMembers;
+ }
+
+ public void setMaxMembers(Integer maxMembers) {
+ this.maxMembers = maxMembers;
+ }
+
+ public Integer getCurrentMembers() {
+ return currentMembers;
+ }
+
+ public void setCurrentMembers(Integer currentMembers) {
+ this.currentMembers = currentMembers;
+ }
+
+ public Long getStatus() {
+ return status;
+ }
+
+ public void setStatus(Long status) {
+ this.status = status;
+ }
+
+ public String getLocation() {
+ return location;
+ }
+
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ public String getCoverImage() {
+ return coverImage;
+ }
+
+ public void setCoverImage(String coverImage) {
+ this.coverImage = coverImage;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+}
diff --git a/gym-manage-api/manage-db/src/main/java/cn/novalon/gym/manage/db/repository/GroupCourseRepository.java b/gym-manage-api/manage-db/src/main/java/cn/novalon/gym/manage/db/repository/GroupCourseRepository.java
new file mode 100644
index 0000000..d4846b9
--- /dev/null
+++ b/gym-manage-api/manage-db/src/main/java/cn/novalon/gym/manage/db/repository/GroupCourseRepository.java
@@ -0,0 +1,57 @@
+package cn.novalon.gym.manage.db.repository;
+
+import cn.novalon.gym.manage.db.converter.GroupCourseConverter;
+import cn.novalon.gym.manage.db.dao.GroupCourseDao;
+import cn.novalon.gym.manage.sys.core.domain.GroupCourse;
+import cn.novalon.gym.manage.sys.core.repository.IGroupCourseRepository;
+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;
+
+/**
+ * @author:liwentao
+ * @date:2026/4/26-04-26-14:16
+ */
+@Repository
+public class GroupCourseRepository implements IGroupCourseRepository {
+ private final GroupCourseDao groupCourseDao;
+ private final GroupCourseConverter groupCourseConverter;
+ private final R2dbcEntityTemplate r2dbcEntityTemplate;
+
+ public GroupCourseRepository(GroupCourseDao groupCourseDao,GroupCourseConverter groupCourseConverter,
+ R2dbcEntityTemplate r2dbcEntityTemplate){
+ this.groupCourseDao = groupCourseDao;
+ this.groupCourseConverter = groupCourseConverter;
+ this.r2dbcEntityTemplate = r2dbcEntityTemplate;
+ }
+
+ @Override
+ public Mono findByIdAndDeletedAtIsNull(Long id) {
+ return groupCourseDao.findByIdIsAndDeletedAtIsNull(id)
+ .map(groupCourseConverter::toDomain);
+ }
+
+ @Override
+ public Flux findAll() {
+ return groupCourseDao.findAll()
+ .map(groupCourseConverter::toDomain);
+ }
+
+ public Flux findAll(Sort sort) {
+ return groupCourseDao.findAll(sort)
+ .map(groupCourseConverter::toDomain);
+ }
+
+ @Override
+ public Flux findByDeletedAtIsNull() {
+ return groupCourseDao.findAllByDeletedAtIsNull()
+ .map(groupCourseConverter::toDomain);
+ }
+
+ public Flux findByDeletedAtIsNull(Sort sort) {
+ return groupCourseDao.findAllByDeletedAtIsNull(sort)
+ .map(groupCourseConverter::toDomain);
+ }
+}
diff --git a/gym-manage-api/manage-db/src/main/java/cn/novalon/gym/manage/db/repository/SysUserRepository.java b/gym-manage-api/manage-db/src/main/java/cn/novalon/gym/manage/db/repository/SysUserRepository.java
index c0feccc..2fb85ee 100644
--- a/gym-manage-api/manage-db/src/main/java/cn/novalon/gym/manage/db/repository/SysUserRepository.java
+++ b/gym-manage-api/manage-db/src/main/java/cn/novalon/gym/manage/db/repository/SysUserRepository.java
@@ -10,6 +10,7 @@ import cn.novalon.gym.manage.sys.core.query.SysUserQuery;
import cn.novalon.gym.manage.sys.core.repository.ISysUserRepository;
import cn.novalon.gym.manage.common.dto.PageRequest;
import cn.novalon.gym.manage.common.dto.PageResponse;
+import cn.novalon.gym.manage.sys.dto.response.UserResponse;
import org.springframework.data.domain.Sort;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.data.relational.core.query.Query;
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V6__Create_GroupCourse_table.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V6__Create_GroupCourse_table.sql
new file mode 100644
index 0000000..4b82d66
--- /dev/null
+++ b/gym-manage-api/manage-db/src/main/resources/db/migration/V6__Create_GroupCourse_table.sql
@@ -0,0 +1,71 @@
+-- ============================================
+-- 团课相关表
+-- ============================================
+
+-- 团课课程表
+CREATE TABLE IF NOT EXISTS group_course (
+ id BIGSERIAL PRIMARY KEY,
+ course_name VARCHAR(100) NOT NULL,
+ coach_id BIGINT,
+ course_type BIGINT,
+ start_time TIMESTAMP NOT NULL,
+ end_time TIMESTAMP NOT NULL,
+ max_members INTEGER DEFAULT 20,
+ current_members INTEGER DEFAULT 0,
+ status VARCHAR(1) DEFAULT '0',
+ location VARCHAR(255),
+ cover_image VARCHAR(500),
+ description TEXT,
+ create_by VARCHAR(50),
+ update_by VARCHAR(50),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP
+);
+
+-- 团课预约记录表
+CREATE TABLE IF NOT EXISTS group_course_booking (
+ id BIGSERIAL PRIMARY KEY,
+ course_id BIGINT NOT NULL,
+ user_id BIGINT NOT NULL,
+ booking_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ status VARCHAR(1) DEFAULT '0',
+ cancel_time TIMESTAMP,
+ create_by VARCHAR(50),
+ update_by VARCHAR(50),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP
+);
+
+COMMENT ON TABLE group_course IS '团课课程表';
+COMMENT ON COLUMN group_course.id IS '主键ID';
+COMMENT ON COLUMN group_course.course_name IS '课程名称';
+COMMENT ON COLUMN group_course.coach_id IS '教练ID(关联sys_user)';
+COMMENT ON COLUMN group_course.course_type IS '课程类型(如瑜伽/普拉提/动感单车)';
+COMMENT ON COLUMN group_course.start_time IS '开始时间';
+COMMENT ON COLUMN group_course.end_time IS '结束时间';
+COMMENT ON COLUMN group_course.max_members IS '最大参与人数';
+COMMENT ON COLUMN group_course.current_members IS '当前参与人数';
+COMMENT ON COLUMN group_course.status IS '状态(0正常 1已取消 2已结束)';
+COMMENT ON COLUMN group_course.location IS '上课地点';
+COMMENT ON COLUMN group_course.cover_image IS '封面图URL';
+COMMENT ON COLUMN group_course.description IS '课程描述';
+COMMENT ON COLUMN group_course.create_by IS '创建人';
+COMMENT ON COLUMN group_course.update_by IS '更新人';
+COMMENT ON COLUMN group_course.created_at IS '创建时间';
+COMMENT ON COLUMN group_course.updated_at IS '更新时间';
+COMMENT ON COLUMN group_course.deleted_at IS '删除时间(软删除)';
+
+COMMENT ON TABLE group_course_booking IS '团课预约记录表';
+COMMENT ON COLUMN group_course_booking.id IS '主键ID';
+COMMENT ON COLUMN group_course_booking.course_id IS '团课ID';
+COMMENT ON COLUMN group_course_booking.user_id IS '用户ID';
+COMMENT ON COLUMN group_course_booking.booking_time IS '预约时间';
+COMMENT ON COLUMN group_course_booking.status IS '状态(0已预约 1已取消 2已出席 3缺席)';
+COMMENT ON COLUMN group_course_booking.cancel_time IS '取消时间';
+COMMENT ON COLUMN group_course_booking.create_by IS '创建人';
+COMMENT ON COLUMN group_course_booking.update_by IS '更新人';
+COMMENT ON COLUMN group_course_booking.created_at IS '创建时间';
+COMMENT ON COLUMN group_course_booking.updated_at IS '更新时间';
+COMMENT ON COLUMN group_course_booking.deleted_at IS '删除时间(软删除)';
\ No newline at end of file
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V7__Insert_GroupCourse_test_data.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V7__Insert_GroupCourse_test_data.sql
new file mode 100644
index 0000000..30d85c7
--- /dev/null
+++ b/gym-manage-api/manage-db/src/main/resources/db/migration/V7__Insert_GroupCourse_test_data.sql
@@ -0,0 +1,19 @@
+-- 测试数据1: 进行中的瑜伽课程 (已有部分学员)
+INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, cover_image, description, create_by, created_at, updated_at) VALUES
+ ('清晨流瑜伽', 101, 1, '2026-05-10 09:00:00', '2026-05-10 10:30:00', 15, 8, '1', 'A座3楼瑜伽教室', '/images/yoga_flow.jpg', '适合有一定基础的学员,通过流畅的体式连接呼吸,唤醒身体能量。', 'admin', '2026-05-01 10:00:00', '2026-05-01 10:00:00');
+
+-- 测试数据2: 即将开始的搏击课 (几乎满员)
+INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, cover_image, description, create_by, created_at, updated_at) VALUES
+ ('燃脂搏击', 102, 2, '2026-05-12 18:30:00', '2026-05-12 19:30:00', 20, 19, '1', '综合训练区', '/images/kickboxing.jpg', '高强度间歇训练,配合音乐快速燃脂,释放压力。', 'coach_zhang', '2026-05-02 14:30:00', '2026-05-02 14:30:00');
+
+-- 测试数据3: 已结束的私教小团课 (课程号已满)
+INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, cover_image, description, create_by, created_at, updated_at) VALUES
+ ('蜜桃臀塑造', 103, 3, '2026-04-25 19:00:00', '2026-04-25 20:00:00', 10, 10, '2', '私教专区', '/images/glute.jpg', '针对性训练臀部肌肉群,打造完美臀线。小班教学,动作一对一纠正。', 'coach_li', '2026-04-20 09:15:00', '2026-04-20 09:15:00');
+
+-- 测试数据4: 即将开始的动感单车 (名额充足,尚未有人报名)
+INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, cover_image, description, create_by, created_at, updated_at) VALUES
+ ('极速燃脂单车', 104, 2, '2026-05-15 19:30:00', '2026-05-15 20:20:00', 25, 0, '0', '单车房', '/images/spinning.jpg', '跟随音乐节奏变换阻力和速度,体验爬坡与冲刺的快感,一节课消耗800大卡。', 'admin', '2026-05-06 11:00:00', '2026-05-06 11:00:00');
+
+-- 测试数据5: 已删除/作废的课程 (deleted_at 不为空)
+INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, cover_image, description, create_by, created_at, updated_at, deleted_at) VALUES
+ ('周末冥想修复', 101, 1, '2026-05-03 15:00:00', '2026-05-03 16:00:00', 12, 3, '3', '冥想室', '/images/meditation.jpg', '通过呼吸和正念冥想,深度放松身心,缓解一周疲劳。', 'coach_wang', '2026-04-28 08:00:00', '2026-04-28 08:00:00', '2026-04-29 16:20:00');
\ No newline at end of file
diff --git a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/config/SecurityConfig.java b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/config/SecurityConfig.java
index 4df2a4b..b68cf04 100644
--- a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/config/SecurityConfig.java
+++ b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/config/SecurityConfig.java
@@ -50,7 +50,11 @@ public class SecurityConfig {
spec.pathMatchers("/api/auth/**").permitAll()
.pathMatchers("/api/public/**").permitAll()
.pathMatchers("/ws/**").permitAll()
- .pathMatchers("/actuator/**").permitAll();
+ .pathMatchers("/actuator/**").permitAll()
+ //========TODO lwt 以下是测试接口,生产时必关========
+ .pathMatchers("/api/users/**").permitAll()
+ .pathMatchers("/api/groupCourse/**").permitAll();
+
if (isDevOrTest) {
spec.pathMatchers("/swagger-ui.html").permitAll()
diff --git a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/domain/GroupCourse.java b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/domain/GroupCourse.java
new file mode 100644
index 0000000..11b9949
--- /dev/null
+++ b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/domain/GroupCourse.java
@@ -0,0 +1,146 @@
+package cn.novalon.gym.manage.sys.core.domain;
+/**
+ * @author:liwentao
+ * @date:2026/4/26-04-26-13:20
+ */
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import org.springframework.data.relational.core.mapping.Column;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+@Schema(description = "")
+public class GroupCourse extends BaseDomain{
+
+ //课程名称
+ @Schema(description = "团课名", example = "Push-up")
+ private String courseName;
+
+ //教练id
+ @Schema(description = "教练id", example = "1")
+ private Long coachId;
+
+ //课程类型
+ @Schema(description = "课程类型", example = "1")
+ private Long courseType;
+
+ //开始时间
+ @Schema(description = "开始时间", example = "2026-01-01")
+ private LocalDateTime startTime;
+
+ //结束时间
+ @Schema(description = "结束时间", example = "2026-01-02")
+ private LocalDateTime endTime;
+
+ //最大参与人数
+ @Schema(description = "最大参与人数", example = "20")
+ private Integer maxMembers;
+
+ //当前参与人数
+ @Schema(description = "当前参与人数", example = "2")
+ private Integer currentMembers;
+
+ //课程状态:0-正常,1-已取消,2-已结束
+ @Schema(description = "课程状态", example = "0")
+ private Long status;
+
+ //上课地点
+ @Schema(description = "上课地点", example = "龙泉驿区幸福路")
+ private String location;
+
+ //封面图URL
+ @Schema(description = "封面图URL", example = "https://12345.com")
+ private String coverImage;
+
+ //课程描述
+ @Schema(description = "课程描述", example = "从入门到入土")
+ private String description;
+
+ public String getCourseName() {
+ return courseName;
+ }
+
+ public void setCourseName(String courseName) {
+ this.courseName = courseName;
+ }
+
+ public Long getCoachId() {
+ return coachId;
+ }
+
+ public void setCoachId(Long coachId) {
+ this.coachId = coachId;
+ }
+
+ public Long getCourseType() {
+ return courseType;
+ }
+
+ public void setCourseType(Long courseType) {
+ this.courseType = courseType;
+ }
+
+ public LocalDateTime getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(LocalDateTime startTime) {
+ this.startTime = startTime;
+ }
+
+ public LocalDateTime getEndTime() {
+ return endTime;
+ }
+
+ public void setEndTime(LocalDateTime endTime) {
+ this.endTime = endTime;
+ }
+
+ public Integer getMaxMembers() {
+ return maxMembers;
+ }
+
+ public void setMaxMembers(Integer maxMembers) {
+ this.maxMembers = maxMembers;
+ }
+
+ public Integer getCurrentMembers() {
+ return currentMembers;
+ }
+
+ public void setCurrentMembers(Integer currentMembers) {
+ this.currentMembers = currentMembers;
+ }
+
+ public Long getStatus() {
+ return status;
+ }
+
+ public void setStatus(Long status) {
+ this.status = status;
+ }
+
+ public String getLocation() {
+ return location;
+ }
+
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ public String getCoverImage() {
+ return coverImage;
+ }
+
+ public void setCoverImage(String coverImage) {
+ this.coverImage = coverImage;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+}
diff --git a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/repository/IGroupCourseRepository.java b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/repository/IGroupCourseRepository.java
new file mode 100644
index 0000000..2d020d4
--- /dev/null
+++ b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/repository/IGroupCourseRepository.java
@@ -0,0 +1,14 @@
+package cn.novalon.gym.manage.sys.core.repository;
+
+import cn.novalon.gym.manage.sys.core.domain.GroupCourse;
+import org.springframework.data.domain.Sort;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public interface IGroupCourseRepository {
+ Mono findByIdAndDeletedAtIsNull(Long id);
+ Flux findAll();
+ Flux findAll(Sort sort);
+ Flux findByDeletedAtIsNull();
+ Flux findByDeletedAtIsNull(Sort sort);
+}
diff --git a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/service/IGroupCourseService.java b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/service/IGroupCourseService.java
new file mode 100644
index 0000000..28efd2e
--- /dev/null
+++ b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/service/IGroupCourseService.java
@@ -0,0 +1,11 @@
+package cn.novalon.gym.manage.sys.core.service;
+
+import cn.novalon.gym.manage.sys.core.domain.GroupCourse;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public interface IGroupCourseService {
+ Mono findById(Long id);
+ Flux findAll();
+ Flux findAll(boolean includeDeleted);
+}
diff --git a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/service/impl/GroupCourseService.java b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/service/impl/GroupCourseService.java
new file mode 100644
index 0000000..2b1fcbf
--- /dev/null
+++ b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/service/impl/GroupCourseService.java
@@ -0,0 +1,42 @@
+package cn.novalon.gym.manage.sys.core.service.impl;
+
+import cn.novalon.gym.manage.sys.audit.service.IAuditLogService;
+import cn.novalon.gym.manage.sys.core.domain.GroupCourse;
+import cn.novalon.gym.manage.sys.core.repository.IGroupCourseRepository;
+import cn.novalon.gym.manage.sys.core.service.IGroupCourseService;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author:liwentao
+ * @date:2026/4/26-04-26-14:12
+ */
+@Service
+public class GroupCourseService implements IGroupCourseService {
+ private final IGroupCourseRepository groupCourseRepository;
+ private final IAuditLogService auditLogService;
+ public GroupCourseService(IGroupCourseRepository groupCourseRepository, IAuditLogService auditLogService){
+ this.groupCourseRepository = groupCourseRepository;
+ this.auditLogService = auditLogService;
+ }
+
+ @Override
+ public Mono findById(Long id) {
+ return groupCourseRepository.findByIdAndDeletedAtIsNull(id);
+ }
+
+ @Override
+ public Flux findAll() {
+ return groupCourseRepository.findAll();
+ }
+
+ @Override
+ public Flux findAll(boolean includeDeleted) {
+ if(includeDeleted){
+ return groupCourseRepository.findAll();
+ }else{
+ return groupCourseRepository.findByDeletedAtIsNull();
+ }
+ }
+}
diff --git a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/service/impl/SysUserService.java b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/service/impl/SysUserService.java
index 3d7bf64..ee1da91 100644
--- a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/service/impl/SysUserService.java
+++ b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/core/service/impl/SysUserService.java
@@ -15,6 +15,7 @@ import cn.novalon.gym.manage.sys.core.repository.IUserRoleRepository;
import cn.novalon.gym.manage.sys.core.service.ISysUserService;
import cn.novalon.gym.manage.sys.core.command.CreateUserCommand;
import cn.novalon.gym.manage.sys.core.command.UpdateUserCommand;
+import cn.novalon.gym.manage.sys.dto.response.UserResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
diff --git a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/handler/groupCourse/GroupCourseHandler.java b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/handler/groupCourse/GroupCourseHandler.java
new file mode 100644
index 0000000..b439351
--- /dev/null
+++ b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/handler/groupCourse/GroupCourseHandler.java
@@ -0,0 +1,40 @@
+package cn.novalon.gym.manage.sys.handler.groupCourse;
+
+import cn.novalon.gym.manage.sys.core.domain.GroupCourse;
+import cn.novalon.gym.manage.sys.core.service.IGroupCourseService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Validator;
+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;
+
+/**
+ * @author:liwentao
+ * @date:2026/4/26-04-26-14:30
+ */
+@Component
+@Tag(name="团课管理",description = "团课相关操作")
+public class GroupCourseHandler {
+ private final IGroupCourseService groupCourseService;
+ private final Validator validator;
+ public GroupCourseHandler(IGroupCourseService groupCourseService,Validator validator){
+ this.groupCourseService = groupCourseService;
+ this.validator = validator;
+ }
+
+ @Operation(summary = "获取所有用户", description = "获取系统中所有用户列表")
+ public Mono getAllGroupCourse(ServerRequest request){
+ boolean includeDeleted = Boolean.valueOf(request.queryParam("includeDeleted").orElse("false"));
+ return ServerResponse.ok()
+ .body(groupCourseService.findAll(includeDeleted), GroupCourse.class);
+ }
+
+ public Mono getGroupCourseById(ServerRequest request){
+ Long id = Long.valueOf(request.pathVariable("id"));
+ return groupCourseService.findById(id)
+ .flatMap(groupCourse -> ServerResponse.ok().bodyValue(groupCourse))
+ .switchIfEmpty(ServerResponse.notFound().build());
+ }
+}
diff --git a/gym-manage-api/pom.xml b/gym-manage-api/pom.xml
index df2e4c2..aec5848 100644
--- a/gym-manage-api/pom.xml
+++ b/gym-manage-api/pom.xml
@@ -221,6 +221,13 @@
lombok
true
+
+
+
+ cn.hutool
+ hutool-all
+ 5.8.38
+