diff --git a/gym-manage-api/docs/groupcourse-api.md b/gym-manage-api/docs/groupcourse-api.md
index 124c171..4880759 100644
--- a/gym-manage-api/docs/groupcourse-api.md
+++ b/gym-manage-api/docs/groupcourse-api.md
@@ -604,26 +604,26 @@
| 属性 | 值 |
|------|-----|
| **HTTP方法** | POST |
-| **接口路径** | `/api/groupCourse/{courseId}/signin` |
+| **接口路径** | `/api/groupCourse/signin/{memberId}` |
| **所属文件** | `GroupCourseHandler.java` |
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
-| courseId | Long | 是 | 团课ID |
+| memberId | Long | 是 | 会员ID |
**请求体**:
```json
{
- "memberId": 1001
+ "courseId": 1
}
```
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
-| memberId | Long | **是** | 会员ID |
+| courseId | Long | **是** | 团课ID |
**成功响应** (200 OK):
@@ -638,16 +638,37 @@
}
```
-**失败响应** (400 Bad Request):
+**失败响应** (400 Bad Request) —— 校验规则及对应错误信息:
+
+| 序号 | 校验规则 | 错误信息 |
+|------|----------|----------|
+| 1 | 团课不存在或已被删除 | `团课不存在或已删除` |
+| 2 | 团课状态为"已取消" | `团课已取消,无法签到` |
+| 3 | 团课状态为"已结束" | `团课已结束,无法签到` |
+| 4 | 当前时间早于开课前2小时 | `未到签到时间,请在开课前2小时内签到` |
+| 5 | 当前时间晚于团课结束时间 | `团课已结束,无法签到` |
+| 6 | 课程当前人数已达上限 | `课程已满员,无法签到` |
+| 7 | 用户未预约此课程 | `您未预约此课程,无法签到` |
+| - | 请求体为空 | `请求体不能为空` |
+| - | 请求体缺少courseId | `courseId不能为空` |
+
+> **说明**: 校验按上表顺序依次执行,命中第一个失败条件即返回对应错误信息。
+
+**失败响应示例**:
```json
{
"success": false,
- "message": "课程已满员"
+ "message": "未到签到时间,请在开课前2小时内签到"
}
```
----
+```json
+{
+ "success": false,
+ "message": "您未预约此课程,无法签到"
+}
+```
### 删除团课
@@ -1637,7 +1658,13 @@
### 团课管理
1. **创建团课**:课程名称为必填项
2. **取消团课**:需提前24小时通知,否则拒绝操作
-3. **团课签到**:验证课程状态必须为正常,且未达最大人数限制
+3. **团课签到**:严格按以下顺序校验,任一不通过即拒绝签到:
+ - 团课是否存在且未被删除
+ - 团课状态不为"已取消"
+ - 团课状态不为"已结束"(含已过结束时间)
+ - 当前时间在开课前2小时内(签到时间窗口)
+ - 课程当前人数未达到最大人数上限
+ - 用户已预约该课程(有效预约)
4. **删除团课**:采用软删除机制,数据保留可恢复
### 团课预约
diff --git a/gym-manage-api/gym-groupCourse/pom.xml b/gym-manage-api/gym-groupCourse/pom.xml
index a29e14e..bfafbbb 100644
--- a/gym-manage-api/gym-groupCourse/pom.xml
+++ b/gym-manage-api/gym-groupCourse/pom.xml
@@ -86,6 +86,25 @@
gym-member
${project.version}
+
+
+
+ com.google.zxing
+ core
+ 3.5.3
+
+
+ com.google.zxing
+ javase
+ 3.5.3
+
+
+
+
+ com.aliyun.oss
+ aliyun-sdk-oss
+ 3.17.1
+
diff --git a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/domain/GroupCourse.java b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/domain/GroupCourse.java
index b3100e7..f059d36 100644
--- a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/domain/GroupCourse.java
+++ b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/domain/GroupCourse.java
@@ -60,6 +60,10 @@ public class GroupCourse extends BaseDomain{
@Schema(description = "储值卡额度(消耗金额)", example = "50.00")
private java.math.BigDecimal storedValueAmount;
+ //二维码路径
+ @Schema(description = "二维码路径", example = "D:\\Games\\exmp\\image\\abc123_20260618120000.png")
+ private String qrCodePath;
+
public String getCourseName() {
return courseName;
}
@@ -163,4 +167,12 @@ public class GroupCourse extends BaseDomain{
public void setStoredValueAmount(java.math.BigDecimal storedValueAmount) {
this.storedValueAmount = storedValueAmount;
}
+
+ public String getQrCodePath() {
+ return qrCodePath;
+ }
+
+ public void setQrCodePath(String qrCodePath) {
+ this.qrCodePath = qrCodePath;
+ }
}
diff --git a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/domain/GroupCourseDetail.java b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/domain/GroupCourseDetail.java
index d8ae246..9ca0202 100644
--- a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/domain/GroupCourseDetail.java
+++ b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/domain/GroupCourseDetail.java
@@ -54,6 +54,9 @@ public class GroupCourseDetail extends BaseDomain {
@Schema(description = "储值卡额度(消耗金额)", example = "50.00")
private BigDecimal storedValueAmount;
+ @Schema(description = "二维码路径", example = "D:\\Games\\exmp\\image\\abc123_20260618120000.png")
+ private String qrCodePath;
+
// ===== 关联的类型信息 =====
@Schema(description = "类型信息")
@@ -187,6 +190,14 @@ public class GroupCourseDetail extends BaseDomain {
this.storedValueAmount = storedValueAmount;
}
+ public String getQrCodePath() {
+ return qrCodePath;
+ }
+
+ public void setQrCodePath(String qrCodePath) {
+ this.qrCodePath = qrCodePath;
+ }
+
public GroupCourseType getTypeInfo() {
return typeInfo;
}
diff --git a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/entity/GroupCourseEntity.java b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/entity/GroupCourseEntity.java
index 1b16c78..9e4d98c 100644
--- a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/entity/GroupCourseEntity.java
+++ b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/entity/GroupCourseEntity.java
@@ -62,6 +62,10 @@ public class GroupCourseEntity extends BaseEntity {
@Column("stored_value_amount")
private java.math.BigDecimal storedValueAmount;
+ //二维码路径
+ @Column("qr_code_path")
+ private String qrCodePath;
+
public String getCourseName() {
return courseName;
}
@@ -165,4 +169,12 @@ public class GroupCourseEntity extends BaseEntity {
public void setStoredValueAmount(java.math.BigDecimal storedValueAmount) {
this.storedValueAmount = storedValueAmount;
}
+
+ public String getQrCodePath() {
+ return qrCodePath;
+ }
+
+ public void setQrCodePath(String qrCodePath) {
+ this.qrCodePath = qrCodePath;
+ }
}
diff --git a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/handler/GroupCourseHandler.java b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/handler/GroupCourseHandler.java
index d0abac6..9b92588 100644
--- a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/handler/GroupCourseHandler.java
+++ b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/handler/GroupCourseHandler.java
@@ -159,7 +159,7 @@ public class GroupCourseHandler {
@Operation(summary = "团课签到", description = "会员签到参加团课")
public Mono signIn(ServerRequest request) {
- Long courseId = Long.valueOf(request.pathVariable("courseId"));
+ Long memberId = Long.valueOf(request.pathVariable("memberId"));
return request.bodyToMono(Map.class)
.flatMap(body -> {
@@ -170,15 +170,15 @@ public class GroupCourseHandler {
return ServerResponse.badRequest().bodyValue(response);
}
- Object memberIdObj = body.get("memberId");
- if (memberIdObj == null) {
+ Object courseIdObj = body.get("courseId");
+ if (courseIdObj == null) {
Map response = new HashMap<>();
response.put("success", false);
- response.put("message", "memberId不能为空");
+ response.put("message", "courseId不能为空");
return ServerResponse.badRequest().bodyValue(response);
}
- Long memberId = ((Number) memberIdObj).longValue();
+ Long courseId = ((Number) courseIdObj).longValue();
return groupCourseService.signIn(courseId, memberId)
.flatMap(course -> {
diff --git a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/initializer/QrCodeInitializer.java b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/initializer/QrCodeInitializer.java
new file mode 100644
index 0000000..772309f
--- /dev/null
+++ b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/initializer/QrCodeInitializer.java
@@ -0,0 +1,89 @@
+package cn.novalon.gym.manage.groupcourse.initializer;
+
+import cn.novalon.gym.manage.groupcourse.domain.GroupCourse;
+import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseRepository;
+import cn.novalon.gym.manage.groupcourse.util.QRCodeUtil;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.stereotype.Component;
+import reactor.core.publisher.Mono;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * 项目启动时补全缺失的团课二维码
+ * 遍历所有未删除的团课,对qrCodePath为空的课程生成二维码并上传至阿里云OSS
+ */
+@Component
+public class QrCodeInitializer implements CommandLineRunner {
+
+ private static final Logger logger = LoggerFactory.getLogger(QrCodeInitializer.class);
+
+ private final IGroupCourseRepository groupCourseRepository;
+ private final ObjectMapper objectMapper;
+
+ public QrCodeInitializer(IGroupCourseRepository groupCourseRepository,
+ ObjectMapper objectMapper) {
+ this.groupCourseRepository = groupCourseRepository;
+ this.objectMapper = objectMapper;
+ }
+
+ @Override
+ public void run(String... args) {
+ logger.info("===== 开始检查团课二维码缺失情况 =====");
+
+ groupCourseRepository.findByDeletedAtIsNull()
+ .filter(course -> course.getQrCodePath() == null || course.getQrCodePath().isEmpty())
+ .flatMap(course -> {
+ try {
+ logger.info("发现缺失二维码的团课 - id={}, name={}", course.getId(), course.getCourseName());
+
+ // 生成二维码内容:团课基础信息JSON
+ Map qrCodeContent = new HashMap<>();
+ qrCodeContent.put("id", course.getId());
+ qrCodeContent.put("courseName", course.getCourseName());
+ qrCodeContent.put("coachId", course.getCoachId());
+ qrCodeContent.put("courseType", course.getCourseType());
+ qrCodeContent.put("startTime", course.getStartTime() != null ? course.getStartTime().toString() : null);
+ qrCodeContent.put("endTime", course.getEndTime() != null ? course.getEndTime().toString() : null);
+ qrCodeContent.put("maxMembers", course.getMaxMembers());
+ qrCodeContent.put("location", course.getLocation());
+ qrCodeContent.put("description", course.getDescription());
+
+ String jsonContent = objectMapper.writeValueAsString(qrCodeContent);
+
+ // 生成二维码并上传到阿里云OSS
+ String uuid = UUID.randomUUID().toString().replace("-", "");
+ String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
+ String fileName = "qr_" + uuid + "_" + timestamp + ".png";
+ String ossUrl = QRCodeUtil.generateQRCodeAndUploadToOSS(jsonContent, fileName);
+
+ course.setQrCodePath(ossUrl);
+
+ // 更新数据库
+ return groupCourseRepository.update(course)
+ .doOnSuccess(updated -> logger.info("团课二维码补全成功 - id={}, name={}, ossUrl={}",
+ updated.getId(), updated.getCourseName(), ossUrl))
+ .doOnError(error -> logger.error("团课二维码补全失败(更新DB) - id={}, name={}, error: {}",
+ course.getId(), course.getCourseName(), error.getMessage()));
+ } catch (Exception e) {
+ logger.error("团课二维码补全失败(生成) - id={}, name={}, error: {}",
+ course.getId(), course.getCourseName(), e.getMessage(), e);
+ return Mono.empty();
+ }
+ })
+ .collectList()
+ .doOnSuccess(list -> logger.info("===== 团课二维码检查完毕,共补全 {} 个缺失二维码 =====", list.size()))
+ .onErrorResume(error -> {
+ logger.error("团课二维码初始化检查异常: {}", error.getMessage(), error);
+ return Mono.empty();
+ })
+ .subscribe();
+ }
+}
diff --git a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseService.java b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseService.java
index 037fe44..023c8ee 100644
--- a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseService.java
+++ b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseService.java
@@ -18,6 +18,7 @@ import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseBookingRepositor
import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseRepository;
import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseTypeRepository;
import cn.novalon.gym.manage.groupcourse.service.IGroupCourseService;
+import cn.novalon.gym.manage.groupcourse.util.QRCodeUtil;
import cn.novalon.gym.manage.member.entity.MemberCard;
import cn.novalon.gym.manage.member.entity.MemberCardRecord;
import cn.novalon.gym.manage.member.enums.MemberCardType;
@@ -32,6 +33,8 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
@Service
public class GroupCourseService implements IGroupCourseService {
@@ -150,6 +153,7 @@ public class GroupCourseService implements IGroupCourseService {
detail.setDescription(course.getDescription());
detail.setPointCardAmount(course.getPointCardAmount());
detail.setStoredValueAmount(course.getStoredValueAmount());
+ detail.setQrCodePath(course.getQrCodePath());
detail.setCreatedAt(course.getCreatedAt());
detail.setUpdatedAt(course.getUpdatedAt());
@@ -263,7 +267,38 @@ public class GroupCourseService implements IGroupCourseService {
@Override
public Mono create(GroupCourse groupCourse) {
return groupCourseRepository.save(groupCourse)
- .doOnSuccess(course -> logger.info("团课创建成功 - id={}, name={}", course.getId(), course.getCourseName()))
+ .flatMap(course -> {
+ try {
+ // 生成二维码内容:团课基础信息JSON
+ Map qrCodeContent = new HashMap<>();
+ qrCodeContent.put("id", course.getId());
+ qrCodeContent.put("courseName", course.getCourseName());
+ qrCodeContent.put("coachId", course.getCoachId());
+ qrCodeContent.put("courseType", course.getCourseType());
+ qrCodeContent.put("startTime", course.getStartTime());
+ qrCodeContent.put("endTime", course.getEndTime());
+ qrCodeContent.put("maxMembers", course.getMaxMembers());
+ qrCodeContent.put("location", course.getLocation());
+ qrCodeContent.put("description", course.getDescription());
+ qrCodeContent.put("createdAt", course.getCreatedAt());
+
+ String jsonContent = objectMapper.writeValueAsString(qrCodeContent);
+
+ // 生成二维码并上传到阿里云OSS
+ String qrCodeUrl = QRCodeUtil.generateQRCodeAndUploadToOSS(jsonContent);
+ course.setQrCodePath(qrCodeUrl);
+
+ logger.info("团课二维码上传到OSS成功 - id={}, qrCodeUrl={}", course.getId(), qrCodeUrl);
+
+ // 更新团课信息,保存二维码路径
+ return groupCourseRepository.update(course)
+ .doOnSuccess(updatedCourse -> logger.info("团课创建成功 - id={}, name={}", updatedCourse.getId(), updatedCourse.getCourseName()));
+ } catch (Exception e) {
+ logger.error("团课二维码生成失败 - id={}, error: {}", course.getId(), e.getMessage(), e);
+ // 即使二维码生成失败,也返回成功创建的团课
+ return Mono.just(course);
+ }
+ })
.flatMap(course -> clearCache().thenReturn(course))
.doOnError(error -> logger.error("团课创建失败 - error: {}", error.getMessage()));
}
@@ -309,6 +344,9 @@ public class GroupCourseService implements IGroupCourseService {
if (groupCourse.getStoredValueAmount() != null) {
existing.setStoredValueAmount(groupCourse.getStoredValueAmount());
}
+ if (groupCourse.getQrCodePath() != null) {
+ existing.setQrCodePath(groupCourse.getQrCodePath());
+ }
return groupCourseRepository.update(existing);
})
.doOnSuccess(course -> logger.info("团课更新成功 - id={}", id))
@@ -441,19 +479,36 @@ public class GroupCourseService implements IGroupCourseService {
@Override
public Mono signIn(Long courseId, Long memberId) {
return groupCourseRepository.findByIdAndDeletedAtIsNull(courseId)
- .switchIfEmpty(Mono.error(new RuntimeException("团课不存在")))
+ .switchIfEmpty(Mono.error(new RuntimeException("团课不存在或已删除")))
.flatMap(course -> {
- if (course.getStatus() != 0L) {
- return Mono.error(new RuntimeException("课程状态不允许签到"));
+ // 校验1:团课已取消
+ if (course.getStatus().equals(CourseStatus.CANCELLED.getValue())) {
+ return Mono.error(new RuntimeException("团课已取消,无法签到"));
}
+ // 校验2:团课已结束
+ if (course.getStatus().equals(CourseStatus.ENDED.getValue())) {
+ return Mono.error(new RuntimeException("团课已结束,无法签到"));
+ }
+
+ // 校验3:签到时间校验(开课前2小时内才能签到)
+ LocalDateTime now = LocalDateTime.now();
+ LocalDateTime signInStart = course.getStartTime().minusHours(2);
+ if (now.isBefore(signInStart)) {
+ return Mono.error(new RuntimeException("未到签到时间,请在开课前2小时内签到"));
+ }
+ if (now.isAfter(course.getEndTime())) {
+ return Mono.error(new RuntimeException("团课已结束,无法签到"));
+ }
+
+ // 校验4:课程已满员
if (course.getCurrentMembers() >= course.getMaxMembers()) {
- return Mono.error(new RuntimeException("课程已满员"));
+ return Mono.error(new RuntimeException("课程已满员,无法签到"));
}
- // 检查会员是否已预约此课程
+ // 校验5:用户已预约此课程(有效预约,状态为0-已预约)
return bookingRepository.findValidBooking(courseId, memberId)
- .switchIfEmpty(Mono.error(new RuntimeException("会员未预约此课程")))
+ .switchIfEmpty(Mono.error(new RuntimeException("您未预约此课程,无法签到")))
.flatMap(booking -> {
// 更新课程当前人数
return groupCourseRepository.updateCurrentMembers(courseId, 1)
diff --git a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/util/OSSUtil.java b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/util/OSSUtil.java
new file mode 100644
index 0000000..391e27f
--- /dev/null
+++ b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/util/OSSUtil.java
@@ -0,0 +1,109 @@
+package cn.novalon.gym.manage.groupcourse.util;
+
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.OSSClientBuilder;
+import com.aliyun.oss.model.PutObjectRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * 阿里云OSS工具类
+ */
+public class OSSUtil {
+
+ private static final Logger logger = LoggerFactory.getLogger(OSSUtil.class);
+
+ // OSS配置信息
+ private static final String ENDPOINT = "oss-cn-beijing.aliyuncs.com";
+ private static final String ACCESS_KEY_ID = "LTAI5t9TFh9Vayeahz45kZjg";
+ private static final String ACCESS_KEY_SECRET = "zD6NlCeH5UhjBs4vnQVqn8Ksi3CaZz";
+ private static final String BUCKET_NAME = "ycc-filesaver";
+
+ // OSS访问地址前缀
+ private static final String OSS_URL_PREFIX = "https://" + BUCKET_NAME + "." + ENDPOINT + "/";
+
+ // 文件存储目录
+ private static final String QRCODE_DIR = "qrcode/";
+
+ /**
+ * 上传文件到阿里云OSS
+ *
+ * @param localFilePath 本地文件路径
+ * @param fileName 文件名(不含路径)
+ * @return OSS访问地址
+ */
+ public static String uploadToOSS(String localFilePath, String fileName) {
+ OSS ossClient = null;
+ try {
+ // 创建OSS客户端
+ ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
+
+ // 构建OSS文件路径:qrcode/2026/06/18/xxx.png
+ String datePath = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
+ String ossFilePath = QRCODE_DIR + datePath + "/" + fileName;
+
+ // 创建上传请求
+ PutObjectRequest putObjectRequest = new PutObjectRequest(BUCKET_NAME, ossFilePath, new File(localFilePath));
+
+ // 上传文件
+ ossClient.putObject(putObjectRequest);
+
+ // 构建访问地址
+ String accessUrl = OSS_URL_PREFIX + ossFilePath;
+
+ logger.info("文件上传到OSS成功: localPath={}, ossUrl={}", localFilePath, accessUrl);
+
+ return accessUrl;
+ } catch (Exception e) {
+ logger.error("文件上传到OSS失败 - localPath: {}, error: {}", localFilePath, e.getMessage(), e);
+ throw new RuntimeException("文件上传到OSS失败: " + e.getMessage(), e);
+ } finally {
+ if (ossClient != null) {
+ ossClient.shutdown();
+ }
+ }
+ }
+
+ /**
+ * 上传文件到阿里云OSS(自定义存储路径)
+ *
+ * @param localFilePath 本地文件路径
+ * @param ossDirectory OSS存储目录
+ * @param fileName 文件名(不含路径)
+ * @return OSS访问地址
+ */
+ public static String uploadToOSS(String localFilePath, String ossDirectory, String fileName) {
+ OSS ossClient = null;
+ try {
+ // 创建OSS客户端
+ ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
+
+ // 构建OSS文件路径
+ String ossFilePath = ossDirectory + fileName;
+
+ // 创建上传请求
+ PutObjectRequest putObjectRequest = new PutObjectRequest(BUCKET_NAME, ossFilePath, new File(localFilePath));
+
+ // 上传文件
+ ossClient.putObject(putObjectRequest);
+
+ // 构建访问地址
+ String accessUrl = OSS_URL_PREFIX + ossFilePath;
+
+ logger.info("文件上传到OSS成功: localPath={}, ossUrl={}", localFilePath, accessUrl);
+
+ return accessUrl;
+ } catch (Exception e) {
+ logger.error("文件上传到OSS失败 - localPath: {}, error: {}", localFilePath, e.getMessage(), e);
+ throw new RuntimeException("文件上传到OSS失败: " + e.getMessage(), e);
+ } finally {
+ if (ossClient != null) {
+ ossClient.shutdown();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/util/QRCodeUtil.java b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/util/QRCodeUtil.java
new file mode 100644
index 0000000..44fc2a1
--- /dev/null
+++ b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/util/QRCodeUtil.java
@@ -0,0 +1,156 @@
+package cn.novalon.gym.manage.groupcourse.util;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.WriterException;
+import com.google.zxing.client.j2se.MatrixToImageWriter;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.qrcode.QRCodeWriter;
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * 二维码生成工具类
+ */
+public class QRCodeUtil {
+
+ private static final Logger logger = LoggerFactory.getLogger(QRCodeUtil.class);
+
+ // 二维码默认保存路径(本地临时路径)
+ private static final String DEFAULT_SAVE_PATH = "D:\\Games\\exmp\\image";
+
+ // 二维码尺寸
+ private static final int QR_CODE_WIDTH = 300;
+ private static final int QR_CODE_HEIGHT = 300;
+
+ /**
+ * 生成二维码并保存到指定路径
+ *
+ * @param content 二维码内容
+ * @param savePath 保存路径
+ * @param fileName 文件名(不含扩展名)
+ * @return 生成的二维码文件完整路径
+ */
+ public static String generateQRCode(String content, String savePath, String fileName) {
+ try {
+ Path directory = Paths.get(savePath);
+ if (!Files.exists(directory)) {
+ Files.createDirectories(directory);
+ logger.info("创建二维码保存目录: {}", savePath);
+ }
+
+ Map hints = new HashMap<>();
+ hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
+ hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
+ hints.put(EncodeHintType.MARGIN, 1);
+
+ QRCodeWriter qrCodeWriter = new QRCodeWriter();
+ BitMatrix bitMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, QR_CODE_WIDTH, QR_CODE_HEIGHT, hints);
+
+ String filePath = Paths.get(savePath, fileName + ".png").toString();
+ Path path = Paths.get(filePath);
+
+ MatrixToImageWriter.writeToPath(bitMatrix, "PNG", path);
+ logger.info("二维码生成成功: {}", filePath);
+
+ return filePath;
+ } catch (WriterException e) {
+ logger.error("二维码生成失败 - WriterException: {}", e.getMessage(), e);
+ throw new RuntimeException("二维码生成失败: " + e.getMessage(), e);
+ } catch (IOException e) {
+ logger.error("二维码保存失败 - IOException: {}", e.getMessage(), e);
+ throw new RuntimeException("二维码保存失败: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * 生成二维码并保存到默认路径
+ * 文件名格式: UUID + 创建时间
+ *
+ * @param content 二维码内容
+ * @return 生成的二维码文件完整路径
+ */
+ public static String generateQRCode(String content) {
+ String uuid = UUID.randomUUID().toString().replace("-", "");
+ String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
+ String fileName = uuid + "_" + timestamp;
+
+ return generateQRCode(content, DEFAULT_SAVE_PATH, fileName);
+ }
+
+ /**
+ * 生成二维码并保存到默认路径,使用自定义文件名
+ *
+ * @param content 二维码内容
+ * @param fileName 文件名(不含扩展名)
+ * @return 生成的二维码文件完整路径
+ */
+ public static String generateQRCodeWithFileName(String content, String fileName) {
+ return generateQRCode(content, DEFAULT_SAVE_PATH, fileName);
+ }
+
+ /**
+ * 生成二维码并上传到阿里云OSS
+ * 文件名格式: UUID + 创建时间
+ *
+ * @param content 二维码内容
+ * @return 阿里云OSS访问地址
+ */
+ public static String generateQRCodeAndUploadToOSS(String content) {
+ String uuid = UUID.randomUUID().toString().replace("-", "");
+ String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
+ String fileName = uuid + "_" + timestamp + ".png";
+
+ return generateQRCodeAndUploadToOSS(content, fileName);
+ }
+
+ /**
+ * 生成二维码并上传到阿里云OSS
+ *
+ * @param content 二维码内容
+ * @param fileName 文件名(含扩展名)
+ * @return 阿里云OSS访问地址
+ */
+ public static String generateQRCodeAndUploadToOSS(String content, String fileName) {
+ try {
+ Path tempDir = Files.createTempDirectory("qrcode_temp");
+ String tempFilePath = tempDir.resolve(fileName).toString();
+
+ Map hints = new HashMap<>();
+ hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
+ hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
+ hints.put(EncodeHintType.MARGIN, 1);
+
+ QRCodeWriter qrCodeWriter = new QRCodeWriter();
+ BitMatrix bitMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, QR_CODE_WIDTH, QR_CODE_HEIGHT, hints);
+
+ MatrixToImageWriter.writeToPath(bitMatrix, "PNG", Paths.get(tempFilePath));
+ logger.info("二维码临时文件生成成功: {}", tempFilePath);
+
+ String ossUrl = OSSUtil.uploadToOSS(tempFilePath, fileName);
+
+ Files.deleteIfExists(Paths.get(tempFilePath));
+ Files.deleteIfExists(tempDir);
+ logger.info("临时文件已删除: {}", tempFilePath);
+
+ return ossUrl;
+ } catch (WriterException e) {
+ logger.error("二维码生成失败 - WriterException: {}", e.getMessage(), e);
+ throw new RuntimeException("二维码生成失败: " + e.getMessage(), e);
+ } catch (IOException e) {
+ logger.error("二维码处理失败 - IOException: {}", e.getMessage(), e);
+ throw new RuntimeException("二维码处理失败: " + e.getMessage(), e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/gym-manage-api/gym-groupCourse/src/test/java/cn/novalon/gym/manage/groupcourse/util/QRCodeUtilTest.java b/gym-manage-api/gym-groupCourse/src/test/java/cn/novalon/gym/manage/groupcourse/util/QRCodeUtilTest.java
new file mode 100644
index 0000000..709cb3f
--- /dev/null
+++ b/gym-manage-api/gym-groupCourse/src/test/java/cn/novalon/gym/manage/groupcourse/util/QRCodeUtilTest.java
@@ -0,0 +1,71 @@
+package cn.novalon.gym.manage.groupcourse.util;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import java.nio.file.Path;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * QRCodeUtil测试类
+ */
+class QRCodeUtilTest {
+
+ @TempDir
+ Path tempDir;
+
+ @Test
+ void testGenerateQRCode() {
+ String content = "测试二维码内容";
+ String qrCodePath = QRCodeUtil.generateQRCode(content);
+
+ assertNotNull(qrCodePath, "二维码路径不应为空");
+ assertTrue(qrCodePath.endsWith(".png"), "二维码文件应为PNG格式");
+ assertTrue(qrCodePath.contains("D:\\Games\\exmp\\image"), "二维码应保存到指定路径");
+
+ System.out.println("生成的二维码路径: " + qrCodePath);
+ }
+
+ @Test
+ void testGenerateQRCodeWithCustomPath() {
+ String content = "自定义路径测试";
+ String customPath = tempDir.toString();
+ String fileName = "test_qrcode";
+
+ String qrCodePath = QRCodeUtil.generateQRCode(content, customPath, fileName);
+
+ assertNotNull(qrCodePath, "二维码路径不应为空");
+ assertTrue(qrCodePath.endsWith(".png"), "二维码文件应为PNG格式");
+ assertTrue(qrCodePath.contains(fileName), "二维码文件名应包含指定名称");
+
+ System.out.println("生成的二维码路径: " + qrCodePath);
+ }
+
+ @Test
+ void testGenerateQRCodeWithJsonContent() {
+ String jsonContent = "{\"id\":1,\"courseName\":\"瑜伽课\",\"coachId\":100,\"startTime\":\"2026-06-18T10:00:00\"}";
+
+ String qrCodePath = QRCodeUtil.generateQRCode(jsonContent);
+
+ assertNotNull(qrCodePath, "二维码路径不应为空");
+ assertTrue(qrCodePath.endsWith(".png"), "二维码文件应为PNG格式");
+
+ System.out.println("JSON内容二维码路径: " + qrCodePath);
+ }
+
+ @Test
+ void testGenerateQRCodeAndUploadToOSS() {
+ String jsonContent = "{\"id\":1,\"courseName\":\"瑜伽课\",\"coachId\":100,\"startTime\":\"2026-06-18T10:00:00\"}";
+
+ String ossUrl = QRCodeUtil.generateQRCodeAndUploadToOSS(jsonContent);
+
+ assertNotNull(ossUrl, "OSS访问地址不应为空");
+ assertTrue(ossUrl.startsWith("https://"), "OSS访问地址应为HTTPS");
+ assertTrue(ossUrl.contains("ycc-filesaver.oss-cn-beijing.aliyuncs.com"), "OSS访问地址应包含正确的域名");
+ assertTrue(ossUrl.contains("/qrcode/"), "OSS访问地址应包含qrcode目录");
+ assertTrue(ossUrl.endsWith(".png"), "OSS访问地址应为PNG格式");
+
+ System.out.println("上传到OSS的二维码地址: " + ossUrl);
+ }
+}
\ No newline at end of file
diff --git a/gym-manage-api/log.text b/gym-manage-api/log.text
index 081e809..f336e34 100644
--- a/gym-manage-api/log.text
+++ b/gym-manage-api/log.text
@@ -1,223 +1,387 @@
-2026-06-11T13:43:44.371+08:00 TRACE 9324 --- [gym-manage-api] [nio-8084-exec-2] o.s.w.s.adapter.HttpWebHandlerAdapter : [27afd417] HTTP POST "/api/groupCourse/types/1/labels", headers={masked}
-2026-06-11T13:43:44.372+08:00 DEBUG 9324 --- [gym-manage-api] [ parallel-2] o.s.w.s.s.DefaultWebSessionManager : Created new WebSession.
-2026-06-11T13:43:44.372+08:00 INFO 9324 --- [gym-manage-api] [ parallel-2] c.n.g.m.sys.audit.OperationLogWebFilter : WebFilter 拦截请求: POST /api/groupCourse/types/1/labels
-2026-06-11T13:43:44.372+08:00 INFO 9324 --- [gym-manage-api] [ parallel-2] c.n.g.m.sys.audit.OperationLogWebFilter : 未匹配到操作日志配置,跳过: POST /api/groupCourse/types/1/labels
-2026-06-11T13:43:44.372+08:00 INFO 9324 --- [gym-manage-api] [ parallel-2] c.n.g.m.sys.audit.OperationLogWebFilter : WebFilter 拦截请求: POST /api/groupCourse/types/1/labels
-2026-06-11T13:43:44.372+08:00 INFO 9324 --- [gym-manage-api] [ parallel-2] c.n.g.m.sys.audit.OperationLogWebFilter : 未匹配到操作日志配置,跳过: POST /api/groupCourse/types/1/labels
-2026-06-11T13:43:44.372+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.372+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.372+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.372+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.372+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/dictionaries" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/users" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/users/{id}/action/change-password" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/users/{id}/action/logical-delete" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/users/logical-delete" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/users/action/restore" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/users/{id}/action/restore" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/users/{id}/roles" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/menus" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/roles" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/roles/{id}/restore" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/roles/{id}/permissions" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/config" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.373+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/logs/login" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/logs/exception" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/logs/operation" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/auth/login" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/auth/register" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/auth/logout" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/dict/types" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/dict/data" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/notices" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/messages" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/files/upload" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/permissions" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/member/auth/miniapp/login" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/member/auth/mp/callback" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/member/phone/bind" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/admin/member/{id}/phone" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.375+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/member-cards" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/member-card-records/purchase" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/member-card-records/{recordId}/renew" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/member-card-records/{recordId}/use" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/member-card-records/{recordId}/refund" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/member-card-records/process-expired" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/member-card-transactions" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/groupCourse/page" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/groupCourse/types" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/groupCourse/labels" does not match against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/groupCourse/types/{typeId}/labels" matches against value "/api/groupCourse/types/1/labels"
-2026-06-11T13:43:44.376+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.function.server.RouterFunctions : [27afd417] Matched (POST && /api/groupCourse/types/{typeId}/labels)
-2026-06-11T13:43:44.376+08:00 DEBUG 9324 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.s.s.RouterFunctionMapping : [27afd417] Mapped to cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000223a09d1920@3760f3e8
-2026-06-11T13:43:44.377+08:00 TRACE 9324 --- [gym-manage-api] [ parallel-2] org.springframework.web.HttpLogging : [27afd417] Decoded [{labelIds=[1, 3, 5]}]
-2026-06-11T13:43:44.377+08:00 DEBUG 9324 --- [gym-manage-api] [ parallel-2] o.s.r.c.R2dbcTransactionManager : Creating new transaction with name [cn.novalon.gym.manage.groupcourse.repository.impl.CourseLabelRepository.addLabelsToType]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
-2026-06-11T13:43:44.377+08:00 DEBUG 9324 --- [gym-manage-api] [ parallel-2] o.s.r.c.R2dbcTransactionManager : Acquired Connection [PooledConnection[PostgresqlConnection{client=io.r2dbc.postgresql.client.ReactorNettyClient@c3a5202, codecs=io.r2dbc.postgresql.codec.DefaultCodecs@922d1d4}]] for R2DBC transaction
-2026-06-11T13:43:44.377+08:00 DEBUG 9324 --- [gym-manage-api] [ parallel-2] o.s.r.c.R2dbcTransactionManager : Starting R2DBC transaction on Connection [PooledConnection[PostgresqlConnection{client=io.r2dbc.postgresql.client.ReactorNettyClient@c3a5202, codecs=io.r2dbc.postgresql.codec.DefaultCodecs@922d1d4}]] using [ExtendedTransactionDefinition [transactionName='cn.novalon.gym.manage.groupcourse.repository.impl.CourseLabelRepository.addLabelsToType', readOnly=false, isolationLevel=null, lockWaitTimeout=PT0S]]
-2026-06-11T13:43:44.379+08:00 DEBUG 9324 --- [gym-manage-api] [actor-tcp-nio-6] o.s.r2dbc.core.DefaultDatabaseClient : Executing SQL statement [SELECT course_type_label.type_id, course_type_label.label_id, course_type_label.id, course_type_label.create_by, course_type_label.update_by, course_type_label.created_at, course_type_label.updated_at, course_type_label.deleted_at FROM course_type_label WHERE course_type_label.type_id = $1 AND (course_type_label.label_id = $2) AND (course_type_label.deleted_at IS NULL)]
-2026-06-11T13:43:44.381+08:00 DEBUG 9324 --- [gym-manage-api] [actor-tcp-nio-6] o.s.r2dbc.core.DefaultDatabaseClient : Executing SQL statement [SELECT course_type_label.type_id, course_type_label.label_id, course_type_label.id, course_type_label.create_by, course_type_label.update_by, course_type_label.created_at, course_type_label.updated_at, course_type_label.deleted_at FROM course_type_label WHERE course_type_label.type_id = $1 AND (course_type_label.label_id = $2) AND (course_type_label.deleted_at IS NULL)]
-2026-06-11T13:43:44.382+08:00 DEBUG 9324 --- [gym-manage-api] [actor-tcp-nio-6] o.s.r2dbc.core.DefaultDatabaseClient : Executing SQL statement [SELECT course_type_label.type_id, course_type_label.label_id, course_type_label.id, course_type_label.create_by, course_type_label.update_by, course_type_label.created_at, course_type_label.updated_at, course_type_label.deleted_at FROM course_type_label WHERE course_type_label.type_id = $1 AND (course_type_label.label_id = $2) AND (course_type_label.deleted_at IS NULL)]
-2026-06-11T13:43:44.382+08:00 DEBUG 9324 --- [gym-manage-api] [actor-tcp-nio-6] o.s.r2dbc.core.DefaultDatabaseClient : Executing SQL statement [INSERT INTO course_type_label (type_id, label_id, create_by, update_by, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6)]
-2026-06-11T13:43:44.385+08:00 DEBUG 9324 --- [gym-manage-api] [actor-tcp-nio-6] o.s.r2dbc.core.DefaultDatabaseClient : Executing SQL statement [INSERT INTO course_type_label (type_id, label_id, create_by, update_by, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6)]
-2026-06-11T13:43:44.385+08:00 DEBUG 9324 --- [gym-manage-api] [actor-tcp-nio-6] o.s.r2dbc.core.DefaultDatabaseClient : Executing SQL statement [INSERT INTO course_type_label (type_id, label_id, create_by, update_by, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6)]
-2026-06-11T13:43:44.387+08:00 DEBUG 9324 --- [gym-manage-api] [actor-tcp-nio-6] o.s.r.c.R2dbcTransactionManager : Initiating transaction rollback
-2026-06-11T13:43:44.387+08:00 DEBUG 9324 --- [gym-manage-api] [actor-tcp-nio-6] o.s.r.c.R2dbcTransactionManager : Rolling back R2DBC transaction on Connection [PooledConnection[PostgresqlConnection{client=io.r2dbc.postgresql.client.ReactorNettyClient@c3a5202, codecs=io.r2dbc.postgresql.codec.DefaultCodecs@922d1d4}]]
-2026-06-11T13:43:44.389+08:00 DEBUG 9324 --- [gym-manage-api] [actor-tcp-nio-6] o.s.r.c.R2dbcTransactionManager : Releasing R2DBC Connection [PooledConnection[PostgresqlConnection{client=io.r2dbc.postgresql.client.ReactorNettyClient@c3a5202, codecs=io.r2dbc.postgresql.codec.DefaultCodecs@922d1d4}]] after transaction
-2026-06-11T13:43:44.389+08:00 ERROR 9324 --- [gym-manage-api] [actor-tcp-nio-6] c.n.g.m.g.s.impl.CourseLabelService : 标签添加到类型失败 - typeId=1, error: executeMany; SQL [INSERT INTO course_type_label (type_id, label_id, create_by, update_by, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6)]; 重复键违反唯一约束"course_type_label_type_id_label_id_key"
-2026-06-11T13:43:44.389+08:00 TRACE 9324 --- [gym-manage-api] [actor-tcp-nio-6] org.springframework.web.HttpLogging : [27afd417] Encoding [{success=false, message=executeMany; SQL [INSERT INTO course_type_label (type_id, label_id, create_by, update_by, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6)]; 重复键违反唯一约束"course_type_label_type_id_label_id_key"}]
-2026-06-11T13:43:44.390+08:00 TRACE 9324 --- [gym-manage-api] [actor-tcp-nio-6] o.s.w.s.adapter.HttpWebHandlerAdapter : [27afd417] Completed 400 BAD_REQUEST, headers={masked}
-2026-06-11T13:43:44.390+08:00 TRACE 9324 --- [gym-manage-api] [actor-tcp-nio-6] org.springframework.web.HttpLogging : [27afd417] onComplete
+[
+ {
+ "id": "3",
+ "createBy": "admin",
+ "updateBy": null,
+ "createdAt": "2026-06-15T10:00:00",
+ "updatedAt": "2026-06-15T10:00:00",
+ "deletedAt": null,
+ "courseId": "1",
+ "recommendTitle": "本周热门推荐",
+ "recommendContent": "极速燃脂单车课程,跟随音乐节奏变换阻力和速度,体验爬坡与冲刺的快感,一节课消耗800大卡!",
+ "recommendReason": "教练专业,课程内容丰富,深受学员喜爱,燃脂效果显著",
+ "priority": 20,
+ "isActive": true,
+ "groupCourse": {
+ "id": "1",
+ "createBy": "system",
+ "updateBy": "system",
+ "createdAt": "2026-06-11T13:50:25.118925",
+ "updatedAt": "2026-06-11T13:50:25.118925",
+ "deletedAt": null,
+ "courseName": "动感单车升级版aaaaa",
+ "coachId": "2",
+ "courseType": "2",
+ "startTime": "2026-06-02T16:45:00",
+ "endTime": "2026-06-15T20:20:00",
+ "maxMembers": 30,
+ "currentMembers": 0,
+ "status": "0",
+ "location": "单车房",
+ "coverImage": "/images/spinning.jpg",
+ "description": "升级版高强度有氧运动课程",
+ "pointCardAmount": 2,
+ "storedValueAmount": 80
+ }
+ },
+ {
+ "id": "7",
+ "createBy": "coach_li",
+ "updateBy": null,
+ "createdAt": "2026-05-25T09:15:00",
+ "updatedAt": "2026-05-25T09:15:00",
+ "deletedAt": null,
+ "courseId": "6",
+ "recommendTitle": "塑形热门课程",
+ "recommendContent": "蜜桃臀塑造课程,针对性训练臀部肌肉群,打造完美曲线。",
+ "recommendReason": "专业私教指导,动作标准,效果显著,深受女性学员喜爱",
+ "priority": 18,
+ "isActive": true,
+ "groupCourse": {
+ "id": "6",
+ "createBy": "coach_li",
+ "updateBy": null,
+ "createdAt": "2026-05-20T09:15:00",
+ "updatedAt": "2026-05-20T09:15:00",
+ "deletedAt": null,
+ "courseName": "蜜桃臀塑造",
+ "coachId": "103",
+ "courseType": "3",
+ "startTime": "2026-05-30T19:00:00",
+ "endTime": "2026-05-30T20:00:00",
+ "maxMembers": 10,
+ "currentMembers": 8,
+ "status": "2",
+ "location": "私教专区",
+ "coverImage": "/images/glute.jpg",
+ "description": "针对性训练臀部肌肉群,课程已于5月30日结束,无法预约。",
+ "pointCardAmount": 1,
+ "storedValueAmount": 0
+ }
+ },
+ {
+ "id": "9",
+ "createBy": "coach_zhang",
+ "updateBy": null,
+ "createdAt": "2026-06-15T14:00:00",
+ "updatedAt": "2026-06-15T14:00:00",
+ "deletedAt": null,
+ "courseId": "1",
+ "recommendTitle": "减脂首选课程",
+ "recommendContent": "想要快速减脂?极速燃脂单车是你的最佳选择!专业教练带领,科学训练计划。",
+ "recommendReason": "减脂效果最佳,课程强度适中,适合想要快速瘦身的学员",
+ "priority": 16,
+ "isActive": true,
+ "groupCourse": {
+ "id": "1",
+ "createBy": "system",
+ "updateBy": "system",
+ "createdAt": "2026-06-11T13:50:25.118925",
+ "updatedAt": "2026-06-11T13:50:25.118925",
+ "deletedAt": null,
+ "courseName": "动感单车升级版aaaaa",
+ "coachId": "2",
+ "courseType": "2",
+ "startTime": "2026-06-02T16:45:00",
+ "endTime": "2026-06-15T20:20:00",
+ "maxMembers": 30,
+ "currentMembers": 0,
+ "status": "0",
+ "location": "单车房",
+ "coverImage": "/images/spinning.jpg",
+ "description": "升级版高强度有氧运动课程",
+ "pointCardAmount": 2,
+ "storedValueAmount": 80
+ }
+ },
+ {
+ "id": "4",
+ "createBy": "admin",
+ "updateBy": null,
+ "createdAt": "2026-06-15T11:00:00",
+ "updatedAt": "2026-06-15T11:00:00",
+ "deletedAt": null,
+ "courseId": "2",
+ "recommendTitle": "新手友好推荐",
+ "recommendContent": "清晨流瑜伽课程,适合有一定基础的学员,通过流畅的体式连接呼吸,唤醒身体能量。",
+ "recommendReason": "适合新手入门,教练耐心指导,课程节奏适中",
+ "priority": 15,
+ "isActive": true,
+ "groupCourse": {
+ "id": "2",
+ "createBy": "admin",
+ "updateBy": null,
+ "createdAt": "2026-06-01T10:00:00",
+ "updatedAt": "2026-06-01T10:00:00",
+ "deletedAt": null,
+ "courseName": "清晨流瑜伽",
+ "coachId": "101",
+ "courseType": "1",
+ "startTime": "2026-06-12T09:00:00",
+ "endTime": "2026-06-12T10:30:00",
+ "maxMembers": 15,
+ "currentMembers": 5,
+ "status": "0",
+ "location": "A座3楼瑜伽教室",
+ "coverImage": "/images/yoga_flow.jpg",
+ "description": "适合有一定基础的学员,通过流畅的体式连接呼吸,唤醒身体能量。",
+ "pointCardAmount": 1,
+ "storedValueAmount": 0
+ }
+ },
+ {
+ "id": "10",
+ "createBy": "coach_wang",
+ "updateBy": null,
+ "createdAt": "2026-06-15T15:00:00",
+ "updatedAt": "2026-06-15T15:00:00",
+ "deletedAt": null,
+ "courseId": "2",
+ "recommendTitle": "晨练优选",
+ "recommendContent": "清晨流瑜伽,唤醒身体能量,开启活力一天!适合晨练爱好者。",
+ "recommendReason": "晨练最佳选择,提升身体活力,改善精神状态",
+ "priority": 14,
+ "isActive": true,
+ "groupCourse": {
+ "id": "2",
+ "createBy": "admin",
+ "updateBy": null,
+ "createdAt": "2026-06-01T10:00:00",
+ "updatedAt": "2026-06-01T10:00:00",
+ "deletedAt": null,
+ "courseName": "清晨流瑜伽",
+ "coachId": "101",
+ "courseType": "1",
+ "startTime": "2026-06-12T09:00:00",
+ "endTime": "2026-06-12T10:30:00",
+ "maxMembers": 15,
+ "currentMembers": 5,
+ "status": "0",
+ "location": "A座3楼瑜伽教室",
+ "coverImage": "/images/yoga_flow.jpg",
+ "description": "适合有一定基础的学员,通过流畅的体式连接呼吸,唤醒身体能量。",
+ "pointCardAmount": 1,
+ "storedValueAmount": 0
+ }
+ },
+ {
+ "id": "6",
+ "createBy": "coach_li",
+ "updateBy": null,
+ "createdAt": "2026-06-15T13:00:00",
+ "updatedAt": "2026-06-15T13:00:00",
+ "deletedAt": null,
+ "courseId": "4",
+ "recommendTitle": "基础瑜伽推荐",
+ "recommendContent": "基础哈他瑜伽课程,适合所有级别学员,通过基础体式练习提升身体柔韧性和平衡能力。",
+ "recommendReason": "零基础友好,适合所有健身水平,放松身心",
+ "priority": 12,
+ "isActive": true,
+ "groupCourse": {
+ "id": "4",
+ "createBy": "coach_li",
+ "updateBy": null,
+ "createdAt": "2026-06-01T08:00:00",
+ "updatedAt": "2026-06-01T08:00:00",
+ "deletedAt": null,
+ "courseName": "哈他瑜伽",
+ "coachId": "101",
+ "courseType": "1",
+ "startTime": "2026-06-01T15:20:00",
+ "endTime": "2026-06-01T16:50:00",
+ "maxMembers": 12,
+ "currentMembers": 3,
+ "status": "0",
+ "location": "瑜伽教室B",
+ "coverImage": "/images/hatha_yoga.jpg",
+ "description": "基础哈他瑜伽,适合所有级别。距开始不足30分钟,已停止预约。",
+ "pointCardAmount": 1,
+ "storedValueAmount": 0
+ }
+ },
+ {
+ "id": "11",
+ "createBy": "coach_li",
+ "updateBy": null,
+ "createdAt": "2026-06-15T16:00:00",
+ "updatedAt": "2026-06-15T16:00:00",
+ "deletedAt": null,
+ "courseId": "4",
+ "recommendTitle": "身心平衡推荐",
+ "recommendContent": "哈他瑜伽课程,通过体式练习和呼吸控制,达到身心平衡,提升整体健康水平。",
+ "recommendReason": "改善身体柔韧性,增强核心力量,提升身体协调性",
+ "priority": 11,
+ "isActive": true,
+ "groupCourse": {
+ "id": "4",
+ "createBy": "coach_li",
+ "updateBy": null,
+ "createdAt": "2026-06-01T08:00:00",
+ "updatedAt": "2026-06-01T08:00:00",
+ "deletedAt": null,
+ "courseName": "哈他瑜伽",
+ "coachId": "101",
+ "courseType": "1",
+ "startTime": "2026-06-01T15:20:00",
+ "endTime": "2026-06-01T16:50:00",
+ "maxMembers": 12,
+ "currentMembers": 3,
+ "status": "0",
+ "location": "瑜伽教室B",
+ "coverImage": "/images/hatha_yoga.jpg",
+ "description": "基础哈他瑜伽,适合所有级别。距开始不足30分钟,已停止预约。",
+ "pointCardAmount": 1,
+ "storedValueAmount": 0
+ }
+ },
+ {
+ "id": "1",
+ "createBy": "system",
+ "updateBy": "system",
+ "createdAt": "2026-06-15T16:21:20.865146",
+ "updatedAt": "2026-06-15T16:23:05.180219",
+ "deletedAt": null,
+ "courseId": "1",
+ "recommendTitle": "本周热门课程",
+ "recommendContent": "这是一门非常棒的课程,快来参加吧!",
+ "recommendReason": "教练专业,课程内容丰富",
+ "priority": 10,
+ "isActive": false,
+ "groupCourse": {
+ "id": "1",
+ "createBy": "system",
+ "updateBy": "system",
+ "createdAt": "2026-06-11T13:50:25.118925",
+ "updatedAt": "2026-06-11T13:50:25.118925",
+ "deletedAt": null,
+ "courseName": "动感单车升级版aaaaa",
+ "coachId": "2",
+ "courseType": "2",
+ "startTime": "2026-06-02T16:45:00",
+ "endTime": "2026-06-15T20:20:00",
+ "maxMembers": 30,
+ "currentMembers": 0,
+ "status": "0",
+ "location": "单车房",
+ "coverImage": "/images/spinning.jpg",
+ "description": "升级版高强度有氧运动课程",
+ "pointCardAmount": 2,
+ "storedValueAmount": 80
+ }
+ },
+ {
+ "id": "5",
+ "createBy": "admin",
+ "updateBy": null,
+ "createdAt": "2026-06-15T12:00:00",
+ "updatedAt": "2026-06-15T12:00:00",
+ "deletedAt": null,
+ "courseId": "3",
+ "recommendTitle": "高强度燃脂",
+ "recommendContent": "燃脂搏击课程,高强度间歇训练,配合音乐快速燃脂,释放压力。",
+ "recommendReason": "高强度训练,适合进阶学员,快速燃脂塑形",
+ "priority": 10,
+ "isActive": false,
+ "groupCourse": {
+ "id": "3",
+ "createBy": "coach_zhang",
+ "updateBy": null,
+ "createdAt": "2026-06-01T14:30:00",
+ "updatedAt": "2026-06-01T14:30:00",
+ "deletedAt": null,
+ "courseName": "燃脂搏击",
+ "coachId": "102",
+ "courseType": "2",
+ "startTime": "2026-06-10T18:30:00",
+ "endTime": "2026-06-10T19:30:00",
+ "maxMembers": 20,
+ "currentMembers": 20,
+ "status": "0",
+ "location": "综合训练区",
+ "coverImage": "/images/kickboxing.jpg",
+ "description": "高强度间歇训练,配合音乐快速燃脂,释放压力。名额已满,无法预约。",
+ "pointCardAmount": 1,
+ "storedValueAmount": 0
+ }
+ },
+ {
+ "id": "12",
+ "createBy": "admin",
+ "updateBy": null,
+ "createdAt": "2026-05-25T10:00:00",
+ "updatedAt": "2026-05-25T10:00:00",
+ "deletedAt": null,
+ "courseId": "7",
+ "recommendTitle": "职场减压课程",
+ "recommendContent": "午间冥想放松,专为职场人士设计,快速缓解工作压力,提升工作状态。",
+ "recommendReason": "职场减压首选,课程时间短,效果显著",
+ "priority": 9,
+ "isActive": false,
+ "groupCourse": {
+ "id": "7",
+ "createBy": "admin",
+ "updateBy": null,
+ "createdAt": "2026-05-25T09:00:00",
+ "updatedAt": "2026-05-25T09:00:00",
+ "deletedAt": null,
+ "courseName": "午间冥想放松",
+ "coachId": "101",
+ "courseType": "1",
+ "startTime": "2026-05-31T12:00:00",
+ "endTime": "2026-05-31T13:00:00",
+ "maxMembers": 15,
+ "currentMembers": 6,
+ "status": "2",
+ "location": "冥想室",
+ "coverImage": "/images/meditation_noon.jpg",
+ "description": "午间冥想课程,已于5月31日结束。",
+ "pointCardAmount": 1,
+ "storedValueAmount": 0
+ }
+ },
+ {
+ "id": "8",
+ "createBy": "admin",
+ "updateBy": null,
+ "createdAt": "2026-05-25T09:00:00",
+ "updatedAt": "2026-05-25T09:00:00",
+ "deletedAt": null,
+ "courseId": "7",
+ "recommendTitle": "午间放松推荐",
+ "recommendContent": "午间冥想放松课程,通过呼吸和正念冥想,深度放松身心,缓解工作压力。",
+ "recommendReason": "适合上班族,午间放松充电,提升下午工作效率",
+ "priority": 8,
+ "isActive": true,
+ "groupCourse": {
+ "id": "7",
+ "createBy": "admin",
+ "updateBy": null,
+ "createdAt": "2026-05-25T09:00:00",
+ "updatedAt": "2026-05-25T09:00:00",
+ "deletedAt": null,
+ "courseName": "午间冥想放松",
+ "coachId": "101",
+ "courseType": "1",
+ "startTime": "2026-05-31T12:00:00",
+ "endTime": "2026-05-31T13:00:00",
+ "maxMembers": 15,
+ "currentMembers": 6,
+ "status": "2",
+ "location": "冥想室",
+ "coverImage": "/images/meditation_noon.jpg",
+ "description": "午间冥想课程,已于5月31日结束。",
+ "pointCardAmount": 1,
+ "storedValueAmount": 0
+ }
+ }
+]
\ 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 d8d05e8..e75fcf0 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
@@ -319,7 +319,7 @@ public class SystemRouter {
.PUT("/api/groupCourse/{id}", groupCourseHandler::updateGroupCourse)
.DELETE("/api/groupCourse/{id}", groupCourseHandler::deleteGroupCourse)
.POST("/api/groupCourse/{id}/cancel", groupCourseHandler::cancelGroupCourse)
- .POST("/api/groupCourse/{courseId}/signin", groupCourseHandler::signIn)
+ .POST("/api/groupCourse/signin/{memberId}", groupCourseHandler::signIn)
.POST("/api/groupCourse/search", groupCourseHandler::searchGroupCourses)
// ========= 签到模块路由 ==========
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V19__Add_GroupCourse_QRCode_Path.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V19__Add_GroupCourse_QRCode_Path.sql
new file mode 100644
index 0000000..8039970
--- /dev/null
+++ b/gym-manage-api/manage-db/src/main/resources/db/migration/V19__Add_GroupCourse_QRCode_Path.sql
@@ -0,0 +1,9 @@
+-- ============================================
+-- 为团课表添加二维码路径字段
+-- ============================================
+
+-- 添加二维码路径字段
+ALTER TABLE group_course ADD COLUMN qr_code_path VARCHAR(500);
+
+-- 添加字段注释
+COMMENT ON COLUMN group_course.qr_code_path IS '二维码图片路径';
\ No newline at end of file