From 0b2146f2370ed658f0edc9d73936e463cbeb1986 Mon Sep 17 00:00:00 2001 From: liwentao Date: Thu, 18 Jun 2026 10:11:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=9B=A2=E8=AF=BE=E6=89=AB?= =?UTF-8?q?=E7=A0=81=E7=AD=BE=E5=88=B0=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E5=B7=B2=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gym-manage-api/docs/groupcourse-api.md | 43 +- gym-manage-api/gym-groupCourse/pom.xml | 19 + .../groupcourse/domain/GroupCourse.java | 12 + .../groupcourse/domain/GroupCourseDetail.java | 11 + .../groupcourse/entity/GroupCourseEntity.java | 12 + .../handler/GroupCourseHandler.java | 10 +- .../initializer/QrCodeInitializer.java | 89 +++ .../service/impl/GroupCourseService.java | 69 +- .../gym/manage/groupcourse/util/OSSUtil.java | 109 ++++ .../manage/groupcourse/util/QRCodeUtil.java | 156 +++++ .../groupcourse/util/QRCodeUtilTest.java | 71 ++ gym-manage-api/log.text | 610 +++++++++++------- .../gym/manage/app/config/SystemRouter.java | 2 +- .../V19__Add_GroupCourse_QRCode_Path.sql | 9 + 14 files changed, 978 insertions(+), 244 deletions(-) create mode 100644 gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/initializer/QrCodeInitializer.java create mode 100644 gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/util/OSSUtil.java create mode 100644 gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/util/QRCodeUtil.java create mode 100644 gym-manage-api/gym-groupCourse/src/test/java/cn/novalon/gym/manage/groupcourse/util/QRCodeUtilTest.java create mode 100644 gym-manage-api/manage-db/src/main/resources/db/migration/V19__Add_GroupCourse_QRCode_Path.sql 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