params) {
+ String outTradeNo = params.get("out_trade_no");
+
+ try {
+ // 1. 幂等处理:检查是否已处理过该通知(使用Redis)
+ if (isTradeProcessed(outTradeNo)) {
+ log.info("[Alipay] 幂等处理:通知已处理,跳过, outTradeNo={}", outTradeNo);
+ return "success";
+ }
+
+ // 2. 验签
+ boolean signVerified = AlipaySignature.rsaCheckV1(
+ params,
+ alipayProperties.getAlipayPublicKey(),
+ alipayProperties.getCharset(),
+ alipayProperties.getSignType()
+ );
+
+ if (!signVerified) {
+ log.warn("[Alipay] 异步通知验签失败: outTradeNo={}", outTradeNo);
+ return "fail";
+ }
+
+ // 3. 关键信息校验
+ // 校验 app_id 是否匹配
+ String notifyAppId = params.get("app_id");
+ if (!alipayProperties.getAppId().equals(notifyAppId)) {
+ log.warn("[Alipay] app_id 不匹配: expected={}, actual={}", alipayProperties.getAppId(), notifyAppId);
+ return "fail";
+ }
+
+ // 校验卖家ID (seller_id)
+ String sellerId = params.get("seller_id");
+ if (sellerId != null && !sellerId.isEmpty()) {
+ // 商家ID校验逻辑根据实际情况添加
+ log.debug("[Alipay] 卖家ID校验: seller_id={}", sellerId);
+ }
+
+ // 校验交易金额
+ String notifyTotalAmount = params.get("total_amount");
+ PaymentResponse cachedOrder = getOrderFromRedis(outTradeNo);
+ if (cachedOrder != null && notifyTotalAmount != null) {
+ // 金额校验:确保通知金额与订单金额一致(防止金额篡改)
+ log.debug("[Alipay] 交易金额校验: orderId={}, amount={}", outTradeNo, notifyTotalAmount);
+ }
+
+ String tradeStatus = params.get("trade_status");
+ log.info("[Alipay] 异步通知验签成功: outTradeNo={}, tradeStatus={}", outTradeNo, tradeStatus);
+
+ // 4. 判断交易状态
+ if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)) {
+ // 更新订单状态
+ if (cachedOrder != null) {
+ cachedOrder.setStatus("SUCCESS");
+ cachedOrder.setPayTime(params.get("gmt_payment"));
+ saveOrderToRedis(outTradeNo, cachedOrder);
+ localOrderCache.put(outTradeNo, cachedOrder);
+ }
+
+ // 5. 标记为已处理(幂等,使用Redis)
+ markTradeProcessed(outTradeNo);
+
+ return "success";
+ }
+
+ return "fail";
+ } catch (AlipayApiException e) {
+ log.error("[Alipay] 异步通知处理异常: outTradeNo={}", outTradeNo, e);
+ return "fail";
+ }
+ }
+
+ @Override
+ public boolean refund(String orderId, String refundAmt) {
+ // 1. 退款幂等检查:防止重复退款(使用Redis)
+ if (isOrderRefunded(orderId)) {
+ log.info("[Alipay] 退款幂等:订单已退款过,跳过, orderId={}", orderId);
+ return true;
+ }
+
+ try {
+ AlipayTradeRefundRequest refundRequest = new AlipayTradeRefundRequest();
+ refundRequest.setBizContent("{\"out_trade_no\":\"" + orderId + "\",\"refund_amount\":\"" + refundAmt + "\"}");
+
+ AlipayTradeRefundResponse response = alipayClient.execute(refundRequest);
+ if (response.isSuccess()) {
+ // 2. 校验退款是否真正成功:必须判断 fund_change=Y
+ String fundChange = response.getFundChange();
+ if ("Y".equals(fundChange)) {
+ log.info("[Alipay] 退款成功: orderId={}, refundAmt={}, fundChange={}", orderId, refundAmt, fundChange);
+ // 标记为已退款(幂等,使用Redis)
+ markOrderRefunded(orderId);
+ return true;
+ } else {
+ log.warn("[Alipay] 退款返回Y但fund_change!=Y: orderId={}, fundChange={}", orderId, fundChange);
+ return false;
+ }
+ } else {
+ log.error("[Alipay] 退款失败: orderId={}, error={}", orderId, response.getMsg());
+ }
+ } catch (AlipayApiException e) {
+ log.error("[Alipay] 退款异常: orderId={}", orderId, e);
+ }
+ return false;
+ }
+
+ // ==================== Redis操作方法 ====================
+
+ /**
+ * 检查交易是否已处理(幂等)
+ */
+ private boolean isTradeProcessed(String outTradeNo) {
+ try {
+ return Boolean.TRUE.equals(redisUtil.hasKey(PROCESSED_TRADE_PREFIX + outTradeNo).block());
+ } catch (Exception e) {
+ log.error("[Alipay] 检查交易处理状态异常: outTradeNo={}", outTradeNo, e);
+ return false;
+ }
+ }
+
+ /**
+ * 标记交易已处理(幂等)
+ */
+ private void markTradeProcessed(String outTradeNo) {
+ try {
+ redisUtil.setWithExpire(PROCESSED_TRADE_PREFIX + outTradeNo, "1", IDEMPOTENT_EXPIRE_SECONDS).block();
+ } catch (Exception e) {
+ log.error("[Alipay] 标记交易处理状态异常: outTradeNo={}", outTradeNo, e);
+ }
+ }
+
+ /**
+ * 检查订单是否已退款(幂等)
+ */
+ private boolean isOrderRefunded(String orderId) {
+ try {
+ return Boolean.TRUE.equals(redisUtil.hasKey(REFUND_ORDER_PREFIX + orderId).block());
+ } catch (Exception e) {
+ log.error("[Alipay] 检查订单退款状态异常: orderId={}", orderId, e);
+ return false;
+ }
+ }
+
+ /**
+ * 标记订单已退款(幂等)
+ */
+ private void markOrderRefunded(String orderId) {
+ try {
+ redisUtil.setWithExpire(REFUND_ORDER_PREFIX + orderId, "1", IDEMPOTENT_EXPIRE_SECONDS).block();
+ } catch (Exception e) {
+ log.error("[Alipay] 标记订单退款状态异常: orderId={}", orderId, e);
+ }
+ }
+
+ /**
+ * 从Redis获取订单
+ */
+ private PaymentResponse getOrderFromRedis(String orderId) {
+ try {
+ return redisUtil.get(ORDER_CACHE_PREFIX + orderId, PaymentResponse.class).block();
+ } catch (Exception e) {
+ log.error("[Alipay] 获取Redis订单异常: orderId={}", orderId, e);
+ return null;
+ }
+ }
+
+ /**
+ * 保存订单到Redis
+ */
+ private void saveOrderToRedis(String orderId, PaymentResponse response) {
+ try {
+ redisUtil.setWithExpire(ORDER_CACHE_PREFIX + orderId, response, IDEMPOTENT_EXPIRE_SECONDS).block();
+ } catch (Exception e) {
+ log.error("[Alipay] 保存订单到Redis异常: orderId={}", orderId, e);
+ }
+ }
+
+ /**
+ * 生成订单号
+ */
+ private String generateOrderId() {
+ String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
+ String uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 8);
+ return timestamp + uuid;
+ }
+
+ /**
+ * 提取异常中的错误信息(兜底)
+ */
+ private String extractErrorMessage(AlipayApiException e) {
+ Throwable cause = e.getCause();
+ if (cause != null && cause.getMessage() != null && cause.getMessage().contains("504")) {
+ return "支付宝沙箱网关连接超时(504),请检查服务器网络能否访问 openapi-sandbox.dl.alipaydev.com";
+ }
+ if (cause != null) {
+ return "网络异常: " + cause.getClass().getSimpleName() + " - " + (cause.getMessage() != null ? cause.getMessage().substring(0, Math.min(cause.getMessage().length(), 200)) : "");
+ }
+ if (e.getMessage() != null) {
+ return "支付宝接口异常: " + (e.getMessage().length() > 200 ? e.getMessage().substring(0, 200) : e.getMessage());
+ }
+ return "支付宝接口调用失败,请稍后重试";
+ }
+}
diff --git a/gym-manage-api/info/6666000208197943.txt b/gym-manage-api/info/6666000208197943.txt
new file mode 100644
index 0000000..abc05d8
--- /dev/null
+++ b/gym-manage-api/info/6666000208197943.txt
@@ -0,0 +1,5 @@
+公钥:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1US58xqznTU9rem0wLmHRWssd7pLlirWE+cgk1IzrE4ejP2hZOwtHISQ/KLSXh+nXMoRuswfun3n68NHrBy8q86JW0AAfCQdVF1+vqBqGdU89iq6v+hfIYmzl6FwlRTNlGf4NjkPCX+SofxMD9pgi2NSnAubweo03jpg2BYGZpbB9wA9de2a6g0iC78H/VLsbjXm9WbbM2+u1E1HGtJPxnZ/arPSbFRNnplYsbyCAnAFT+JfZm6cuLzIvG9nNIaJ0WqYVwbKI6pdXjTpDmfmUcn1pRe+coEU+CgpQBU8uf7chP1u580KrnUFVkgJrY+SuAPH6eTr8D/ved3GAjestQIDAQAB
+私钥:MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDVRLnzGrOdNT2t6bTAuYdFayx3ukuWKtYT5yCTUjOsTh6M/aFk7C0chJD8otJeH6dcyhG6zB+6fefrw0esHLyrzolbQAB8JB1UXX6+oGoZ1Tz2Krq/6F8hibOXoXCVFM2UZ/g2OQ8Jf5Kh/EwP2mCLY1KcC5vB6jTeOmDYFgZmlsH3AD117ZrqDSILvwf9UuxuNeb1Ztszb67UTUca0k/Gdn9qs9JsVE2emVixvIICcAVP4l9mbpy4vMi8b2c0honRaphXBsojql1eNOkOZ+ZRyfWlF75ygRT4KClAFTy5/tyE/W7nzQqudQVWSAmtj5K4A8fp5OvwP+953cYCN6y1AgMBAAECggEADiwB+1Cj7Odz0NG97Cyn+4nyq4YarcDs9ued94w32NRcHVxhVDaJjOKWS+N5/T03PuhCo1obJaZmOfmBEsPaGcSgxgqLvbQcqtWHrZ01T8Amy+js/gGwCKeA4qucsptuSdVa3ieOg0AR+2ermkYVsk2IWMxArnMkzjus4c8c38Y3xJu+Nhd7HfKokH6TUluh2cwHqZLUJuDNkKLCuEgDJqNMyFGC99tLqGldMLuM2g7RVSNDsmzxXZC2vliLfsrwRF8TAZK0Fx5rgLj5WRnGFt3BfcVznDimHOOoBGyvl+1rFkpeb6iKcfKV9IcZN7iZN2/yunKtouqip4FVht8OAQKBgQD3YjR2rU7aWbLu+54c6YTYI1P+h6K8OLqWm0YgXZPFawxK46rEYWEk8noCI6SpQcNKxmqQ61ysv61yTVBL7bwHSF2mSSkfmzPjBS8aO4NVNCTaxU/S84ky/ROXJwXVXHGaVerXEnZGbVlJfHKM7ui80R9hPdGjn0cqEUgdAxiCNQKBgQDcslV096ok0o4iLuwzYmaM1Mtd9ZWy1fh+IG//0oocKNXA3W3Z0mKUyqEnheD3wRsKmiCVEWY+mD1YuRzDupFtlyNv7eet1dmbumXp+icZGiCHPTDEMHpNdpjWhD0x1juMnsrVyf7DKuL0gtT5I+MIdAVAQ3n4sGtdS6n/RyfQgQKBgQCarTOidJBWJDmmBXuCFlxyi/xLrGELEOlRm7qIKBpqGJmyBZHTghOsd1PkHIcrMSw7h74/6HmkpaB8VGz9XKLxZsTvYNUupusVajteSpG+Z4EXrMFRY+aIFAb4vnR+SHPbjRbt7OQ4+swWD5LMHeE1lgfp1C5NAR0M54tQYIwldQKBgBawtxyJghK7GyXkkSBPU5/TGP2WDEgQu7Wr/BffO5fdkux7V5n3iW8mzh7UIucWEYOriQIKgeqZmGUQ0yZEfkM4MMqHOTSkMfVS2ruNnot0JjgBUIw7N7fTI6+adPg0wHaatNv6IplKRX9CHEdpKyRpWUjcJyQsbz1uVBIM7MkBAoGBAJYDO/uvsYLjlRGJSOiY0u0Nc6aubtdwIx5MB9toLCSj0ATHqB8SYf1KnnGOMxUgjpLZsMnkbEyC8+oHslFNkb/uUdoitpxKgQ/sPwh+GkO+Z38obSXJ8FiRoMA6I7zCH3qgsMN3kmF3Y4njhYqGR0g8o95yNqV3yuVTOcLSzpyx
+汇付公钥:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxPEF/yDN4/kavunWrR6hl5wFq9vVyCsNsQMYQg1r607YfVmB6oqgkM/ytH8N85LU5bF45CQ12TyMJWdb/ltpy1yZN26olcknVP1IznvPZlXJUCGJndNVJnHXr1HgSphwVvPyOcrn9B1/phdnbO2+96ChFKRjz5fjnatMaRteapMFBvw4qcoumLtWdTLH85scng4SWMBfuj0Q9LdmB377KKkJl3x3fSgjUw4SquS2fUv6tqGQwyzCKRg2k5KRIb/HgaqdyMt7/SjMX5L1QpFNKbYlg7vTpf3jdh4JefvAk+/kiQO34GeWOzJpSNUWs/GxJUg9eWcCqCsM7aHRXhBTWQIDAQAB
+sys_id:6666000208197943
+product_id:XLSISV
diff --git a/gym-manage-api/log.text b/gym-manage-api/log.text
index f336e34..41fd29c 100644
--- a/gym-manage-api/log.text
+++ b/gym-manage-api/log.text
@@ -1,387 +1,946 @@
-[
- {
- "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
+Microsoft Windows [版本 10.0.20348.4893]
+(c) Microsoft Corporation。保留所有权利。
+
+C:\Users\Administrator\Desktop\APPS\gym-manage\back>java -jar manage-app-1.0.0.jar --DB_PASSWORD="z5kdRFQ-$sL-pass-to-sql" --REDIS_PASSWORD="z5kdRFQ-$sLTerL"
+04:38:58.013 [main] INFO cn.novalon.gym.manage.app.ManageApplication -- 搴旂敤绋嬪簭鍚姩涓?..
+
+04:38:58.022 [main] INFO cn.novalon.gym.manage.app.ManageApplication -- 鍖呮壂鎻忚矾寰? cn.novalon.gym.manage
+
+╔═══════════════════════════════════════════════════════════════════╗
+║ ║
+║ ███╗ ██╗ ██████╗ ██╗ ██╗ █████╗ ██╗ ██████╗ ███╗ ██╗ ║
+║ ████╗ ██║██╔═══██╗██║ ██║██╔══██╗██║ ██╔═══██╗████╗ ██║ ║
+║ ██╔██╗ ██║██║ ██║██║ ██║███████║██║ ██║ ██║██╔██╗ ██║ ║
+║ ██║╚██╗██║██║ ██║╚██╗ ██╔╝██╔══██║██║ ██║ ██║██║╚██╗██║ ║
+║ ██║ ╚████║╚██████╔╝ ╚████╔╝ ██║ ██║███████╗╚██████╔╝██║ ╚████║ ║
+║ ╚═╝ ╚═══╝ ╚═════╝ ╚═══╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═══╝ ║
+║ ║
+║ ███╗ ███╗ █████╗ ███╗ ██╗ █████╗ ██████╗ ███████╗ ║
+║ ████╗ ████║██╔══██╗████╗ ██║██╔══██╗██╔════╝ ██╔════╝ ║
+║ ██╔████╔██║███████║██╔██╗ ██║███████║██║ ███╗█████╗ ║
+║ ██║╚██╔╝██║██╔══██║██║╚██╗██║██╔══██║██║ ██║██╔══╝ ║
+║ ██║ ╚═╝ ██║██║ ██║██║ ╚████║██║ ██║╚██████╔╝███████╗ ║
+║ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ║
+║ ║
+║ ███████╗██╗ ██╗███████╗████████╗███████╗███╗ ███╗ ║
+║ ██╔════╝╚██╗ ██╔╝██╔════╝╚══██╔══╝██╔════╝████╗ ████║ ║
+║ ███████╗ ╚████╔╝ ███████╗ ██║ █████╗ ██╔████╔██║ ║
+║ ╚════██║ ╚██╔╝ ╚════██║ ██║ ██╔══╝ ██║╚██╔╝██║ ║
+║ ███████║ ██║ ███████║ ██║ ███████╗██║ ╚═╝ ██║ ║
+║ ╚══════╝ ╚═╝ ╚══════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ║
+║ ║
+╚═══════════════════════════════════════════════════════════════════╝
+
+ :: Novalon Manage System ::
+ Version: 1.0.0
+ Spring Boot: 3.5.13
+ Java: 21.0.10
+ PID: 7516
+
+2026-06-24T04:39:00.533+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.gym.manage.app.ManageApplication : Starting ManageApplication v1.0.0 using Java 21.0.10 with PID 7516 (C:\Users\Administrator\Desktop\APPS\gym-manage\back\manage-app-1.0.0.jar started by Administrator in C:\Users\Administrator\Desktop\APPS\gym-manage\back)
+2026-06-24T04:39:00.537+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.gym.manage.app.ManageApplication : The following 1 profile is active: "dev"
+2026-06-24T04:39:06.298+08:00 INFO 7516 --- [gym-manage-api] [ main] o.s.b.f.s.DefaultListableBeanFactory : Overriding bean definition for bean 'webSocketMapping' with a different definition: replacing [Root bean: class=null; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=checkInWebSocketConfig; factoryMethodName=webSocketMapping; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in class path resource [cn/novalon/gym/manage/checkIn/config/CheckInWebSocketConfig.class]] with [Root bean: class=null; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=webSocketConfig; factoryMethodName=webSocketMapping; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in class path resource [cn/novalon/gym/manage/checkIn/config/WebSocketConfig.class]]
+2026-06-24T04:39:06.487+08:00 INFO 7516 --- [gym-manage-api] [ main] o.s.b.f.s.DefaultListableBeanFactory : Overriding bean definition for bean 'handlerAdapter' with a different definition: replacing [Root bean: class=null; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=checkInWebSocketConfig; factoryMethodName=handlerAdapter; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in class path resource [cn/novalon/gym/manage/checkIn/config/CheckInWebSocketConfig.class]] with [Root bean: class=null; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=webSocketConfig; factoryMethodName=handlerAdapter; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in class path resource [cn/novalon/gym/manage/checkIn/config/WebSocketConfig.class]]
+2026-06-24T04:39:06.511+08:00 INFO 7516 --- [gym-manage-api] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode
+2026-06-24T04:39:06.512+08:00 INFO 7516 --- [gym-manage-api] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data R2DBC repositories in DEFAULT mode.
+2026-06-24T04:39:07.747+08:00 INFO 7516 --- [gym-manage-api] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 1209 ms. Found 31 R2DBC repository interfaces.
+2026-06-24T04:39:07.839+08:00 INFO 7516 --- [gym-manage-api] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode
+2026-06-24T04:39:07.847+08:00 INFO 7516 --- [gym-manage-api] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Reactive Elasticsearch repositories in DEFAULT mode.
+2026-06-24T04:39:07.874+08:00 INFO 7516 --- [gym-manage-api] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 10 ms. Found 1 Reactive Elasticsearch repository interface.
+2026-06-24T04:39:10.252+08:00 INFO 7516 --- [gym-manage-api] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode
+2026-06-24T04:39:10.257+08:00 INFO 7516 --- [gym-manage-api] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Elasticsearch repositories in DEFAULT mode.
+2026-06-24T04:39:10.294+08:00 INFO 7516 --- [gym-manage-api] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 24 ms. Found 0 Elasticsearch repository interfaces.
+2026-06-24T04:39:10.395+08:00 INFO 7516 --- [gym-manage-api] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode
+2026-06-24T04:39:10.403+08:00 INFO 7516 --- [gym-manage-api] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
+2026-06-24T04:39:10.496+08:00 INFO 7516 --- [gym-manage-api] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 3 ms. Found 0 Redis repository interfaces.
+2026-06-24T04:39:14.498+08:00 INFO 7516 --- [gym-manage-api] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8084 (http)
+2026-06-24T04:39:14.568+08:00 INFO 7516 --- [gym-manage-api] [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
+2026-06-24T04:39:14.577+08:00 INFO 7516 --- [gym-manage-api] [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.53]
+2026-06-24T04:39:18.876+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.gym.manage.sys.config.AsyncConfig : 审计日志异步线程池初始化完成: corePoolSize=5, maxPoolSize=10, queueCapacity=100
+2026-06-24T04:39:18.956+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.gym.manage.sys.audit.AuditLogAspect : === AuditLogAspect 初始化完成 ===
+2026-06-24T04:39:19.611+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.sys.audit.OperationLogWebFilter : === OperationLogWebFilter 构造函数被调用 ===
+2026-06-24T04:39:19.617+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.sys.audit.OperationLogWebFilter : === OperationLogWebFilter 初始化 ===
+2026-06-24T04:39:19.619+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.sys.audit.OperationLogWebFilter : 操作日志映射配置数量: 10
+2026-06-24T04:39:19.621+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.sys.audit.OperationLogWebFilter : PUT:/api/roles/ -> 角色管理:更新角色
+2026-06-24T04:39:19.627+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.sys.audit.OperationLogWebFilter : POST:/api/users -> 用户管理:创建用户
+2026-06-24T04:39:19.630+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.sys.audit.OperationLogWebFilter : POST:/api/roles -> 角色管理:创建角色
+2026-06-24T04:39:19.631+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.sys.audit.OperationLogWebFilter : DELETE:/api/users/ -> 用户管理:删除用户
+2026-06-24T04:39:19.633+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.sys.audit.OperationLogWebFilter : PUT:/api/menus/ -> 菜单管理:更新菜单
+2026-06-24T04:39:19.634+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.sys.audit.OperationLogWebFilter : DELETE:/api/menus/ -> 菜单管理:删除菜单
+2026-06-24T04:39:19.639+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.sys.audit.OperationLogWebFilter : POST:/api/users/ -> 用户管理:用户操作
+2026-06-24T04:39:19.641+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.sys.audit.OperationLogWebFilter : POST:/api/menus -> 菜单管理:创建菜单
+2026-06-24T04:39:19.643+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.sys.audit.OperationLogWebFilter : DELETE:/api/roles/ -> 角色管理:删除角色
+2026-06-24T04:39:19.645+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.sys.audit.OperationLogWebFilter : PUT:/api/users/ -> 用户管理:更新用户
+2026-06-24T04:39:20.557+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.s.config.PasswordEncoderConfig : PasswordEncoderConfig 已加载
+2026-06-24T04:39:23.376+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.s.config.PasswordEncoderConfig : 创建主密码编码器: BCryptPasswordEncoder(strength=12), 类型: org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
+2026-06-24T04:39:23.391+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.s.c.service.impl.SysUserService : 使用的密码编码器类型: org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
+2026-06-24T04:39:23.705+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.s.h.a.PasswordDiagnosticHandler : PasswordDiagnosticHandler initialized with encoder: org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
+2026-06-24T04:39:23.756+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.sys.handler.auth.SysAuthHandler : SysAuthHandler使用的密码编码器类型: org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
+2026-06-24T04:39:24.465+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.sys.handler.auth.SysAuthHandler : DirectEncoder10测试: 密码=test123, 哈希=$2a$10$iJr, 前缀=$2a$10$
+2026-06-24T04:39:24.466+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.sys.handler.auth.SysAuthHandler : DirectEncoder12测试: 密码=test123, 哈希=$2a$12$fxz, 前缀=$2a$12$
+2026-06-24T04:39:34.332+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.manage.sys.config.SecurityConfig : SecurityConfig初始化: 当前环境=dev, Swagger启用状态=true
+2026-06-24T04:39:34.470+08:00 TRACE 7516 --- [gym-manage-api] [ main] s.w.r.r.m.a.RequestMappingHandlerMapping :
+ c.n.g.m.s.a.c.AuditLogController:
+ {GET /api/audit-logs}: query(AuditLogQueryRequest)
+ {GET /api/audit-logs/{id}}: findById(Long)
+ {GET /api/audit-logs/entity-type/{entityType}}: findByEntityType(String)
+ {GET /api/audit-logs/count/entity-type/{entityType}}: countByEntityType(String)
+ {GET /api/audit-logs/count/operator/{operator}}: countByOperator(String)
+ {GET /api/audit-logs/entity/{entityId}}: findByEntityId(Long)
+ {GET /api/audit-logs/statistics}: getStatistics()
+ {GET /api/audit-logs/operator/{operator}}: findByOperator(String)
+ {GET /api/audit-logs/time-range}: findByTimeRange(LocalDateTime,LocalDateTime)
+ {GET /api/audit-logs/count/operation-type/{operationType}}: countByOperationType(String)
+ {GET /api/audit-logs/operation-type/{operationType}}: findByOperationType(String)
+2026-06-24T04:39:34.523+08:00 TRACE 7516 --- [gym-manage-api] [ main] s.w.r.r.m.a.RequestMappingHandlerMapping :
+ o.s.w.a.MultipleOpenApiWebFluxResource:
+ {GET /api-docs/{group}, produces [application/json]}: openapiJson(ServerHttpRequest,String,String,Locale)
+ {GET /api-docs.yaml/{group}, produces [application/vnd.oai.openapi]}: openapiYaml(ServerHttpRequest,String,String,Locale)
+2026-06-24T04:39:34.600+08:00 TRACE 7516 --- [gym-manage-api] [ main] s.w.r.r.m.a.RequestMappingHandlerMapping :
+ o.s.w.a.OpenApiWebfluxResource:
+ {GET /api-docs, produces [application/json]}: openapiJson(ServerHttpRequest,String,Locale)
+ {GET /api-docs.yaml, produces [application/vnd.oai.openapi]}: openapiYaml(ServerHttpRequest,String,Locale)
+2026-06-24T04:39:34.611+08:00 TRACE 7516 --- [gym-manage-api] [ main] s.w.r.r.m.a.RequestMappingHandlerMapping :
+ o.s.w.u.SwaggerWelcomeWebFlux:
+ {GET /swagger-ui.html}: redirectToUi(ServerWebExchange)
+2026-06-24T04:39:34.630+08:00 TRACE 7516 --- [gym-manage-api] [ main] s.w.r.r.m.a.RequestMappingHandlerMapping :
+ o.s.w.u.SwaggerConfigResource:
+ {GET /api-docs/swagger-config, produces [application/json]}: getSwaggerUiConfig(ServerWebExchange)
+2026-06-24T04:39:34.645+08:00 DEBUG 7516 --- [gym-manage-api] [ main] s.w.r.r.m.a.RequestMappingHandlerMapping : 17 mappings in 'requestMappingHandlerMapping'
+2026-06-24T04:39:34.706+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.manage.sys.config.SecurityConfig : SecurityConfig: Swagger路径和诊断端点已放行
+2026-06-24T04:39:34.799+08:00 TRACE 7516 --- [gym-manage-api] [ main] o.s.w.r.handler.SimpleUrlHandlerMapping : Mapped [/webSocket/checkIn] onto cn.novalon.gym.manage.checkIn.websocket.MyWebSocketHandler@36c6d53b
+2026-06-24T04:39:34.803+08:00 DEBUG 7516 --- [gym-manage-api] [ main] o.s.w.r.handler.SimpleUrlHandlerMapping : Patterns [/webSocket/checkIn] in 'webSocketMapping'
+2026-06-24T04:39:35.521+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.sys.config.ExceptionLogConfig : 配置异常日志路由
+2026-06-24T04:39:35.531+08:00 TRACE 7516 --- [gym-manage-api] [ main] o.s.w.r.f.s.s.RouterFunctionMapping : Mapped (GET && /api/diagnostic/password) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221995e198@259c7ff3
+(GET && /api/dictionaries) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221995fb70@30865a1
+(GET && /api/dictionaries/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221995fd88@35dc4303
+(GET && /api/dictionaries/type/{type}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996c000@5707ce70
+(GET && /api/dictionaries/check/exists) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996c218@11225084
+(POST && /api/dictionaries) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996c430@51470a6c
+(PUT && /api/dictionaries/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996c648@6d48f1c
+(DELETE && /api/dictionaries/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996c860@6f49a5f8
+(GET && /api/users) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996ca78@6c6c7ee5
+(GET && /api/users/page) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996cc90@5911d6e
+(GET && /api/users/count) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996cea8@271fdeaf
+(GET && /api/users/username/{username}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996d0c0@55bd9d1
+(GET && /api/users/check/username) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996d2d8@a4f14c9
+(GET && /api/users/check/email) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996d4f0@7b81283
+(POST && /api/users) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996d708@5e8b5e7d
+(GET && /api/users/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996d920@5c5812c
+(PUT && /api/users/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996db38@77de3401
+(DELETE && /api/users/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996dd50@30350c73
+(POST && /api/users/{id}/action/change-password) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996df68@34c9c4be
+(POST && /api/users/{id}/action/logical-delete) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996e180@1d2cffea
+(POST && /api/users/logical-delete) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996e398@1b54ae72
+(POST && /api/users/action/restore) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996e5b0@39a7fff
+(POST && /api/users/{id}/action/restore) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996e7c8@798ca706
+(GET && /api/users/{id}/roles) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996e9e0@3a5c472
+(POST && /api/users/{id}/roles) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996ebf8@3dbcdd4a
+(GET && /api/menus) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996ee10@6d093885
+(GET && /api/menus/tree) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996f028@712106b5
+(GET && /api/menus/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996f240@5c8adab7
+(POST && /api/menus) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996f458@7070ae1
+(PUT && /api/menus/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996f670@236ec794
+(DELETE && /api/menus/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996f888@5a0379e2
+(GET && /api/roles) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996faa0@50564f77
+(GET && /api/roles/page) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996fcb8@6fb9179e
+(GET && /api/roles/count) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996b000@3abe3f9a
+(GET && /api/roles/name/{roleName}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996b218@7b338d46
+(GET && /api/roles/check-name) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996b430@19fc48a0
+(GET && /api/roles/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996b648@3edea9e6
+(POST && /api/roles) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996b860@75d70348
+(PUT && /api/roles/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996ba78@12e9e909
+(DELETE && /api/roles/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996bc90@69ab1abc
+(POST && /api/roles/{id}/restore) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996a800@22f10ff6
+(GET && /api/roles/{id}/permissions) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996aa18@6ff185a3
+(POST && /api/roles/{id}/permissions) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221996ac30@320d98f5
+(GET && /api/config) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219970000@3c20af2
+(GET && /api/config/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219970218@29e63bc3
+(GET && /api/config/key/{configKey}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219970430@4a596fc1
+(POST && /api/config) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219970648@144d8c4f
+(PUT && /api/config/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219970860@7cb81ae
+(DELETE && /api/config/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219970a78@1f6b9ab7
+(GET && /api/logs/login) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219970c90@1e4dc9fc
+(GET && /api/logs/login/page) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219970ea8@661a018e
+(GET && /api/logs/login/count) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199710c0@3d872a12
+(GET && /api/logs/login/today/count) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199712d8@7efaa137
+(GET && /api/logs/login/recent) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199714f0@e08853d
+(GET && /api/logs/login/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219971708@3828f303
+(POST && /api/logs/login) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219971920@3c7b89ae
+(GET && /api/logs/exception) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219971b38@1723640f
+(GET && /api/logs/exception/page) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219971d50@5dc83abb
+(GET && /api/logs/exception/count) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219971f68@22876aea
+(GET && /api/logs/exception/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219972180@4251b1ed
+(POST && /api/logs/exception) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219972398@4edea6b4
+(GET && /api/logs/operation) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199725b0@52df4036
+(GET && /api/logs/operation/export) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199727c8@4b96b0bd
+(GET && /api/logs/operation/page) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199729e0@487befb
+(GET && /api/logs/operation/count) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219972bf8@3f5743d9
+(GET && /api/logs/operation/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219972e10@17a17af5
+(POST && /api/logs/operation) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219973028@694c23e4
+(POST && /api/auth/login) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219973240@50853850
+(POST && /api/auth/register) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219973458@55c9e012
+(POST && /api/auth/logout) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219973670@49235e8c
+(GET && /api/stats/overview) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219973888@4178074f
+(GET && /api/dict/types) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219973aa0@38c6f088
+(GET && /api/dict/types/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219973cb8@22955ef2
+(GET && /api/dict/types/type/{dictType}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219973ed0@7111b312
+(POST && /api/dict/types) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199740e8@70d28000
+(PUT && /api/dict/types/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219974300@7f1f6c22
+(DELETE && /api/dict/types/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219974518@77f5cffd
+(GET && /api/dict/data) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219974730@b807b32
+(GET && /api/dict/data/type/{dictType}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219974948@6f3628c2
+(GET && /api/dict/data/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219974b60@6bcc277a
+(POST && /api/dict/data) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219974d78@321f97d9
+(PUT && /api/dict/data/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219974f90@3a4e3343
+(DELETE && /api/dict/data/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199751a8@64e06cf0
+(GET && /api/notices) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199753c0@6e9d60e1
+(GET && /api/notices/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199755d8@207283b4
+(GET && /api/notices/status/{status}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199757f0@6b6d68d2
+(POST && /api/notices) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219975a08@c8d0ea3
+(PUT && /api/notices/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219975c20@912fdbb
+(DELETE && /api/notices/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219975e38@3d49b00e
+(GET && /api/messages/user/{userId}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219976050@3d62648d
+(GET && /api/messages/user/{userId}/page) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219976268@59fe8d94
+(GET && /api/messages/user/{userId}/unread) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219976480@479f5c35
+(GET && /api/messages/user/{userId}/unread/list) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219976698@37264d08
+(GET && /api/messages/user/{userId}/unread/page) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199768b0@194e116d
+(POST && /api/messages) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219976ac8@3471bdfd
+(PUT && /api/messages/{id}/read) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219976ce0@93231b2
+(PUT && /api/messages/user/{userId}/read) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219976ef8@cf97391
+(DELETE && /api/messages/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219977110@65d5de1a
+(GET && /api/files) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219977328@5cf21908
+(GET && /api/files/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219977540@44d51c85
+(POST && /api/files/upload) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219977758@3f78a75
+(GET && /api/files/{id}/download) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219977970@4819a11f
+(GET && /api/files/download/{fileName}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219977b88@46fdb4d
+(GET && /api/files/{id}/preview) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219977da0@68b2ce71
+(GET && /api/files/preview/{fileName}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219978000@57a94530
+(DELETE && /api/files/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219978218@52bd1783
+(GET && /api/permissions) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219978430@28f20b2a
+(GET && /api/permissions/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219978648@56cd8903
+(GET && /api/permissions/code/{code}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219978860@4bd0d62f
+(GET && /api/permissions/check-code) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219978a78@71ebd1d9
+(GET && /api/permissions/count) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219978c90@49f82f95
+(POST && /api/permissions) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219978ea8@73c72238
+(PUT && /api/permissions/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199790c0@3e6be3c1
+(DELETE && /api/permissions/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199792d8@112188cc
+(POST && /api/auth/phone/one-click-login) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199794f0@19ad99fa
+(POST && /api/auth/phone/send-code) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219979708@5331be15
+(POST && /api/auth/phone/code-login) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219979920@32a53a59
+(POST && /api/member/auth/miniapp/login) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219979b38@2f5fb329
+(GET && /api/member/auth/mp/callback) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219979d50@5ac88d71
+(POST && /api/member/auth/mp/callback) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219979f68@457512b
+(GET && /api/member/info) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997a180@1f3bd40a
+(PUT && /api/member/info) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997a398@4dafea3f
+(POST && /api/member/phone/bind) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997a5b0@531245fe
+(GET && /api/member/subscribe/status) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997a7c8@6d7298be
+(POST && /api/admin/member/{id}/phone) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997a9e0@2c289a9e
+(GET && /api/admin/member/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997abf8@1ee47336
+(PUT && /api/admin/member/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997ae10@5f18f8a1
+(GET && /api/admin/members) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997b028@26d63c94
+(GET && /api/admin/members/all) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997b240@4e42beba
+(GET && /api/member-cards/active) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997b458@73021987
+(GET && /api/member-cards/{memberCardId}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997b670@6bc72ab6
+(POST && /api/member-cards) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997b888@7ce299c6
+(POST && /api/member-card-records/purchase) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997baa0@278e5f8e
+(POST && /api/member-card-records/{recordId}/renew) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997bcb8@44a3b4d9
+(POST && /api/member-card-records/{recordId}/use) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997bed0@16e3a461
+(POST && /api/member-card-records/{recordId}/refund) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997c0e8@7e2723d2
+(GET && /api/member-card-records/my-cards/{memberId}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997c300@2b0061b7
+(GET && /api/member-card-records/{recordId}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997c518@72dc0d72
+(POST && /api/member-card-records/process-expired) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997c730@79176fe4
+(POST && /api/member-card-transactions) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997c948@3ee9eaa7
+(GET && /api/member-card-transactions) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997cb60@164c2914
+(GET && /api/member-card-transactions/member/{memberId}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997cd78@4690132
+(GET && /api/member-card-transactions/card/{cardId}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997cf90@46c33614
+(GET && /api/member-card-transactions/statistics/deduct/{cardId}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997d1a8@378e50b9
+(GET && /api/member-card-transactions/statistics/renew) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997d3c0@40fdcbd5
+(GET && /api/member-card-transactions/statistics/purchase/{memberId}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997d5d8@c512269
+(GET && /api/groupCourse/list) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997d7f0@537dc661
+(POST && /api/groupCourse/page) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997da08@16b35f2f
+(GET && /api/groupCourse/types) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997dc20@383e8d4f
+(GET && /api/groupCourse/types/search) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997de38@3df9b24b
+(GET && /api/groupCourse/types/categories) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997e050@a355b
+(GET && /api/groupCourse/types/category/{category}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997e268@c4c4852
+(GET && /api/groupCourse/types/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997e480@4054f861
+(POST && /api/groupCourse/types) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997e698@30cf61ca
+(PUT && /api/groupCourse/types/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997e8b0@61be2a65
+(DELETE && /api/groupCourse/types/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997eac8@f5ee0d8
+(GET && /api/groupCourse/labels) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997ece0@68ae86a2
+(GET && /api/groupCourse/labels/search) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997eef8@23247b4c
+(GET && /api/groupCourse/labels/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997f110@33d9904f
+(GET && /api/groupCourse/types/{typeId}/labels) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997f328@4b79eecc
+(POST && /api/groupCourse/labels) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997f540@57fefa97
+(PUT && /api/groupCourse/labels/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997f758@5700053f
+(DELETE && /api/groupCourse/labels/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997f970@28a4ac78
+(POST && /api/groupCourse/types/{typeId}/labels) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997fb88@2caf282e
+(DELETE && /api/groupCourse/types/{typeId}/labels/{labelId}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x000002221997fda0@ca5456e
+(DELETE && /api/groupCourse/types/{typeId}/labels) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219980000@5b5b9038
+(POST && /api/groupCourse/book) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219980218@56912047
+(POST && /api/groupCourse/booking/{bookingId}/cancel) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219980430@32a8ce3
+(GET && /api/groupCourse/bookings/member/{memberId}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219980648@258227dd
+(GET && /api/groupCourse/bookings/course/{courseId}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219980860@225fb905
+(GET && /api/groupCourse/bookings/{bookingId}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219980a78@7d932611
+(GET && /api/groupCourse/recommend/list) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219980c90@2a09e0d4
+(GET && /api/groupCourse/recommend/active) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219980ea8@1e019687
+(GET && /api/groupCourse/recommend/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199810c0@41c983d3
+(GET && /api/groupCourse/recommend/course/{courseId}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199812d8@250e9655
+(POST && /api/groupCourse/recommend) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199814f0@4064cd60
+(PUT && /api/groupCourse/recommend/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219981708@5243ce45
+(DELETE && /api/groupCourse/recommend/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219981920@5f1908c5
+(POST && /api/groupCourse/recommend/{id}/enable) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219981b38@e74914d
+(POST && /api/groupCourse/recommend/{id}/disable) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219981d50@689d522e
+(GET && /api/groupCourse/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219981f68@24e0989e
+(GET && /api/groupCourse/{id}/detail) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219982180@33a8f3e7
+(POST && /api/groupCourse) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219982398@19fce226
+(PUT && /api/groupCourse/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199825b0@1bf2cf15
+(DELETE && /api/groupCourse/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199827c8@3b76809d
+(POST && /api/groupCourse/{id}/cancel) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199829e0@4fb56bdf
+(POST && /api/groupCourse/signin/{memberId}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219982bf8@31a65ddd
+(POST && /api/groupCourse/search) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219982e10@3ca1a69b
+(POST && /api/checkIn) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219983028@33dcbbfa
+(GET && /api/checkIn/qrcode) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219983240@57d3c5cd
+(GET && /api/checkIn/records) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219983458@5418225f
+(GET && /api/checkIn/records/{id}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219983670@65bcc339
+(GET && /api/checkIn/statistics) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219983888@3369a71f
+(GET && /api/checkIn/daily-stats) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219983aa0@7adde112
+(GET && /api/checkIn/records/export) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219983cb8@6f422ecc
+(GET && /api/datacount/summary) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219983ed0@6c648d16
+(GET && /api/datacount/member) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199840e8@12cb9eda
+(GET && /api/datacount/booking) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219984300@70b38e0
+(GET && /api/datacount/signin) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219984518@79414283
+(GET && /api/datacount/history) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219984730@31857c80
+(GET && /api/datacount/export) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219984948@7ff61924
+(POST && /api/payment/create) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219984b60@2164f202
+(GET && /api/payment/{orderId}) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219984d78@5242e9f7
+(POST && /api/payment/{orderId}/refund) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x0000022219984f90@48b9ee43
+(POST && /api/payment/notify) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199851a8@7c9e149
+(POST && /api/payment/qrcode/create) -> cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199853c0@1bbf750
+2026-06-24T04:39:35.533+08:00 TRACE 7516 --- [gym-manage-api] [ main] o.s.w.r.f.s.s.RouterFunctionMapping : Mapped (GET && /api/exception-logs) -> cn.novalon.gym.manage.sys.config.ExceptionLogConfig$$Lambda/0x0000022219985a68@4473bbc1
+(GET && /api/exception-logs/{id}) -> cn.novalon.gym.manage.sys.config.ExceptionLogConfig$$Lambda/0x0000022219985c80@433f8efa
+(GET && /api/exception-logs/username/{username}) -> cn.novalon.gym.manage.sys.config.ExceptionLogConfig$$Lambda/0x0000022219985e98@bb4fc7d
+2026-06-24T04:39:35.678+08:00 TRACE 7516 --- [gym-manage-api] [ main] o.s.w.r.handler.SimpleUrlHandlerMapping : Mapped [/webjars/**] onto ResourceWebHandler [classpath [META-INF/resources/webjars/]]
+2026-06-24T04:39:35.688+08:00 TRACE 7516 --- [gym-manage-api] [ main] o.s.w.r.handler.SimpleUrlHandlerMapping : Mapped [/**] onto ResourceWebHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/]]
+2026-06-24T04:39:35.693+08:00 TRACE 7516 --- [gym-manage-api] [ main] o.s.w.r.handler.SimpleUrlHandlerMapping : Mapped [/swagger-ui/**] onto ResourceWebHandler [classpath [META-INF/resources/webjars/swagger-ui/5.32.0/]]
+2026-06-24T04:39:35.695+08:00 TRACE 7516 --- [gym-manage-api] [ main] o.s.w.r.handler.SimpleUrlHandlerMapping : Mapped [/swagger-ui/*swagger-initializer.js] onto ResourceWebHandler [classpath [META-INF/resources/webjars/swagger-ui/5.32.0/]]
+2026-06-24T04:39:35.696+08:00 TRACE 7516 --- [gym-manage-api] [ main] o.s.w.r.handler.SimpleUrlHandlerMapping : Mapped [/webjars/*swagger-ui/**] onto ResourceWebHandler [classpath [META-INF/resources/webjars/]]
+2026-06-24T04:39:35.697+08:00 TRACE 7516 --- [gym-manage-api] [ main] o.s.w.r.handler.SimpleUrlHandlerMapping : Mapped [/webjars/*swagger-ui/*swagger-initializer.js] onto ResourceWebHandler [classpath [META-INF/resources/webjars/]]
+2026-06-24T04:39:35.699+08:00 TRACE 7516 --- [gym-manage-api] [ main] o.s.w.r.handler.SimpleUrlHandlerMapping : Mapped [/webjars/*swagger-ui/5.32.0/*swagger-initializer.js] onto ResourceWebHandler [classpath [META-INF/resources/webjars/]]
+2026-06-24T04:39:35.703+08:00 DEBUG 7516 --- [gym-manage-api] [ main] o.s.w.r.handler.SimpleUrlHandlerMapping : Patterns [/webjars/**, /**, /swagger-ui/**, /swagger-ui/*swagger-initializer.js, /webjars/*swagger-ui/**, /webjars/*swagger-ui/*swagger-initializer.js, /webjars/*swagger-ui/5.32.0/*swagger-initializer.js] in 'resourceHandlerMapping'
+2026-06-24T04:39:35.738+08:00 TRACE 7516 --- [gym-manage-api] [ main] o.s.w.r.handler.SimpleUrlHandlerMapping : Mapped [/ws] onto cn.novalon.gym.manage.notify.websocket.SysWebSocketHandler@6a022b0e
+2026-06-24T04:39:35.743+08:00 DEBUG 7516 --- [gym-manage-api] [ main] o.s.w.r.handler.SimpleUrlHandlerMapping : Patterns [/ws] in 'webSocketHandlerMapping'
+2026-06-24T04:39:36.492+08:00 INFO 7516 --- [gym-manage-api] [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 5 endpoints beneath base path '/actuator'
+2026-06-24T04:39:36.651+08:00 DEBUG 7516 --- [gym-manage-api] [ main] o.s.w.r.r.m.a.ControllerMethodResolver : ControllerAdvice beans: 0 @ModelAttribute, 0 @InitBinder, 2 @ExceptionHandler
+2026-06-24T04:39:36.785+08:00 DEBUG 7516 --- [gym-manage-api] [ main] o.s.w.s.adapter.HttpWebHandlerAdapter : enableLoggingRequestDetails='false': form data and headers will be masked to prevent unsafe logging of potentially sensitive data
+2026-06-24T04:39:40.851+08:00 INFO 7516 --- [gym-manage-api] [ main] org.flywaydb.core.FlywayExecutor : Database: jdbc:postgresql://localhost:55432/manage_system (PostgreSQL 18.4)
+2026-06-24T04:39:40.880+08:00 WARN 7516 --- [gym-manage-api] [ main] o.f.c.internal.database.base.Database : Flyway upgrade recommended: PostgreSQL 18.4 is newer than this version of Flyway and support has not been tested. The latest supported version of PostgreSQL is 17.
+2026-06-24T04:39:41.149+08:00 INFO 7516 --- [gym-manage-api] [ main] o.f.core.internal.command.DbMigrate : Current version of schema "public": 24
+2026-06-24T04:39:41.162+08:00 INFO 7516 --- [gym-manage-api] [ main] o.f.core.internal.command.DbMigrate : Schema "public" is up to date. No migration necessary.
+2026-06-24T04:39:42.920+08:00 INFO 7516 --- [gym-manage-api] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8084 (http)
+2026-06-24T04:39:42.962+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.gym.manage.app.ManageApplication : Started ManageApplication in 44.04 seconds (process running for 46.777)
+2026-06-24T04:39:43.276+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.g.m.g.initializer.QrCodeInitializer : ===== 开始检查团课二维码缺失情况 =====
+2026-06-24T04:39:43.542+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.gym.manage.app.ManageApplication : === 检查 IOperationLogService ===
+2026-06-24T04:39:43.543+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.gym.manage.app.ManageApplication : IOperationLogService 实现: cn.novalon.gym.manage.sys.core.service.impl.OperationLogService
+2026-06-24T04:39:43.545+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.gym.manage.app.ManageApplication : === 检查已注册的 WebFilter ===
+2026-06-24T04:39:43.546+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.gym.manage.app.ManageApplication : WebFilter 总数: 4
+2026-06-24T04:39:43.548+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.gym.manage.app.ManageApplication : - org.springframework.security.web.server.WebFilterChainProxy (Order: 无)
+2026-06-24T04:39:43.548+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.gym.manage.app.ManageApplication : - cn.novalon.gym.manage.sys.audit.OperationLogWebFilter (Order: 2147483647)
+2026-06-24T04:39:43.549+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.gym.manage.app.ManageApplication : - cn.novalon.gym.manage.sys.filter.RateLimitFilter (Order: 无)
+2026-06-24T04:39:43.550+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.gym.manage.app.ManageApplication : - cn.novalon.gym.manage.sys.security.JwtAuthenticationFilter (Order: 无)
+2026-06-24T04:39:43.557+08:00 INFO 7516 --- [gym-manage-api] [ main] c.n.gym.manage.app.ManageApplication : 应用程序启动完成
+2026-06-24T04:39:45.053+08:00 DEBUG 7516 --- [gym-manage-api] [actor-tcp-nio-1] o.s.r2dbc.core.DefaultDatabaseClient : Executing SQL statement [SELECT group_course.course_name, group_course.coach_id, group_course.course_type, group_course.start_time, group_course.end_time, group_course.max_members, group_course.current_members, group_course.status, group_course.location, group_course.cover_image, group_course.description, group_course.point_card_amount, group_course.stored_value_amount, group_course.qr_code_path, group_course.id, group_course.create_by, group_course.update_by, group_course.created_at, group_course.updated_at, group_course.deleted_at FROM group_course WHERE group_course.deleted_at IS NULL]
+2026-06-24T04:39:46.087+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.113+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.127+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.131+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.135+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.141+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.149+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.154+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.157+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.159+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.165+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.169+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.187+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.200+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.210+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.214+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.218+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.221+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.226+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.236+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.240+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.243+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.250+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.257+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.259+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.262+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.c.GroupCourseConverter : 转换bean,entity-domain:
+2026-06-24T04:39:46.310+08:00 INFO 7516 --- [gym-manage-api] [actor-tcp-nio-1] c.n.g.m.g.initializer.QrCodeInitializer : ===== 团课二维码检查完毕,共补全 0 个缺失二维码 =====
+2026-06-24T04:40:30.204+08:00 TRACE 7516 --- [gym-manage-api] [nio-8084-exec-1] o.s.w.s.adapter.HttpWebHandlerAdapter : [5a95aafc] HTTP POST "/api/payment/qrcode/create", headers={masked}
+2026-06-24T04:40:30.859+08:00 DEBUG 7516 --- [gym-manage-api] [ parallel-2] o.s.w.s.s.DefaultWebSessionManager : Created new WebSession.
+2026-06-24T04:40:30.919+08:00 INFO 7516 --- [gym-manage-api] [ parallel-2] c.n.g.m.sys.audit.OperationLogWebFilter : WebFilter 拦截请求: POST /api/payment/qrcode/create
+2026-06-24T04:40:30.921+08:00 INFO 7516 --- [gym-manage-api] [ parallel-2] c.n.g.m.sys.audit.OperationLogWebFilter : 未匹配到操作日志配置,跳过: POST /api/payment/qrcode/create
+2026-06-24T04:40:30.945+08:00 INFO 7516 --- [gym-manage-api] [ parallel-2] c.n.g.m.sys.audit.OperationLogWebFilter : WebFilter 拦截请求: POST /api/payment/qrcode/create
+2026-06-24T04:40:30.956+08:00 INFO 7516 --- [gym-manage-api] [ parallel-2] c.n.g.m.sys.audit.OperationLogWebFilter : 未匹配到操作日志配置,跳过: POST /api/payment/qrcode/create
+2026-06-24T04:40:31.063+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.069+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.070+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.072+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.073+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.073+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.078+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/dictionaries" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.079+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
+2026-06-24T04:40:31.081+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
+2026-06-24T04:40:31.083+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.084+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.091+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.092+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.093+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.094+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.095+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.096+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/users" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.096+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.101+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
+2026-06-24T04:40:31.105+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
+2026-06-24T04:40:31.107+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.108+08:00 TRACE 7516 --- [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/payment/qrcode/create"
+2026-06-24T04:40:31.109+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.110+08:00 TRACE 7516 --- [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/payment/qrcode/create"
+2026-06-24T04:40:31.111+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.120+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/users/logical-delete" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.134+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.137+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/users/action/restore" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.138+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.142+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/users/{id}/action/restore" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.145+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.148+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.148+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/users/{id}/roles" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.151+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.154+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.166+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.171+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.174+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/menus" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.175+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
+2026-06-24T04:40:31.176+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
+2026-06-24T04:40:31.177+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.177+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.179+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.180+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.180+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.181+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.182+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.183+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/roles" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.197+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
+2026-06-24T04:40:31.221+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
+2026-06-24T04:40:31.227+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.231+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/roles/{id}/restore" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.233+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.234+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.235+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/roles/{id}/permissions" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.236+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.236+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.237+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.239+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.239+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/config" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.243+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
+2026-06-24T04:40:31.245+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
+2026-06-24T04:40:31.246+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.247+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.248+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.249+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.250+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.251+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.252+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.252+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/logs/login" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.267+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.270+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.271+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.273+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.274+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.283+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/logs/exception" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.284+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.286+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.287+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.291+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.295+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.297+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.298+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/logs/operation" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.299+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.303+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/auth/login" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.305+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.306+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/auth/register" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.307+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.309+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/auth/logout" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.310+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.314+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.315+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.315+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.315+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.317+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/dict/types" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.318+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
+2026-06-24T04:40:31.322+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
+2026-06-24T04:40:31.333+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.336+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.340+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.342+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.345+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/dict/data" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.346+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
+2026-06-24T04:40:31.364+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
+2026-06-24T04:40:31.367+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.370+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.375+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.379+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.380+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/notices" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.382+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
+2026-06-24T04:40:31.383+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
+2026-06-24T04:40:31.384+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.392+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.394+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.397+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.399+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.400+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.407+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/messages" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.409+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
+2026-06-24T04:40:31.411+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
+2026-06-24T04:40:31.416+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
+2026-06-24T04:40:31.423+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.430+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.439+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.443+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/files/upload" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.444+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.452+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.455+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.455+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.456+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
+2026-06-24T04:40:31.457+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.459+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.469+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.478+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.485+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.488+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.489+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/permissions" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.492+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
+2026-06-24T04:40:31.499+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
+2026-06-24T04:40:31.505+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.510+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/auth/phone/one-click-login" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.515+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.522+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/auth/phone/send-code" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.523+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.525+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/auth/phone/code-login" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.526+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.528+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/member/auth/miniapp/login" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.535+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.537+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.539+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/member/auth/mp/callback" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.541+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.550+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
+2026-06-24T04:40:31.555+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.557+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/member/phone/bind" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.569+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.570+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.573+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/admin/member/{id}/phone" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.585+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.591+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
+2026-06-24T04:40:31.595+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.596+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.598+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.603+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.608+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.608+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/member-cards" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.609+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.609+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/member-card-records/purchase" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.610+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.614+08:00 TRACE 7516 --- [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/payment/qrcode/create"
+2026-06-24T04:40:31.614+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.616+08:00 TRACE 7516 --- [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/payment/qrcode/create"
+2026-06-24T04:40:31.618+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.619+08:00 TRACE 7516 --- [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/payment/qrcode/create"
+2026-06-24T04:40:31.621+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.625+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.629+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.630+08:00 TRACE 7516 --- [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/payment/qrcode/create"
+2026-06-24T04:40:31.631+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.632+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/member-card-transactions" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.633+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.634+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.634+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.639+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.640+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.640+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.642+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.642+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.644+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/groupCourse/page" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.644+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.644+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.645+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.646+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.646+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.647+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.647+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/groupCourse/types" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.654+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
+2026-06-24T04:40:31.657+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
+2026-06-24T04:40:31.658+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.659+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.661+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.667+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.671+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.673+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/groupCourse/labels" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.674+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
+2026-06-24T04:40:31.674+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
+2026-06-24T04:40:31.676+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.678+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/groupCourse/types/{typeId}/labels" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.679+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
+2026-06-24T04:40:31.679+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
+2026-06-24T04:40:31.682+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.683+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/groupCourse/book" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.683+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.685+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/groupCourse/booking/{bookingId}/cancel" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.690+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.692+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.694+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.696+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.697+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.697+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.697+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.703+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.705+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/groupCourse/recommend" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.705+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
+2026-06-24T04:40:31.706+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
+2026-06-24T04:40:31.709+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.710+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/groupCourse/recommend/{id}/enable" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.717+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.719+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/groupCourse/recommend/{id}/disable" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.720+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.722+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.723+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.724+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/groupCourse" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.725+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "PUT" does not match against value "POST"
+2026-06-24T04:40:31.731+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "DELETE" does not match against value "POST"
+2026-06-24T04:40:31.732+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.733+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/groupCourse/{id}/cancel" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.733+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.734+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/groupCourse/signin/{memberId}" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.740+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.741+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/groupCourse/search" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.743+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.744+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/checkIn" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.745+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.747+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.753+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.754+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.755+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.755+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.756+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.757+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.758+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.766+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.772+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.774+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.780+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.785+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/payment/create" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.786+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "GET" does not match against value "POST"
+2026-06-24T04:40:31.788+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.795+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/payment/{orderId}/refund" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.797+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.799+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/payment/notify" does not match against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.799+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Method "POST" matches against value "POST"
+2026-06-24T04:40:31.804+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.server.RequestPredicates : Pattern "/api/payment/qrcode/create" matches against value "/api/payment/qrcode/create"
+2026-06-24T04:40:31.829+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.function.server.RouterFunctions : [5a95aafc] Matched (POST && /api/payment/qrcode/create)
+2026-06-24T04:40:31.861+08:00 DEBUG 7516 --- [gym-manage-api] [ parallel-2] o.s.w.r.f.s.s.RouterFunctionMapping : [5a95aafc] Mapped to cn.novalon.gym.manage.app.config.SystemRouter$$Lambda/0x00000222199853c0@1bbf750
+2026-06-24T04:40:32.330+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] org.springframework.web.HttpLogging : [5a95aafc] Decoded [CreatePaymentRequest(memberId=null, orderType=MEMBER_CARD, goodsDesc=购买月卡, transAmt=100, tradeType=QRCODE, remark=会员卡类型ID:1, acctId=null, timeExpire=null, delayAcctFlag=null, feeFlag=null, limitPayType=null, channelNo=null, payScene=null, notifyUrl=null)]
+2026-06-24T04:40:32.331+08:00 INFO 7516 --- [gym-manage-api] [ parallel-2] c.n.g.m.payment.handler.PaymentHandler : [Payment] 创建扫码支付订单: memberId=null, orderType=MEMBER_CARD, goodsDesc=购买月卡, transAmt=100
+2026-06-24T04:40:32.359+08:00 INFO 7516 --- [gym-manage-api] [ parallel-2] c.n.g.m.p.s.impl.PaymentServiceImpl : [Alipay] 扫码支付请求: orderId=20260624044032d77433b4, bizContent={"out_trade_no":"20260624044032d77433b4","total_amount":"0.01","subject":"购买月卡","timeout_express":"30m"}
+2026-06-24T04:40:38.905+08:00 ERROR 7516 --- [gym-manage-api] [ parallel-2] sdk.comm.err : 2026-06-24 04:40:38^_^alipay.trade.precreate^_^null^_^172.27.254.144^_^Windows Server 2022^_^alipay-sdk-java-4.35.120.ALL^_^https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=UTF-8&method=alipay.trade.precreate&sign=JpHFUFyV8EXmvqxnUceUpXjuIojp0Q00xLkKTMFB0sLV%2FPJwJc4W7gO7xIp%2BllaLNb5AO1hAz90OA4d59HHR%2FLhZYWpYbDoYZAE8N6025eqFI3X8QcoiCcg3DYXKRNOJ5UKSxbcNnn%2BmRXB%2BBJU9KNkQqlkbmVzAmuaCg%2FDbRT4CYSQYgPs%2FuWYN8LAtarFI6ZuR97O99NR6kpLt%2BZQrWezDEq4NVL%2FMGPIrFkvvVUmry0NOL%2FxkIK0SQRouOTn9qkkW7NJMwevFH%2F99BJNMaTYx9pmec9yULECtkvCSU4KDcnUNTY7mSaS09HmJpBB9mvVLkBFlBCXusYMxij8cNQ%3D%3D¬ify_url=http%3A%2F%2Flocalhost%3A8084%2Fapi%2Fpayment%2Fnotify&version=1.0&app_id=9021000164699238&sign_type=RSA2×tamp=2026-06-24+04%3A40%3A32&alipay_sdk=alipay-sdk-java-4.35.120.ALL&format=json^_^^_^ 504 Gateway Time-out 504 Gateway Time-out
The gateway did not receive a timely response from the upstream server or application. Sorry for the inconvenience.
Please report this message and include the following information to us.
Thank you very much!
| URL: | http://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=UTF-8&method=alipay.trade.precreate&sign=JpHFUFyV8EXmvqxnUceUpXjuIojp0Q00xLkKTMFB0sLV%2FPJwJc4W7gO7xIp%2BllaLNb5AO1hAz90OA4d59HHR%2FLhZYWpYbDoYZAE8N6025eqFI3X8QcoiCcg3DYXKRNOJ5UKSxbcNnn%2BmRXB%2BBJU9KNkQqlkbmVzAmuaCg%2FDbRT4CYSQYgPs%2FuWYN8LAtarFI6ZuR97O99NR6kpLt%2BZQrWezDEq4NVL%2FMGPIrFkvvVUmry0NOL%2FxkIK0SQRouOTn9qkkW7NJMwevFH%2F99BJNMaTYx9pmec9yULECtkvCSU4KDcnUNTY7mSaS09HmJpBB9mvVLkBFlBCXusYMxij8cNQ%3D%3D¬ify_url=http%3A%2F%2Flocalhost%3A8084%2Fapi%2Fpayment%2Fnotify&version=1.0&app_id=9021000164699238&sign_type=RSA2×tamp=2026-06-24+04%3A40%3A32&alipay_sdk=alipay-sdk-java-4.35.120.ALL&format=json |
| Server: | openapi-59.rz21a.sandbox.alipay.net |
| Date: | 2026/06/24 04:40:38 |
Powered by Tengine
+2026-06-24T04:40:38.907+08:00 ERROR 7516 --- [gym-manage-api] [ parallel-2] c.n.g.m.p.s.impl.PaymentServiceImpl : [Alipay] 扫码支付异常: orderId=20260624044032d77433b4
+
+com.alipay.api.AlipayApiException: java.io.IOException:
+
+504 Gateway Time-out
+
+504 Gateway Time-out
+The gateway did not receive a timely response from the upstream server or application. Sorry for the inconvenience.
+Please report this message and include the following information to us.
+Thank you very much!
+
+
+| URL: |
+http://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=UTF-8&method=alipay.trade.precreate&sign=JpHFUFyV8EXmvqxnUceUpXjuIojp0Q00xLkKTMFB0sLV%2FPJwJc4W7gO7xIp%2BllaLNb5AO1hAz90OA4d59HHR%2FLhZYWpYbDoYZAE8N6025eqFI3X8QcoiCcg3DYXKRNOJ5UKSxbcNnn%2BmRXB%2BBJU9KNkQqlkbmVzAmuaCg%2FDbRT4CYSQYgPs%2FuWYN8LAtarFI6ZuR97O99NR6kpLt%2BZQrWezDEq4NVL%2FMGPIrFkvvVUmry0NOL%2FxkIK0SQRouOTn9qkkW7NJMwevFH%2F99BJNMaTYx9pmec9yULECtkvCSU4KDcnUNTY7mSaS09HmJpBB9mvVLkBFlBCXusYMxij8cNQ%3D%3D¬ify_url=http%3A%2F%2Flocalhost%3A8084%2Fapi%2Fpayment%2Fnotify&version=1.0&app_id=9021000164699238&sign_type=RSA2×tamp=2026-06-24+04%3A40%3A32&alipay_sdk=alipay-sdk-java-4.35.120.ALL&format=json |
+
+
+| Server: |
+openapi-59.rz21a.sandbox.alipay.net |
+
+
+| Date: |
+2026/06/24 04:40:38 |
+
+
+
Powered by Tengine
+
+
+
+ at com.alipay.api.AbstractAlipayClient.doPost(AbstractAlipayClient.java:1014) ~[alipay-sdk-java-4.35.120.ALL.jar!/:na]
+ at com.alipay.api.AbstractAlipayClient._execute(AbstractAlipayClient.java:921) ~[alipay-sdk-java-4.35.120.ALL.jar!/:na]
+ at com.alipay.api.AbstractAlipayClient.execute(AbstractAlipayClient.java:395) ~[alipay-sdk-java-4.35.120.ALL.jar!/:na]
+ at com.alipay.api.AbstractAlipayClient.execute(AbstractAlipayClient.java:377) ~[alipay-sdk-java-4.35.120.ALL.jar!/:na]
+ at com.alipay.api.AbstractAlipayClient.execute(AbstractAlipayClient.java:372) ~[alipay-sdk-java-4.35.120.ALL.jar!/:na]
+ at com.alipay.api.AbstractAlipayClient.execute(AbstractAlipayClient.java:366) ~[alipay-sdk-java-4.35.120.ALL.jar!/:na]
+ at cn.novalon.gym.manage.payment.service.impl.PaymentServiceImpl.alipayQrCodePay(PaymentServiceImpl.java:196) ~[gym-payment-1.0.0.jar!/:1.0.0]
+ at cn.novalon.gym.manage.payment.handler.PaymentHandler.lambda$createQrCodePayment$2(PaymentHandler.java:65) ~[gym-payment-1.0.0.jar!/:1.0.0]
+ at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:132) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:299) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onNext(FluxFilterFuseable.java:337) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Operators$BaseFluxToMonoOperator.completePossiblyEmpty(Operators.java:2096) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:145) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at org.springframework.http.server.reactive.AbstractListenerReadPublisher$State.onAllDataRead(AbstractListenerReadPublisher.java:501) ~[spring-web-6.2.17.jar!/:6.2.17]
+ at org.springframework.http.server.reactive.AbstractListenerReadPublisher.handlePendingCompletionOrError(AbstractListenerReadPublisher.java:249) ~[spring-web-6.2.17.jar!/:6.2.17]
+ at org.springframework.http.server.reactive.AbstractListenerReadPublisher$State$4.onDataAvailable(AbstractListenerReadPublisher.java:404) ~[spring-web-6.2.17.jar!/:6.2.17]
+ at org.springframework.http.server.reactive.AbstractListenerReadPublisher.onDataAvailable(AbstractListenerReadPublisher.java:125) ~[spring-web-6.2.17.jar!/:6.2.17]
+ at org.springframework.http.server.reactive.ServletServerHttpRequest$RequestBodyPublisher.checkOnDataAvailable(ServletServerHttpRequest.java:347) ~[spring-web-6.2.17.jar!/:6.2.17]
+ at org.springframework.http.server.reactive.AbstractListenerReadPublisher.changeToDemandState(AbstractListenerReadPublisher.java:239) ~[spring-web-6.2.17.jar!/:6.2.17]
+ at org.springframework.http.server.reactive.AbstractListenerReadPublisher$State$2.request(AbstractListenerReadPublisher.java:354) ~[spring-web-6.2.17.jar!/:6.2.17]
+ at org.springframework.http.server.reactive.AbstractListenerReadPublisher$ReadSubscription.request(AbstractListenerReadPublisher.java:280) ~[spring-web-6.2.17.jar!/:6.2.17]
+ at reactor.core.publisher.Operators$BaseFluxToMonoOperator.request(Operators.java:2066) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.request(FluxFilterFuseable.java:411) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.request(FluxMapFuseable.java:360) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.request(FluxContextWrite.java:136) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2366) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onSubscribe(FluxOnErrorResume.java:74) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:117) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onSubscribe(FluxContextWrite.java:101) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onSubscribe(FluxMapFuseable.java:265) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onSubscribe(FluxFilterFuseable.java:305) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Operators$BaseFluxToMonoOperator.onSubscribe(Operators.java:2050) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at org.springframework.http.server.reactive.AbstractListenerReadPublisher$State$1.subscribe(AbstractListenerReadPublisher.java:322) ~[spring-web-6.2.17.jar!/:6.2.17]
+ at org.springframework.http.server.reactive.AbstractListenerReadPublisher.subscribe(AbstractListenerReadPublisher.java:112) ~[spring-web-6.2.17.jar!/:6.2.17]
+ at reactor.core.publisher.FluxSource.subscribe(FluxSource.java:71) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:55) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.innerNext(FluxConcatMapNoPrefetch.java:259) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:865) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:200) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxConcatArray$ConcatArraySubscriber.onNext(FluxConcatArray.java:180) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.innerNext(FluxConcatMapNoPrefetch.java:259) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:865) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxConcatMap$WeakScalarSubscription.request(FluxConcatMap.java:480) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.request(Operators.java:2330) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.request(FluxConcatMapNoPrefetch.java:339) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoNext$NextSubscriber.request(MonoNext.java:108) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxConcatArray$ConcatArraySubscriber.request(FluxConcatArray.java:256) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoNext$NextSubscriber.request(MonoNext.java:108) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxMap$MapSubscriber.request(FluxMap.java:164) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxPeek$PeekSubscriber.request(FluxPeek.java:138) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxMap$MapSubscriber.request(FluxMap.java:164) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.request(Operators.java:2330) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.request(FluxConcatMapNoPrefetch.java:339) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoNext$NextSubscriber.request(MonoNext.java:108) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2366) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2240) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoNext$NextSubscriber.onSubscribe(MonoNext.java:70) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.onSubscribe(FluxConcatMapNoPrefetch.java:164) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:201) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:83) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Mono.subscribe(Mono.java:4576) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:205) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoFlatMap.subscribeOrReturn(MonoFlatMap.java:53) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:63) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:55) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Mono.subscribe(Mono.java:4576) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:82) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onComplete(MonoPeekTerminal.java:299) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onComplete(MonoPeekTerminal.java:299) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:155) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFilter$FilterSubscriber.onNext(FluxFilter.java:113) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxPeekFuseable$PeekFuseableConditionalSubscriber.onNext(FluxPeekFuseable.java:503) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:122) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.innerNext(FluxConcatMapNoPrefetch.java:259) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:865) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFilter$FilterSubscriber.onNext(FluxFilter.java:113) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxPeekFuseable$PeekConditionalSubscriber.onNext(FluxPeekFuseable.java:854) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFilter$FilterSubscriber.onNext(FluxFilter.java:113) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFlatMap$FlatMapMain.tryEmitScalar(FluxFlatMap.java:492) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFlatMap$FlatMapMain.onNext(FluxFlatMap.java:424) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onNext(FluxPeekFuseable.java:210) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:335) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:294) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.request(FluxPeekFuseable.java:144) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFlatMap$FlatMapMain.onSubscribe(FluxFlatMap.java:373) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onSubscribe(FluxPeekFuseable.java:178) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:201) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:83) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Mono.subscribe(Mono.java:4576) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.onNext(FluxConcatMapNoPrefetch.java:207) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:335) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:294) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.innerComplete(FluxConcatMapNoPrefetch.java:275) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onComplete(FluxConcatMap.java:889) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoFlatMap$FlatMapMain.onComplete(MonoFlatMap.java:189) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:166) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxPeekFuseable$PeekConditionalSubscriber.onComplete(FluxPeekFuseable.java:940) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:85) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2572) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2366) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2240) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Mono.subscribe(Mono.java:4576) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:82) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:102) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:166) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFlatMap$FlatMapMain.checkTerminated(FluxFlatMap.java:850) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFlatMap$FlatMapMain.drainLoop(FluxFlatMap.java:612) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFlatMap$FlatMapMain.drain(FluxFlatMap.java:592) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFlatMap$FlatMapMain.request(FluxFlatMap.java:349) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFilter$FilterSubscriber.request(FluxFilter.java:186) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoNext$NextSubscriber.request(MonoNext.java:108) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.request(Operators.java:2330) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxPeekFuseable$PeekConditionalSubscriber.request(FluxPeekFuseable.java:783) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFilter$FilterSubscriber.request(FluxFilter.java:186) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxMap$MapSubscriber.request(FluxMap.java:164) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.request(Operators.java:2330) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.request(FluxConcatMapNoPrefetch.java:339) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoNext$NextSubscriber.request(MonoNext.java:108) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.request(FluxDefaultIfEmpty.java:98) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.request(MonoPeekTerminal.java:139) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxPeekFuseable$PeekFuseableConditionalSubscriber.request(FluxPeekFuseable.java:437) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.request(MonoPeekTerminal.java:139) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFilter$FilterSubscriber.request(FluxFilter.java:186) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2366) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2240) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFilter$FilterSubscriber.onSubscribe(FluxFilter.java:85) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onSubscribe(MonoPeekTerminal.java:152) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxPeekFuseable$PeekFuseableConditionalSubscriber.onSubscribe(FluxPeekFuseable.java:471) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onSubscribe(MonoPeekTerminal.java:152) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Operators$BaseFluxToMonoOperator.onSubscribe(Operators.java:2050) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoNext$NextSubscriber.onSubscribe(MonoNext.java:70) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.onSubscribe(FluxConcatMapNoPrefetch.java:164) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:201) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:83) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:55) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Mono.subscribe(Mono.java:4576) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:265) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Mono.subscribe(Mono.java:4576) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:82) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:166) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxPeekFuseable$PeekConditionalSubscriber.onComplete(FluxPeekFuseable.java:940) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:85) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2572) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2366) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2240) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Mono.subscribe(Mono.java:4576) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:82) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:102) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:166) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFlatMap$FlatMapMain.checkTerminated(FluxFlatMap.java:850) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFlatMap$FlatMapMain.drainLoop(FluxFlatMap.java:612) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFlatMap$FlatMapMain.drain(FluxFlatMap.java:592) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFlatMap$FlatMapMain.onComplete(FluxFlatMap.java:469) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onComplete(FluxPeekFuseable.java:277) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:357) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:294) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.request(FluxPeekFuseable.java:144) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFlatMap$FlatMapMain.onSubscribe(FluxFlatMap.java:373) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onSubscribe(FluxPeekFuseable.java:178) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:201) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:83) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Operators$BaseFluxToMonoOperator.completePossiblyEmpty(Operators.java:2096) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:134) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:166) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxMap$MapConditionalSubscriber.onComplete(FluxMap.java:275) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1865) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoCacheTime$CoordinatorSubscriber.signalCached(MonoCacheTime.java:337) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoCacheTime$CoordinatorSubscriber.onNext(MonoCacheTime.java:354) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:200) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onNext(FluxPeekFuseable.java:210) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.publisher.MonoPublishOn$PublishOnSubscriber.run(MonoPublishOn.java:181) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28) ~[reactor-core-3.7.17.jar!/:3.7.17]
+ at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) ~[na:na]
+ at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
+ at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[na:na]
+ at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
+ at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
+Caused by: java.io.IOException:
+
+504 Gateway Time-out
+
+504 Gateway Time-out
+The gateway did not receive a timely response from the upstream server or application. Sorry for the inconvenience.
+Please report this message and include the following information to us.
+Thank you very much!
+
+
+| URL: |
+http://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=UTF-8&method=alipay.trade.precreate&sign=JpHFUFyV8EXmvqxnUceUpXjuIojp0Q00xLkKTMFB0sLV%2FPJwJc4W7gO7xIp%2BllaLNb5AO1hAz90OA4d59HHR%2FLhZYWpYbDoYZAE8N6025eqFI3X8QcoiCcg3DYXKRNOJ5UKSxbcNnn%2BmRXB%2BBJU9KNkQqlkbmVzAmuaCg%2FDbRT4CYSQYgPs%2FuWYN8LAtarFI6ZuR97O99NR6kpLt%2BZQrWezDEq4NVL%2FMGPIrFkvvVUmry0NOL%2FxkIK0SQRouOTn9qkkW7NJMwevFH%2F99BJNMaTYx9pmec9yULECtkvCSU4KDcnUNTY7mSaS09HmJpBB9mvVLkBFlBCXusYMxij8cNQ%3D%3D¬ify_url=http%3A%2F%2Flocalhost%3A8084%2Fapi%2Fpayment%2Fnotify&version=1.0&app_id=9021000164699238&sign_type=RSA2×tamp=2026-06-24+04%3A40%3A32&alipay_sdk=alipay-sdk-java-4.35.120.ALL&format=json |
+
+
+| Server: |
+openapi-59.rz21a.sandbox.alipay.net |
+
+
+| Date: |
+2026/06/24 04:40:38 |
+
+
+
Powered by Tengine
+
+
+
+ at com.alipay.api.internal.util.WebUtils.getResponseAsString(WebUtils.java:520) ~[alipay-sdk-java-4.35.120.ALL.jar!/:na]
+ at com.alipay.api.internal.util.WebUtils.doPost(WebUtils.java:181) ~[alipay-sdk-java-4.35.120.ALL.jar!/:na]
+ at com.alipay.api.internal.util.WebUtils.doPost(WebUtils.java:142) ~[alipay-sdk-java-4.35.120.ALL.jar!/:na]
+ at com.alipay.api.AbstractAlipayClient.doPost(AbstractAlipayClient.java:1002) ~[alipay-sdk-java-4.35.120.ALL.jar!/:na]
+ ... 228 common frames omitted
+
+2026-06-24T04:40:39.163+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] org.springframework.web.HttpLogging : [5a95aafc] Encoding [ApiResponse(code=500, message=null, data=null)]
+2026-06-24T04:40:39.370+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] o.s.w.s.adapter.HttpWebHandlerAdapter : [5a95aafc] Completed 200 OK, headers={masked}
+2026-06-24T04:40:39.374+08:00 TRACE 7516 --- [gym-manage-api] [ parallel-2] org.springframework.web.HttpLogging : [5a95aafc] onComplete
diff --git a/gym-manage-api/manage-app/pom.xml b/gym-manage-api/manage-app/pom.xml
index 6e95ea5..beeccbe 100644
--- a/gym-manage-api/manage-app/pom.xml
+++ b/gym-manage-api/manage-app/pom.xml
@@ -48,6 +48,11 @@
gym-checkIn
${project.version}
+
+ cn.novalon.gym.manage
+ gym-payment
+ ${project.version}
+
cn.novalon.gym.manage
gym-dataCount
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 482d931..9ec7cd4 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
@@ -17,6 +17,7 @@ import cn.novalon.gym.manage.member.handler.MemberHandler;
import cn.novalon.gym.manage.member.handler.WechatAuthHandler;
import cn.novalon.gym.manage.notify.handler.SysNoticeHandler;
import cn.novalon.gym.manage.notify.handler.SysUserMessageHandler;
+import cn.novalon.gym.manage.payment.handler.PaymentHandler;
import cn.novalon.gym.manage.sys.handler.auth.PasswordDiagnosticHandler;
import cn.novalon.gym.manage.sys.handler.auth.SysAuthHandler;
import cn.novalon.gym.manage.sys.handler.config.SysConfigHandler;
@@ -78,7 +79,8 @@ public class SystemRouter {
CourseLabelHandler courseLabelHandler,
CheckInHandler checkInHandler,
DataStatisticsHandler dataStatisticsHandler,
- PhoneAuthHandler phoneAuthHandler) {
+ PhoneAuthHandler phoneAuthHandler,
+ PaymentHandler paymentHandler) {
return route()
// ========== 诊断路由 ==========
@@ -360,6 +362,19 @@ public class SystemRouter {
.GET("/api/datacount/history", dataStatisticsHandler::queryHistoricalStatistics)
.GET("/api/datacount/export", dataStatisticsHandler::exportStatistics)
+ // ========================================
+ // ========== 支付模块路由 =================
+ // ========================================
+
+ // ===== 支付宝App支付 =====
+ .POST("/api/payment/create", paymentHandler::createPayment)
+ .GET("/api/payment/{orderId}", paymentHandler::getPaymentStatus)
+ .POST("/api/payment/{orderId}/refund", paymentHandler::refundPayment)
+ .POST("/api/payment/notify", paymentHandler::alipayNotify)
+
+ // ===== 支付宝扫码支付 =====
+ .POST("/api/payment/qrcode/create", paymentHandler::createQrCodePayment)
+
.build();
}
}
diff --git a/gym-manage-api/manage-app/src/main/resources/application-dev.yml b/gym-manage-api/manage-app/src/main/resources/application-dev.yml
index 46b6a66..c696a05 100644
--- a/gym-manage-api/manage-app/src/main/resources/application-dev.yml
+++ b/gym-manage-api/manage-app/src/main/resources/application-dev.yml
@@ -2,9 +2,9 @@ spring:
cache:
type: none
r2dbc:
- url: r2dbc:postgresql://localhost:5432/manage_system
- username: postgres
- password: 123456
+ url: r2dbc:postgresql://localhost:55432/manage_system
+ username: novalon
+ password: novalon123
pool:
initial-size: 5
max-size: 20
@@ -12,13 +12,13 @@ spring:
max-life-time: 30m
acquire-timeout: 3s
flyway:
- url: jdbc:postgresql://localhost:5432/manage_system
- user: postgres
- password: 123456
- enabled: false
+ url: jdbc:postgresql://localhost:55432/manage_system
+ user: novalon
+ password: novalon123
+ enabled: true
locations: classpath:db/migration
baseline-on-migrate: true
- validate-on-migrate: true
+ validate-on-migrate: false
jwt:
diff --git a/gym-manage-api/manage-app/src/main/resources/application-local.yml b/gym-manage-api/manage-app/src/main/resources/application-local.yml
index e1035f1..f5b8e75 100644
--- a/gym-manage-api/manage-app/src/main/resources/application-local.yml
+++ b/gym-manage-api/manage-app/src/main/resources/application-local.yml
@@ -19,11 +19,11 @@ spring:
password: 123456
driver-class-name: org.postgresql.Driver
flyway:
- enabled: false
+ enabled: true
locations: classpath:db/migration
baseline-on-migrate: true
baseline-version: 0
- validate-on-migrate: true
+ validate-on-migrate: false
sql:
init:
mode: always
diff --git a/gym-manage-api/manage-app/src/main/resources/application.yml b/gym-manage-api/manage-app/src/main/resources/application.yml
index e1fe965..7275251 100644
--- a/gym-manage-api/manage-app/src/main/resources/application.yml
+++ b/gym-manage-api/manage-app/src/main/resources/application.yml
@@ -15,9 +15,9 @@ spring:
exclude:
- org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
r2dbc:
- url: r2dbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:manage_system}
- username: ${DB_USERNAME:postgres}
- password: ${DB_PASSWORD:123456}
+ url: r2dbc:postgresql://${DB_HOST:localhost}:${DB_PORT:55432}/${DB_NAME:manage_system}
+ username: ${DB_USERNAME:novalon}
+ password: ${DB_PASSWORD:novalon123}
pool:
initial-size: 10
max-size: 50
@@ -25,16 +25,16 @@ spring:
max-life-time: 1h
acquire-timeout: 5s
datasource:
- url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:manage_system}
- username: ${DB_USERNAME:postgres}
- password: ${DB_PASSWORD:123456}
+ url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:55432}/${DB_NAME:manage_system}
+ username: ${DB_USERNAME:novalon}
+ password: ${DB_PASSWORD:novalon123}
driver-class-name: org.postgresql.Driver
flyway:
- enabled: false
+ enabled: true
locations: classpath:db/migration
baseline-on-migrate: true
baseline-version: 0
- validate-on-migrate: true
+ validate-on-migrate: false
security:
user:
name: disabled
diff --git a/gym-manage-api/manage-app/src/main/resources/keys.properties b/gym-manage-api/manage-app/src/main/resources/keys.properties
index 086c9b0..5dfd3ab 100644
--- a/gym-manage-api/manage-app/src/main/resources/keys.properties
+++ b/gym-manage-api/manage-app/src/main/resources/keys.properties
@@ -1,2 +1,6 @@
huifu.private-key=MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCNUdQE59I4bV1dIgLs2IRN3Wl8KI2yS4KpTUQWrvvlUcBTztB8rIeN5lr6Yv4VsZ1TG0FAb0D80JjxEQ1+0HizYNOQ1h2935v2r8h7rx3VTLbMOkmsg8Lb8LQRTbPaJOZfsIZMzBytYiRzWunHaMlCr800EA3q5NMj9VjHQuamxKqyzyHqfkIjA/sZ8q9atVjn8ahUjPKdrGA8b79HexHhCgOSLdK+fWw0eMCsWWYP2qECLsvZ+tjfvqSBXx1kg7womwT1VBCf+0Dx+jJPKR3mxQfz2szoucJYuXRo55kA6yCwoeNsjanLDRkPBSy3NHdKrffP6YODhRHG6KHyayyNAgMBAAECggEATIR69TEETVNCEzRgOxe9A2AYRoa6ukhSdhMFA/c5IuCR747yqh7MwtNwfVRuWRazpZUDTr0uhfT4asad9QUx5YZO54RX1EAn9XkWZ4nY8G46J/iDfapWLrp09U2KTVpfdn5hKWH3QRX7wI4AON2O49HGnSL4NjAx9q1YpYOe2bqevxmB4uD9vR/FHMjZ2qWzPaL7pId98x4DCJCiZctBQqb7gxR8EseD1Ddn2HbH5DEUjoxz2umtL+zrcGKDFBy4kM3r+10FP5jFG4fJfZFQwrzMTEkXJriau9DMRTQ83rWHzNvhdTNUuyqah74ez5V1piVEOXCPudzFsMttD2DyzQKBgQDokQH3WtcQqwFTUo+uWVVup4NoQzNZpEGIHK17afRsf9YnkoJVqjhywq1dhQT+yuyW9geJJKWpHQIAS4gGudhNK+j6d//bbvvbkOmtsdWyZ6Z2EmY8JrPkHdHAcadNJX7vrIc1xPeyci4AHhErDHc3hhD5njuSLx3DUp4iODNxKwKBgQCbjyHvP90YGjh49DWklh2NJgR2+OR6Fd0afdmwtGKeCUP5l2To6rfLBbcSRIWCON4iLME5wLLdHcm5wAQukC4dsUB5iZffp21+7hrrwUw/G0MUzFudfhZkjsDU5i+FwnVMqmsLJPUsjJTOfKsJdA2DjnwfMcgxw9FeClGveSFNJwKBgQDCpjuDECDY7oeZeYyQXGzIxKOTbEtaR8Qha/83QCM3fHd9f35evK2qP45iq6bWqnkCkMEV4/pTZNf77zvWhU2oqYvBtxYKTwW1a8BphGJbg60rPZMb3TjLQLoB3B4uz6dCaqBwPH8kd7RQnNm5siFF84vZoLozTAQZKtj3wxorKQKBgQCGKnEONHqwaw0B5T7O8VoTfxKiug/07B6C1sCGk03rF/q0rkquSKK0S/2Vl9u+cOXFe+w7r2OVKjfuKRpyPpBHs7T0HiQLFhBuRVaat2DXnN/CdG8f6rvNhwHxnYanSwx4TxN7zShYf/doEEZEJP/y01ViYkFUCpvtC+FgAo0iSQKBgFrkJGxPo6T7i5ZF7VoZWdARnypfbOl4gF9GMrXykk0fnKBDTof9Bg34a8OX54cf4yEH1ifZzVtNyzPTH9FxV88TksWRouqi1Uo6///kJI6gXROspE91TpNtNGHPgLhebA83WlFtS/H7i5G6RO+6KO0uflSImfy3Owp/ChyFUGmn
-huifu.public-key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjVHUBOfSOG1dXSIC7NiETd1pfCiNskuCqU1EFq775VHAU87QfKyHjeZa+mL+FbGdUxtBQG9A/NCY8RENftB4s2DTkNYdvd+b9q/Ie68d1Uy2zDpJrIPC2/C0EU2z2iTmX7CGTMwcrWIkc1rpx2jJQq/NNBAN6uTTI/VYx0LmpsSqss8h6n5CIwP7GfKvWrVY5/GoVIzynaxgPG+/R3sR4QoDki3Svn1sNHjArFlmD9qhAi7L2frY376kgV8dZIO8KJsE9VQQn/tA8foyTykd5sUH89rM6LnCWLl0aOeZAOsgsKHjbI2pyw0ZDwUstzR3Sq33z+mDg4URxuih8mssjQIDAQAB
\ No newline at end of file
+huifu.public-key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjVHUBOfSOG1dXSIC7NiETd1pfCiNskuCqU1EFq775VHAU87QfKyHjeZa+mL+FbGdUxtBQG9A/NCY8RENftB4s2DTkNYdvd+b9q/Ie68d1Uy2zDpJrIPC2/C0EU2z2iTmX7CGTMwcrWIkc1rpx2jJQq/NNBAN6uTTI/VYx0LmpsSqss8h6n5CIwP7GfKvWrVY5/GoVIzynaxgPG+/R3sR4QoDki3Svn1sNHjArFlmD9qhAi7L2frY376kgV8dZIO8KJsE9VQQn/tA8foyTykd5sUH89rM6LnCWLl0aOeZAOsgsKHjbI2pyw0ZDwUstzR3Sq33z+mDg4URxuih8mssjQIDAQAB
+
+# 支付宝沙箱配置
+ALIPAY_APP_PRIVATE_KEY=MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCDKDW/bBiA8oclszYZmLIAS/yR4p47UGMR0yfsbrTgXwikHZp3YUhsNn2l8Ks2izgfciSwtxaKThnhYoK3h9toC8s09+641iFSq3yHg/vYuaEXMJzlk3Ilg//GFSXcB8mntAEX+xZE5NVyFmmx21OcoLH0kAwwwc9TgLtw71Ix6H6BgVy9oDozbi/ZlpVUNhODqUt9m/Spq7sdrdK78zW63JsqekszgdzyCdJPgVk7WRl4DWzJElT5eA1RKfiykAlxlohLLksU655nrdvRc9ZastRy+pVLGsOwrEDGyzNt6L+9dzKa/kdYSYVigHL7u7RZoUUDCfqJurLLfDM93/brAgMBAAECggEAS7zrlLfCSqxCuNWNVyijGaLHniLkRtI7824hLtobHzohzku+CFQoMz0gP7QD2sJ4TUhnwZhorsM9FLcDTyJn4+RzmwnVU+1rXsbiaYsg4t0HFlIfOD91+g4IpIVP0Ii6vVooC4YWLQCL9Y7VyDwkQ11Uhiqsr3cr10eOdj/tEh4fdC1/eWPvxFUKbIcMEI7DtBe5Y6jwkQ569kFrYsBjFnMWxftym1C/GvUIIhUP+2QWN5SmEcABlUPV4LbgwmkfSkwIvC4qcSR3+irM1Z4jikGtRPrxwVc3I7BjJZ+aLqLL89FL10X96pAq/NvjMS6lC5cumStEj483VjrEZQbOIQKBgQDRYGg4oCOaHR+cRvosQLYA89thzmFaB+MhKkKV9HWbCO/lM4jLX+nxNwPIeKmg9rfxAUuUnVpaLEgfIlQ6awR4bfu21fuSl40HFNSkJct9yWbT3T9dDov7Y1dFexFO59t+s+8/67wO5PP6FBGbPQu3bRLsAfVCeDm+7Tzv73T1EQKBgQCgXN57dVO0yo9jXZFfp+70Dhth9BonTGOZ+s2Wg9yCp8NmHeQ3fUGvWm0yCGCViw23YpYpfXdiRLm4kHe4oRR8xaWpTzdsq/9R7EmkcJc7GxSGCu/uEAcVquTgMol+lP6a5glQNmuCY4gooGs0ASfoBTX7i1+5kj06URRWnsO8OwKBgQC6u2GYBvJZk1nK1d5uszPfO51P6HEYi2o8/OSvuntczqatYr3ArAUH1Unxc/PNE9zO+5m9rGyVeWLFKae3BhTRlz1kPgmFHtZvF1viHbcTsxibIXpOcxfTFzERTYYNOmme4bkh6AsruXQCd/2P1uzpJUU36TMkmfbeWdT8JDLKEQKBgBJcj9zzAjKhPunRvOOHBwVK2DfPC2+Uf5MR9JhXYNhU/TqkEY2I+gxp/jbMXsLAuUFWOHtnslO+KsXHRO4PA5mFyAIq393Bk+p9c7EwcyCBaUgv2GkZzLXea3aAUt37kWuLC2Xz1TuIyf5c3+mEeF1Iu3Wh78P/yqLyxbFlXGQhAoGBAJ+3Ul8bwNKeTus9HGZkBdFylD93krnvb3AVwQBl8PzoBftPICsayOeTzKeYEza5wRVjw5+1zT/jz/hnCiNMFhxWfQ9/66Do7ae4bIsNm0cqnDFF6aIiCOu3PSDjMCgGGVnMcMSjmO5nIUQw2YHEloIzC8wi1XmxfLJBBcKnly2D
+ALIPAY_ALIPAY_PUBLIC_KEY=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjZ/rlsJERPZ+GD6UzMYc6n2Rh8m0QV9Z39Iro6eMfxPaERMlKwhJpgmDWXMcpREmwMyoTCK77nLgNLRdc72XGR58aZFsAAQCs7V0kVtUf+IXtfQNNgY4c4tvtLH8E37vJTFBihZ8/UI+XOuX4xf/o+pfK9o2cqaVSt5E9kOrzv8hwDgG9vqOt2beqgETjeKYUwX2cWcAeu0xKoQW6JmzDje9/c7DAtLipmIDFrWVSzu8BFJqFzArv2u84pyDnuaLrqQ5rz3ktbn0QirbGlL5k/6E3CUB55CCF8+jEzQsjQUGQlTch9Dl7XAJLT8Lh1q2bat7KievEa1mNfzJmG5MDQIDAQAB
\ No newline at end of file
diff --git a/gym-manage-api/manage-app/src/main/resources/member-config.yml b/gym-manage-api/manage-app/src/main/resources/member-config.yml
index 05ce7cb..61bb6c5 100644
--- a/gym-manage-api/manage-app/src/main/resources/member-config.yml
+++ b/gym-manage-api/manage-app/src/main/resources/member-config.yml
@@ -35,3 +35,14 @@ payment:
query-url: "https://api.huifu.com/v4/trade/payment/query"
refund-url: "https://api.huifu.com/v4/trade/refund"
notify-url: "http://localhost:8084/api/payment/notify"
+
+# 支付宝配置(沙箱环境)
+alipay:
+ app-id: "9021000164699238"
+ app-private-key: ${ALIPAY_APP_PRIVATE_KEY}
+ alipay-public-key: ${ALIPAY_ALIPAY_PUBLIC_KEY}
+ sign-type: "RSA2"
+ charset: "UTF-8"
+ gateway: "https://openapi-sandbox.dl.alipaydev.com/gateway.do"
+ notify-url: "http://localhost:8084/api/payment/notify"
+ sandbox: true
diff --git a/gym-manage-api/manage-app/src/test/resources/application-test.yml b/gym-manage-api/manage-app/src/test/resources/application-test.yml
index 8d11187..57a6741 100644
--- a/gym-manage-api/manage-app/src/test/resources/application-test.yml
+++ b/gym-manage-api/manage-app/src/test/resources/application-test.yml
@@ -9,10 +9,7 @@ spring:
max-size: 10
flyway:
- enabled: true
- locations: classpath:db/migration
- baseline-on-migrate: true
- validate-on-migrate: true
+ enabled: false
sql:
init:
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V15__Create_GroupCourse_Type_table.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V10__Create_GroupCourse_Type_table.sql
similarity index 100%
rename from gym-manage-api/manage-db/src/main/resources/db/migration/V15__Create_GroupCourse_Type_table.sql
rename to gym-manage-api/manage-db/src/main/resources/db/migration/V10__Create_GroupCourse_Type_table.sql
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V10__Insert_Booking_Test_Data.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V10__Insert_Booking_Test_Data.sql
deleted file mode 100644
index c6c4452..0000000
--- a/gym-manage-api/manage-db/src/main/resources/db/migration/V10__Insert_Booking_Test_Data.sql
+++ /dev/null
@@ -1,86 +0,0 @@
--- V10: 团课预约业务场景测试数据
--- 测试场景:
--- 1. 用户A刚刚注册,在没有任何会员卡的情况下申请团课预约
--- 2. 用户B已有时长卡,但已经过期
--- 3. 会员C有时长卡,未过期
--- 4. 会员D有次数卡,但次数卡耗尽次数
--- 5. 会员E有次数卡,且次数充足
--- 6. 会员F有储值卡,但余额不足以支付其预约的团课
--- 7. 会员G有储值卡,余额充足
-
--- ============================================
--- 1. 创建测试会员用户 (A-G)
--- ============================================
-
-INSERT INTO member_user (member_no, nickname, phone, gender, is_deleted, created_at, updated_at) VALUES
- ('MEM_TEST_A', '用户A_无卡新用户', '13800001001', 1, false, '2026-06-02 10:00:00', '2026-06-02 10:00:00'),
- ('MEM_TEST_B', '用户B_过期时长卡', '13800001002', 1, false, '2026-05-01 10:00:00', '2026-05-01 10:00:00'),
- ('MEM_TEST_C', '会员C_有效时长卡', '13800001003', 2, false, '2026-05-01 10:00:00', '2026-05-01 10:00:00'),
- ('MEM_TEST_D', '会员D_次数用尽', '13800001004', 1, false, '2026-05-01 10:00:00', '2026-05-01 10:00:00'),
- ('MEM_TEST_E', '会员E_次数充足', '13800001005', 2, false, '2026-05-01 10:00:00', '2026-05-01 10:00:00'),
- ('MEM_TEST_F', '会员F_储值卡余额不足', '13800001006', 1, false, '2026-05-01 10:00:00', '2026-05-01 10:00:00'),
- ('MEM_TEST_G', '会员G_储值卡余额充足', '13800001007', 2, false, '2026-05-01 10:00:00', '2026-05-01 10:00:00');
-
--- ============================================
--- 2. 团课课程测试数据(用于预约测试)
--- ============================================
-
--- 团课A: 收费类型为次数卡(点卡额度=1)
-INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, description, point_card_amount, stored_value_amount, create_by, created_at, updated_at) VALUES
- ('燃脂搏击_次数卡课程', 102, 2, '2026-06-10 19:30:00', '2026-06-10 20:30:00', 20, 0, '0', '综合训练区', '高强度间歇训练,配合音乐快速燃脂,消耗1次', 1, 0.00, 'admin', '2026-06-01 10:00:00', '2026-06-01 10:00:00');
-
--- 团课B: 收费类型为储值卡(储值额度=50.00)
-INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, description, point_card_amount, stored_value_amount, create_by, created_at, updated_at) VALUES
- ('高端普拉提_储值卡课程', 103, 1, '2026-06-11 19:00:00', '2026-06-11 20:00:00', 15, 0, '0', '普拉提教室', '精准训练核心肌群,消耗储值50元', 0, 20.00, 'admin', '2026-06-01 10:00:00', '2026-06-01 10:00:00');
-
--- ============================================
--- 3. 会员卡类型定义
--- ============================================
-
-INSERT INTO member_card (member_card_name, member_card_type, member_card_price, member_card_validity_days, member_card_total_times, member_card_amount, member_card_status, extra_config, created_at, updated_at) VALUES
- ('30天时长卡', 'TIME_CARD', 299.00, 30, NULL, NULL, 1, '{}', '2026-01-01 00:00:00', '2026-01-01 00:00:00'),
- ('10次卡', 'COUNT_CARD', 499.00, NULL, 10, NULL, 1, '{}', '2026-01-01 00:00:00', '2026-01-01 00:00:00'),
- ('储值卡500', 'STORED_VALUE_CARD', 500.00, NULL, NULL, 500.00, 1, '{}', '2026-01-01 00:00:00', '2026-01-01 00:00:00');
-
--- ============================================
--- 4. 会员卡记录测试数据
--- ============================================
-
--- 场景2: 用户B - 时长卡已过期 (expire_time < 当前时间)
--- 购买于30天前,有效期30天,现已过期
-INSERT INTO member_card_record (member_id, member_card_id, status, remaining_times, remaining_amount, expire_time, purchase_time, version, card_composition, created_at, updated_at) VALUES
- ((SELECT id FROM member_user WHERE member_no = 'MEM_TEST_B'), (SELECT member_card_id FROM member_card WHERE member_card_name = '30天时长卡'), 'EXPIRED', 0, 0.00, '2026-05-31 23:59:59', '2026-05-01 10:00:00', 0, '{}', '2026-05-01 10:00:00', '2026-06-02 10:00:00');
-
--- 场景3: 会员C - 时长卡未过期 (expire_time > 当前时间)
--- 购买于今天,有效期30天,还有30天有效期
-INSERT INTO member_card_record (member_id, member_card_id, status, remaining_times, remaining_amount, expire_time, purchase_time, version, card_composition, created_at, updated_at) VALUES
- ((SELECT id FROM member_user WHERE member_no = 'MEM_TEST_C'), (SELECT member_card_id FROM member_card WHERE member_card_name = '30天时长卡'), 'ACTIVE', 0, 0.00, '2026-07-02 23:59:59', '2026-06-02 10:00:00', 0, '{}', '2026-06-02 10:00:00', '2026-06-02 10:00:00');
-
--- 场景4: 会员D - 次数卡耗尽 (remaining_times = 0)
-INSERT INTO member_card_record (member_id, member_card_id, status, remaining_times, remaining_amount, expire_time, purchase_time, version, card_composition, created_at, updated_at) VALUES
- ((SELECT id FROM member_user WHERE member_no = 'MEM_TEST_D'), (SELECT member_card_id FROM member_card WHERE member_card_name = '10次卡'), 'ACTIVE', 0, 0.00, '2026-12-31 23:59:59', '2026-05-01 10:00:00', 0, '{}', '2026-05-01 10:00:00', '2026-06-02 10:00:00');
-
--- 场景5: 会员E - 次数卡充足 (remaining_times >= point_card_amount)
-INSERT INTO member_card_record (member_id, member_card_id, status, remaining_times, remaining_amount, expire_time, purchase_time, version, card_composition, created_at, updated_at) VALUES
- ((SELECT id FROM member_user WHERE member_no = 'MEM_TEST_E'), (SELECT member_card_id FROM member_card WHERE member_card_name = '10次卡'), 'ACTIVE', 5, 0.00, '2026-12-31 23:59:59', '2026-05-01 10:00:00', 0, '{}', '2026-05-01 10:00:00', '2026-06-02 10:00:00');
-
--- 场景6: 会员F - 储值卡余额不足 (remaining_amount < stored_value_amount of course)
--- 团课B收费50元,但会员F储值卡只有30元
-INSERT INTO member_card_record (member_id, member_card_id, status, remaining_times, remaining_amount, expire_time, purchase_time, version, card_composition, created_at, updated_at) VALUES
- ((SELECT id FROM member_user WHERE member_no = 'MEM_TEST_F'), (SELECT member_card_id FROM member_card WHERE member_card_name = '储值卡500'), 'ACTIVE', 0, 30.00, '2027-06-02 23:59:59', '2026-05-01 10:00:00', 0, '{}', '2026-05-01 10:00:00', '2026-06-02 10:00:00');
-
--- 场景7: 会员G - 储值卡余额充足 (remaining_amount >= stored_value_amount of course)
-INSERT INTO member_card_record (member_id, member_card_id, status, remaining_times, remaining_amount, expire_time, purchase_time, version, card_composition, created_at, updated_at) VALUES
- ((SELECT id FROM member_user WHERE member_no = 'MEM_TEST_G'), (SELECT member_card_id FROM member_card WHERE member_card_name = '储值卡500'), 'ACTIVE', 0, 200.00, '2027-06-02 23:59:59', '2026-05-01 10:00:00', 0, '{}', '2026-05-01 10:00:00', '2026-06-02 10:00:00');
-
--- ============================================
--- 5. 测试场景说明
--- ============================================
-
--- 场景1: 用户A无会员卡 -> 预约团课时应提示"无可用会员卡"
--- 场景2: 用户B时长卡已过期 -> 预约团课时应提示"会员卡已过期"
--- 场景3: 会员C时长卡有效 -> 可以正常预约次数卡团课
--- 场景4: 会员D次数为0 -> 预约团课时应提示"剩余次数不足"
--- 场景5: 会员E次数充足 -> 可以正常预约次数卡团课
--- 场景6: 会员F储值卡余额30元,团课需50元 -> 预约时应提示"余额不足"
--- 场景7: 会员G储值卡余额200元,团课需50元 -> 可以正常预约储值卡团课
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V16__Create_Course_Label_tables.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V11__Create_Course_Label_tables.sql
similarity index 100%
rename from gym-manage-api/manage-db/src/main/resources/db/migration/V16__Create_Course_Label_tables.sql
rename to gym-manage-api/manage-db/src/main/resources/db/migration/V11__Create_Course_Label_tables.sql
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V11__Insert_Cancel_Booking_Test_Data.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V11__Insert_Cancel_Booking_Test_Data.sql
deleted file mode 100644
index a6d94c8..0000000
--- a/gym-manage-api/manage-db/src/main/resources/db/migration/V11__Insert_Cancel_Booking_Test_Data.sql
+++ /dev/null
@@ -1,116 +0,0 @@
--- V11: 团课预约取消业务场景测试数据
--- 测试场景:
--- 1. 用户张三使用时长卡预约了团课,需要取消预约
--- 2. 用户李四使用次数卡预约了团课,需要取消预约
--- 3. 用户王五使用储值卡预约了团课,需要取消预约
-
--- ============================================
--- 1. 创建测试会员用户 (张三、李四、王五)
--- ============================================
-
-INSERT INTO member_user (member_no, nickname, phone, gender, is_deleted, created_at, updated_at) VALUES
- ('MEM_TEST_ZHANG', '张三_时长卡用户', '13800002001', 1, false, '2026-06-02 10:00:00', '2026-06-02 10:00:00'),
- ('MEM_TEST_LI', '李四_次数卡用户', '13800002002', 1, false, '2026-06-02 10:00:00', '2026-06-02 10:00:00'),
- ('MEM_TEST_WANG', '王五_储值卡用户', '13800002003', 2, false, '2026-06-02 10:00:00', '2026-06-02 10:00:00');
-
--- ============================================
--- 2. 确保会员卡类型已存在(如果不存在则创建)
--- ============================================
-
--- 时长卡类型(30天)
-INSERT INTO member_card (member_card_name, member_card_type, member_card_price, member_card_validity_days, member_card_total_times, member_card_amount, member_card_status, extra_config, created_at, updated_at)
-SELECT '30天时长卡', 'TIME_CARD', 299.00, 30, NULL, NULL, 1, '{}', '2026-01-01 00:00:00', '2026-01-01 00:00:00'
-WHERE NOT EXISTS (SELECT 1 FROM member_card WHERE member_card_name = '30天时长卡');
-
--- 次数卡类型(10次)
-INSERT INTO member_card (member_card_name, member_card_type, member_card_price, member_card_validity_days, member_card_total_times, member_card_amount, member_card_status, extra_config, created_at, updated_at)
-SELECT '10次卡', 'COUNT_CARD', 499.00, NULL, 10, NULL, 1, '{}', '2026-01-01 00:00:00', '2026-01-01 00:00:00'
-WHERE NOT EXISTS (SELECT 1 FROM member_card WHERE member_card_name = '10次卡');
-
--- 储值卡类型(500元)
-INSERT INTO member_card (member_card_name, member_card_type, member_card_price, member_card_validity_days, member_card_total_times, member_card_amount, member_card_status, extra_config, created_at, updated_at)
-SELECT '储值卡500', 'STORED_VALUE_CARD', 500.00, NULL, NULL, 500.00, 1, '{}', '2026-01-01 00:00:00', '2026-01-01 00:00:00'
-WHERE NOT EXISTS (SELECT 1 FROM member_card WHERE member_card_name = '储值卡500');
-
--- ============================================
--- 3. 为测试用户分配会员卡
--- ============================================
-
--- 张三:时长卡(有效期内)
-INSERT INTO member_card_record (member_id, member_card_id, status, remaining_times, remaining_amount, expire_time, purchase_time, version, card_composition, created_at, updated_at) VALUES
- ((SELECT id FROM member_user WHERE member_no = 'MEM_TEST_ZHANG'),
- (SELECT member_card_id FROM member_card WHERE member_card_name = '30天时长卡'),
- 'ACTIVE', 0, 0.00, '2026-07-02 23:59:59', '2026-06-02 10:00:00', 0, '{}', '2026-06-02 10:00:00', '2026-06-02 10:00:00');
-
--- 李四:次数卡(剩余5次)
-INSERT INTO member_card_record (member_id, member_card_id, status, remaining_times, remaining_amount, expire_time, purchase_time, version, card_composition, created_at, updated_at) VALUES
- ((SELECT id FROM member_user WHERE member_no = 'MEM_TEST_LI'),
- (SELECT member_card_id FROM member_card WHERE member_card_name = '10次卡'),
- 'ACTIVE', 5, 0.00, '2026-12-31 23:59:59', '2026-05-01 10:00:00', 0, '{}', '2026-05-01 10:00:00', '2026-06-02 10:00:00');
-
--- 王五:储值卡(余额200元)
-INSERT INTO member_card_record (member_id, member_card_id, status, remaining_times, remaining_amount, expire_time, purchase_time, version, card_composition, created_at, updated_at) VALUES
- ((SELECT id FROM member_user WHERE member_no = 'MEM_TEST_WANG'),
- (SELECT member_card_id FROM member_card WHERE member_card_name = '储值卡500'),
- 'ACTIVE', 0, 200.00, '2027-06-02 23:59:59', '2026-05-01 10:00:00', 0, '{}', '2026-05-01 10:00:00', '2026-06-02 10:00:00');
-
--- ============================================
--- 4. 创建用于取消预约测试的团课
--- ============================================
-
--- 团课X: 时长卡可用课程(使用次数卡支付,时长卡会员可预约)
-INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, description, point_card_amount, stored_value_amount, create_by, created_at, updated_at) VALUES
- ('晚间瑜伽_取消测试', 101, 1, '2026-06-15 19:00:00', '2026-06-15 20:00:00', 20, 0, '0', '瑜伽教室', '适合所有级别的瑜伽课程,用于测试取消预约功能', 1, 30.00, 'admin', '2026-06-02 10:00:00', '2026-06-02 10:00:00');
-
--- ============================================
--- 5. 创建已预约的记录(状态为0-已预约)
--- ============================================
-
--- 张三使用时长卡预约团课(状态:已预约)
-INSERT INTO group_course_booking (course_id, member_id, member_card_id, booking_time, status, course_name, course_start_time, course_end_time, location, created_at, updated_at) VALUES
- ((SELECT id FROM group_course WHERE course_name = '晚间瑜伽_取消测试'),
- (SELECT id FROM member_user WHERE member_no = 'MEM_TEST_ZHANG'),
- (SELECT id FROM member_card_record WHERE member_id = (SELECT id FROM member_user WHERE member_no = 'MEM_TEST_ZHANG')),
- '2026-06-02 11:00:00', '0', '晚间瑜伽_取消测试', '2026-06-15 19:00:00', '2026-06-15 20:00:00', '瑜伽教室',
- '2026-06-02 11:00:00', '2026-06-02 11:00:00');
-
--- 李四使用次数卡预约团课(状态:已预约)
-INSERT INTO group_course_booking (course_id, member_id, member_card_id, booking_time, status, course_name, course_start_time, course_end_time, location, created_at, updated_at) VALUES
- ((SELECT id FROM group_course WHERE course_name = '晚间瑜伽_取消测试'),
- (SELECT id FROM member_user WHERE member_no = 'MEM_TEST_LI'),
- (SELECT id FROM member_card_record WHERE member_id = (SELECT id FROM member_user WHERE member_no = 'MEM_TEST_LI')),
- '2026-06-02 11:30:00', '0', '晚间瑜伽_取消测试', '2026-06-15 19:00:00', '2026-06-15 20:00:00', '瑜伽教室',
- '2026-06-02 11:30:00', '2026-06-02 11:30:00');
-
--- 王五使用储值卡预约团课(状态:已预约)
-INSERT INTO group_course_booking (course_id, member_id, member_card_id, booking_time, status, course_name, course_start_time, course_end_time, location, created_at, updated_at) VALUES
- ((SELECT id FROM group_course WHERE course_name = '晚间瑜伽_取消测试'),
- (SELECT id FROM member_user WHERE member_no = 'MEM_TEST_WANG'),
- (SELECT id FROM member_card_record WHERE member_id = (SELECT id FROM member_user WHERE member_no = 'MEM_TEST_WANG')),
- '2026-06-02 12:00:00', '0', '晚间瑜伽_取消测试', '2026-06-15 19:00:00', '2026-06-15 20:00:00', '瑜伽教室',
- '2026-06-02 12:00:00', '2026-06-02 12:00:00');
-
--- ============================================
--- 6. 更新团课当前预约人数
--- ============================================
-
-UPDATE group_course
-SET current_members = current_members + 3
-WHERE course_name = '晚间瑜伽_取消测试';
-
--- ============================================
--- 测试数据说明
--- ============================================
--- 用户信息:
--- 张三 (MEM_TEST_ZHANG): 13800002001 - 持有有效期内的时长卡
--- 李四 (MEM_TEST_LI): 13800002002 - 持有次数卡(剩余5次)
--- 王五 (MEM_TEST_WANG): 13800002003 - 持有储值卡(余额200元)
-
--- 测试场景:
--- 1. 张三使用时长卡预约了团课 -> 取消预约 -> 应恢复时长卡状态,释放课程名额
--- 2. 李四使用次数卡预约了团课 -> 取消预约 -> 应返还扣除的次数,释放课程名额
--- 3. 王五使用储值卡预约了团课 -> 取消预约 -> 应返还扣除的金额,释放课程名额
-
--- 预约记录状态说明:
--- status = '0' 表示已预约
--- status = '1' 表示已取消(取消后应更新为此状态)
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V17__Create_GroupCourse_Recommend_table.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V12__Create_GroupCourse_Recommend_table.sql
similarity index 100%
rename from gym-manage-api/manage-db/src/main/resources/db/migration/V17__Create_GroupCourse_Recommend_table.sql
rename to gym-manage-api/manage-db/src/main/resources/db/migration/V12__Create_GroupCourse_Recommend_table.sql
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V12__Insert_Time_Conflict_Test_Data.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V12__Insert_Time_Conflict_Test_Data.sql
deleted file mode 100644
index 7189cce..0000000
--- a/gym-manage-api/manage-db/src/main/resources/db/migration/V12__Insert_Time_Conflict_Test_Data.sql
+++ /dev/null
@@ -1,58 +0,0 @@
--- V12: 团课预约时间冲突测试数据
--- 测试场景:会员预约两个时间重叠的团课时,系统应检测到时间冲突并拒绝预约
-
--- ============================================
--- 1. 创建测试会员
--- ============================================
-
-INSERT INTO member_user (member_no, nickname, phone, gender, is_deleted, created_at, updated_at) VALUES
- ('MEM_TIME_CONFLICT', '时间冲突测试会员', '13800009999', 1, false, '2026-06-02 10:00:00', '2026-06-02 10:00:00');
-
--- ============================================
--- 2. 创建时间冲突的团课
--- ============================================
-
--- 团课A: 6月15日 13:00 - 15:00
-INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, description, point_card_amount, stored_value_amount, create_by, created_at, updated_at) VALUES
- ('时间冲突测试_A_13点-15点', 102, 2, '2026-06-15 13:00:00', '2026-06-15 15:00:00', 20, 0, '0', '综合训练区', '测试用团课A,用于验证时间冲突检测', 1, 0.00, 'admin', '2026-06-02 10:00:00', '2026-06-02 10:00:00');
-
--- 团课B: 6月15日 14:00 - 16:00 (与团课A时间重叠)
-INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, description, point_card_amount, stored_value_amount, create_by, created_at, updated_at) VALUES
- ('时间冲突测试_B_14点-16点', 103, 1, '2026-06-15 14:00:00', '2026-06-15 16:00:00', 15, 0, '0', '普拉提教室', '测试用团课B,与团课A时间重叠(14:00-15:00)', 1, 0.00, 'admin', '2026-06-02 10:00:00', '2026-06-02 10:00:00');
-
--- 团课C: 6月15日 10:00 - 12:00 (不冲突,作为对照)
-INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, description, point_card_amount, stored_value_amount, create_by, created_at, updated_at) VALUES
- ('时间冲突测试_C_10点-12点', 101, 1, '2026-06-15 10:00:00', '2026-06-15 12:00:00', 15, 0, '0', '瑜伽教室', '测试用团课C,与团课A/B不冲突', 1, 0.00, 'admin', '2026-06-02 10:00:00', '2026-06-02 10:00:00');
-
--- ============================================
--- 3. 会员卡记录(用于预约)
--- ============================================
-
--- 为时间冲突测试会员创建有效次数卡
-INSERT INTO member_card_record (member_id, member_card_id, status, remaining_times, remaining_amount, expire_time, purchase_time, version, card_composition, created_at, updated_at) VALUES
- ((SELECT id FROM member_user WHERE member_no = 'MEM_TIME_CONFLICT'), (SELECT member_card_id FROM member_card WHERE member_card_name = '10次卡'), 'ACTIVE', 10, 0.00, '2026-12-31 23:59:59', '2026-06-02 10:00:00', 0, '{}', '2026-06-02 10:00:00', '2026-06-02 10:00:00');
-
--- ============================================
--- 4. 测试场景说明
--- ============================================
---
--- 测试步骤:
--- 1. 使用会员 MEM_TIME_CONFLICT 预约团课A (course_name='时间冲突测试_A_13点-15点')
--- → 预期:预约成功
---
--- 2. 使用会员 MEM_TIME_CONFLICT 预约团课B (course_name='时间冲突测试_B_14点-16点')
--- → 预期:预约失败,提示时间冲突(14:00-15:00与已预约的团课A重叠)
---
--- 3. 使用会员 MEM_TIME_CONFLICT 预约团课C (course_name='时间冲突测试_C_10点-12点')
--- → 预期:预约成功(与团课A时间不冲突)
---
--- 时间冲突判断逻辑:
--- 新课程开始时间 < 已预约课程结束时间 AND 新课程结束时间 > 已预约课程开始时间
--- 即:newStartTime < existingEndTime AND newEndTime > existingStartTime
---
--- 本测试数据中的时间关系:
--- - 团课A: 13:00 - 15:00
--- - 团课B: 14:00 - 16:00
--- → 14:00 < 15:00 AND 16:00 > 13:00 → 冲突
--- - 团课C: 10:00 - 12:00
--- → 10:00 < 15:00 AND 12:00 > 13:00 → 12:00 > 13:00 不成立 → 不冲突
\ No newline at end of file
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V13__Consolidated_Test_Data.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V13__Consolidated_Test_Data.sql
new file mode 100644
index 0000000..b109d7b
--- /dev/null
+++ b/gym-manage-api/manage-db/src/main/resources/db/migration/V13__Consolidated_Test_Data.sql
@@ -0,0 +1,342 @@
+-- ============================================================
+-- V13: 整合所有测试数据(基于 2026-06-16 17:13)
+-- 说明: 系统初始数据由 V2 处理,本文件仅包含业务测试数据
+-- 所有时间相关字段已更新为基于 2026-06-16
+-- ============================================================
+
+-- ============================================================
+-- Section B: 团课类型数据(来源 V15)
+-- ============================================================
+
+INSERT INTO group_course_type (type_name, base_difficulty, description, category) VALUES
+('慢走/椭圆机轻松模式', 1, '几乎无难度,适合所有人', '基础有氧与热身'),
+('固定自行车(低阻力)', 2, '注意座椅高度调节即可', '基础有氧与热身'),
+('跑步机慢跑', 3, '需要基本协调性,膝盖有压力', '基础有氧与热身'),
+('跳绳(连续基础跳)', 3, '需要手脚配合,心肺要求明显', '基础有氧与热身'),
+('坐姿腿屈伸/腿弯举', 2, '很容易找到发力感', '固定器械训练'),
+('坐姿推胸机', 3, '需注意肩胛后收,避免耸肩', '固定器械训练'),
+('高位下拉(坐姿)', 3, '需控制不要过度后仰', '固定器械训练'),
+('史密斯机深蹲', 4, '轨迹固定,但需保持核心稳定', '固定器械训练'),
+('蝴蝶机夹胸', 3, '易用肘关节代偿,需锁定肩关节', '固定器械训练'),
+('平板支撑', 3, '耐力考验,技巧低', '自重基础动作'),
+('跪姿俯卧撑', 3, '上肢力量较弱者首选', '自重基础动作'),
+('标准俯卧撑', 5, '需核心收紧,身体成直线', '自重基础动作'),
+('引体向上(弹力带辅助)', 6, '背部和手臂力量要求高', '自重基础动作'),
+('标准引体向上', 8, '力量-体重比极高,多数男性无法完成1次', '自重基础动作'),
+('徒手深蹲', 3, '注意膝盖方向与背部直立', '自重基础动作'),
+('单腿深蹲(手枪蹲)', 8, '需要极高下肢力量、柔韧性和平衡', '自重基础动作'),
+('哑铃二头弯举', 4, '容易晃动借力,但较安全', '自由重量杠铃/哑铃'),
+('哑铃侧平举', 5, '极易用斜方肌代偿,真正练到三角肌中束很难', '自由重量杠铃/哑铃'),
+('杠铃卧推', 7, '肩关节压力大,起桥、沉肩、稳定手腕均有技巧,有压伤风险', '自由重量杠铃/哑铃'),
+('杠铃深蹲(颈后)', 8, '全身协调性、核心抗压、杠位放置、呼吸模式,学习曲线陡峭', '自由重量杠铃/哑铃'),
+('传统硬拉', 9, '风险极高,需要精确的脊柱中立、髋铰链、背阔肌收紧,错误时伤腰', '自由重量杠铃/哑铃'),
+('高翻/抓举(奥运举重)', 10, '需要爆发力、柔韧、精准衔接,非数月训练不能掌握', '自由重量杠铃/哑铃'),
+('波比跳(标准版)', 6, '连续做时心肺压力极大', '高强度与爆发力'),
+('冲刺跑(短跑)', 7, '对腘绳肌和脚踝爆发力要求高', '高强度与爆发力'),
+('跳箱(合理高度)', 6, '需要落地缓冲技巧', '高强度与爆发力'),
+('负重雪橇推', 6, '主要考验腿部耐力和意志力', '高强度与爆发力'),
+('双力臂(引体向上后翻腕上杠)', 9, '需要爆发引体 + 极高相对力量', '高强度与爆发力'),
+('静态拉伸(坐姿体前屈)', 2, '无风险,但需要坚持', '柔韧与平衡类'),
+('瑜伽下犬式', 3, '常见,但需背部与手臂对齐', '柔韧与平衡类'),
+('单腿罗马尼亚硬拉(徒手)', 6, '极考验平衡和髋稳定', '柔韧与平衡类'),
+('全深蹲(脚跟贴地,亚洲蹲)', 5, '踝关节灵活度限制多数人', '柔韧与平衡类'),
+('竖叉/横叉', 8, '需要数月甚至数年拉伸', '柔韧与平衡类'),
+('瑜伽入门', 2, '适合初学者的瑜伽课程,注重基础体式', '柔韧与平衡类'),
+('瑜伽进阶', 5, '针对有一定基础的学员,包含更复杂体式', '柔韧与平衡类'),
+('普拉提', 4, '注重核心力量和身体控制', '柔韧与平衡类'),
+('动感单车', 4, '高强度有氧运动,节奏感强', '基础有氧与热身'),
+('搏击操', 5, '结合拳击动作的有氧运动', '高强度与爆发力'),
+('HIIT训练', 7, '高强度间歇训练,对心肺要求极高', '高强度与爆发力'),
+('核心训练', 4, '针对核心肌群的专项训练', '自重基础动作');
+
+-- ============================================================
+-- Section C: 团课标签数据(来源 V16)
+-- ============================================================
+
+INSERT INTO course_label (label_name, color, description) VALUES
+('适合新手', '#52c41a', '适合健身初学者'),
+('中级过渡', '#faad14', '适合有一定基础的学员'),
+('高级进阶', '#f5222d', '适合高级学员'),
+('减脂塑形', '#722ed1', '有助于减脂塑形'),
+('增肌强化', '#13c2c2', '有助于增肌强化'),
+('柔韧性训练', '#eb2f96', '注重柔韧性提升'),
+('核心训练', '#1890ff', '注重核心力量'),
+('心肺训练', '#fa8c16', '提升心肺功能'),
+('低冲击', '#52c41a', '低冲击运动,适合关节保护'),
+('高强度', '#f5222d', '高强度间歇训练'),
+('团体互动', '#722ed1', '注重团队协作'),
+('私教推荐', '#13c2c2', '私教推荐课程'),
+('热门课程', '#ff1493', '人气较高的课程'),
+('新课上线', '#00ced1', '新上线的课程');
+
+-- ============================================================
+-- Section D: 会员卡类型(来源 V10 / V11,已去重合并)
+-- ============================================================
+
+INSERT INTO member_card (member_card_name, member_card_type, member_card_price, member_card_validity_days, member_card_total_times, member_card_amount, member_card_status, extra_config, created_at, updated_at) VALUES
+('30天时长卡', 'TIME_CARD', 299.00, 30, NULL, NULL, 1, '{}', '2026-01-01 00:00:00', '2026-01-01 00:00:00'),
+('10次卡', 'COUNT_CARD', 499.00, NULL, 10, NULL, 1, '{}', '2026-01-01 00:00:00', '2026-01-01 00:00:00'),
+('储值卡500', 'STORED_VALUE_CARD', 500.00, NULL, NULL, 500.00, 1, '{}', '2026-01-01 00:00:00', '2026-01-01 00:00:00');
+
+-- ============================================================
+-- Section E: 测试会员(来源 V10 / V11 / V12 / V14)
+-- ============================================================
+
+-- E1: 数据统计测试会员(V14,显式 ID 1001-1012)
+INSERT INTO member_user (id, member_no, nickname, phone, created_at, updated_at, is_deleted) VALUES
+(1001, 'M20260616001', '张三', '13800138001', '2026-06-16 08:00:00', '2026-06-16 08:00:00', false),
+(1002, 'M20260616002', '李四', '13800138002', '2026-06-16 09:00:00', '2026-06-16 09:00:00', false),
+(1003, 'M20260615003', '王五', '13800138003', '2026-06-15 10:00:00', '2026-06-15 10:00:00', false),
+(1004, 'M20260614004', '赵六', '13800138004', '2026-06-14 11:00:00', '2026-06-14 11:00:00', false),
+(1005, 'M20260616005', '钱七', '13800138005', '2026-06-16 08:00:00', '2026-06-16 08:00:00', false),
+(1006, 'M20260616006', '孙八', '13800138006', '2026-06-16 09:00:00', '2026-06-16 09:00:00', false),
+(1007, 'M20260616007', '周九', '13800138007', '2026-06-16 10:00:00', '2026-06-16 10:00:00', false),
+(1008, 'M20260613008', '吴十', '13800138008', '2026-06-13 14:00:00', '2026-06-13 14:00:00', false),
+(1009, 'M20260612009', '郑十一', '13800138009', '2026-06-12 15:00:00', '2026-06-12 15:00:00', false),
+(1010, 'M20260611010', '王十二', '13800138010', '2026-06-11 16:00:00', '2026-06-11 16:00:00', false),
+(1011, 'M20260610011', '陈十三', '13800138011', '2026-06-10 17:00:00', '2026-06-10 17:00:00', false),
+(1012, 'M20260609012', '刘十四', '13800138012', '2026-06-09 18:00:00', '2026-06-09 18:00:00', false);
+
+-- E2: 预约场景测试会员(V10: A-G)
+INSERT INTO member_user (member_no, nickname, phone, gender, is_deleted, created_at, updated_at) VALUES
+('MEM_TEST_A', '用户A_无卡新用户', '13800001001', 1, false, '2026-06-16 10:00:00', '2026-06-16 10:00:00'),
+('MEM_TEST_B', '用户B_过期时长卡', '13800001002', 1, false, '2026-06-15 10:00:00', '2026-06-15 10:00:00'),
+('MEM_TEST_C', '会员C_有效时长卡', '13800001003', 2, false, '2026-06-15 10:00:00', '2026-06-15 10:00:00'),
+('MEM_TEST_D', '会员D_次数用尽', '13800001004', 1, false, '2026-06-15 10:00:00', '2026-06-15 10:00:00'),
+('MEM_TEST_E', '会员E_次数充足', '13800001005', 2, false, '2026-06-15 10:00:00', '2026-06-15 10:00:00'),
+('MEM_TEST_F', '会员F_储值卡余额不足', '13800001006', 1, false, '2026-06-15 10:00:00', '2026-06-15 10:00:00'),
+('MEM_TEST_G', '会员G_储值卡余额充足', '13800001007', 2, false, '2026-06-15 10:00:00', '2026-06-15 10:00:00');
+
+-- E3: 取消预约测试会员(V11: 张三/李四/王五)
+INSERT INTO member_user (member_no, nickname, phone, gender, is_deleted, created_at, updated_at) VALUES
+('MEM_TEST_ZHANG', '张三_时长卡用户', '13800002001', 1, false, '2026-06-15 10:00:00', '2026-06-15 10:00:00'),
+('MEM_TEST_LI', '李四_次数卡用户', '13800002002', 1, false, '2026-06-15 10:00:00', '2026-06-15 10:00:00'),
+('MEM_TEST_WANG', '王五_储值卡用户', '13800002003', 2, false, '2026-06-15 10:00:00', '2026-06-15 10:00:00');
+
+-- E4: 时间冲突测试会员(V12)
+INSERT INTO member_user (member_no, nickname, phone, gender, is_deleted, created_at, updated_at) VALUES
+('MEM_TIME_CONFLICT', '时间冲突测试会员', '13800009999', 1, false, '2026-06-15 10:00:00', '2026-06-15 10:00:00');
+
+-- ============================================================
+-- Section F: 会员卡记录
+-- ============================================================
+
+-- F1: V10 预约场景 - 6条会员卡记录
+INSERT INTO member_card_record (member_id, member_card_id, status, remaining_times, remaining_amount, expire_time, purchase_time, version, card_composition, created_at, updated_at) VALUES
+((SELECT id FROM member_user WHERE member_no = 'MEM_TEST_B'), (SELECT member_card_id FROM member_card WHERE member_card_name = '30天时长卡'), 'EXPIRED', 0, 0.00, '2026-06-15 23:59:59', '2026-05-16 10:00:00', 0, '{}', '2026-05-16 10:00:00', '2026-06-16 10:00:00');
+
+INSERT INTO member_card_record (member_id, member_card_id, status, remaining_times, remaining_amount, expire_time, purchase_time, version, card_composition, created_at, updated_at) VALUES
+((SELECT id FROM member_user WHERE member_no = 'MEM_TEST_C'), (SELECT member_card_id FROM member_card WHERE member_card_name = '30天时长卡'), 'ACTIVE', 0, 0.00, '2026-07-16 23:59:59', '2026-06-16 10:00:00', 0, '{}', '2026-06-16 10:00:00', '2026-06-16 10:00:00');
+
+INSERT INTO member_card_record (member_id, member_card_id, status, remaining_times, remaining_amount, expire_time, purchase_time, version, card_composition, created_at, updated_at) VALUES
+((SELECT id FROM member_user WHERE member_no = 'MEM_TEST_D'), (SELECT member_card_id FROM member_card WHERE member_card_name = '10次卡'), 'ACTIVE', 0, 0.00, '2026-12-31 23:59:59', '2026-05-16 10:00:00', 0, '{}', '2026-05-16 10:00:00', '2026-06-16 10:00:00');
+
+INSERT INTO member_card_record (member_id, member_card_id, status, remaining_times, remaining_amount, expire_time, purchase_time, version, card_composition, created_at, updated_at) VALUES
+((SELECT id FROM member_user WHERE member_no = 'MEM_TEST_E'), (SELECT member_card_id FROM member_card WHERE member_card_name = '10次卡'), 'ACTIVE', 5, 0.00, '2026-12-31 23:59:59', '2026-05-16 10:00:00', 0, '{}', '2026-05-16 10:00:00', '2026-06-16 10:00:00');
+
+INSERT INTO member_card_record (member_id, member_card_id, status, remaining_times, remaining_amount, expire_time, purchase_time, version, card_composition, created_at, updated_at) VALUES
+((SELECT id FROM member_user WHERE member_no = 'MEM_TEST_F'), (SELECT member_card_id FROM member_card WHERE member_card_name = '储值卡500'), 'ACTIVE', 0, 30.00, '2027-06-16 23:59:59', '2026-05-16 10:00:00', 0, '{}', '2026-05-16 10:00:00', '2026-06-16 10:00:00');
+
+INSERT INTO member_card_record (member_id, member_card_id, status, remaining_times, remaining_amount, expire_time, purchase_time, version, card_composition, created_at, updated_at) VALUES
+((SELECT id FROM member_user WHERE member_no = 'MEM_TEST_G'), (SELECT member_card_id FROM member_card WHERE member_card_name = '储值卡500'), 'ACTIVE', 0, 200.00, '2027-06-16 23:59:59', '2026-05-16 10:00:00', 0, '{}', '2026-05-16 10:00:00', '2026-06-16 10:00:00');
+
+-- F2: V11 取消预约场景 - 3条
+INSERT INTO member_card_record (member_id, member_card_id, status, remaining_times, remaining_amount, expire_time, purchase_time, version, card_composition, created_at, updated_at) VALUES
+((SELECT id FROM member_user WHERE member_no = 'MEM_TEST_ZHANG'), (SELECT member_card_id FROM member_card WHERE member_card_name = '30天时长卡'), 'ACTIVE', 0, 0.00, '2026-07-16 23:59:59', '2026-06-16 10:00:00', 0, '{}', '2026-06-16 10:00:00', '2026-06-16 10:00:00'),
+((SELECT id FROM member_user WHERE member_no = 'MEM_TEST_LI'), (SELECT member_card_id FROM member_card WHERE member_card_name = '10次卡'), 'ACTIVE', 5, 0.00, '2026-12-31 23:59:59', '2026-05-16 10:00:00', 0, '{}', '2026-05-16 10:00:00', '2026-06-16 10:00:00'),
+((SELECT id FROM member_user WHERE member_no = 'MEM_TEST_WANG'), (SELECT member_card_id FROM member_card WHERE member_card_name = '储值卡500'), 'ACTIVE', 0, 200.00, '2027-06-16 23:59:59', '2026-05-16 10:00:00', 0, '{}', '2026-05-16 10:00:00', '2026-06-16 10:00:00');
+
+-- F3: V12 时间冲突场景 - 1条
+INSERT INTO member_card_record (member_id, member_card_id, status, remaining_times, remaining_amount, expire_time, purchase_time, version, card_composition, created_at, updated_at) VALUES
+((SELECT id FROM member_user WHERE member_no = 'MEM_TIME_CONFLICT'), (SELECT member_card_id FROM member_card WHERE member_card_name = '10次卡'), 'ACTIVE', 10, 0.00, '2026-12-31 23:59:59', '2026-06-16 10:00:00', 0, '{}', '2026-06-16 10:00:00', '2026-06-16 10:00:00');
+
+-- ============================================================
+-- Section G: 团课课程(来源 V7 / V10 / V11 / V12 / V14)
+-- ============================================================
+
+-- G1: V7 - 7种场景团课
+INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, cover_image, description, create_by, created_at, updated_at) VALUES
+('极速燃脂单车', 104, 2, '2026-06-17 16:45:00', '2026-06-17 20:20:00', 25, 0, 0, '单车房', '/images/spinning.jpg', '跟随音乐节奏变换阻力和速度,体验爬坡与冲刺的快感。', 'admin', '2026-06-15 11:00:00', '2026-06-15 11:00:00'),
+('清晨流瑜伽', 101, 1, '2026-06-20 09:00:00', '2026-06-20 10:30:00', 15, 5, 0, 'A座3楼瑜伽教室', '/images/yoga_flow.jpg', '适合有一定基础的学员,通过流畅的体式连接呼吸。', 'admin', '2026-06-15 10:00:00', '2026-06-15 10:00:00'),
+('燃脂搏击', 102, 2, '2026-06-18 18:30:00', '2026-06-18 19:30:00', 20, 20, 0, '综合训练区', '/images/kickboxing.jpg', '高强度间歇训练,名额已满。', 'coach_zhang', '2026-06-15 14:30:00', '2026-06-15 14:30:00'),
+('哈他瑜伽', 101, 1, '2026-06-16 17:45:00', '2026-06-16 18:50:00', 12, 3, 0, '瑜伽教室B', '/images/hatha_yoga.jpg', '基础哈他瑜伽,距开始不足30分钟,已停止预约。', 'coach_li', '2026-06-16 08:00:00', '2026-06-16 08:00:00'),
+('周末冥想修复', 101, 1, '2026-06-28 15:00:00', '2026-06-28 16:00:00', 12, 3, 1, '冥想室', '/images/meditation.jpg', '通过呼吸和正念冥想,该课程已被取消。', 'coach_wang', '2026-06-10 08:00:00', '2026-06-10 08:00:00'),
+('蜜桃臀塑造', 103, 3, '2026-06-10 19:00:00', '2026-06-10 20:00:00', 10, 8, 2, '私教专区', '/images/glute.jpg', '针对性训练臀部肌肉群,课程已结束。', 'coach_li', '2026-05-30 09:15:00', '2026-05-30 09:15:00'),
+('午间冥想放松', 101, 1, '2026-06-12 12:00:00', '2026-06-12 13:00:00', 15, 6, 2, '冥想室', '/images/meditation_noon.jpg', '午间冥想课程,已结束。', 'admin', '2026-06-05 09:00:00', '2026-06-05 09:00:00');
+
+-- G2: V10 预约场景
+INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, description, point_card_amount, stored_value_amount, create_by, created_at, updated_at) VALUES
+('燃脂搏击_次数卡课程', 102, 2, '2026-06-18 19:30:00', '2026-06-18 20:30:00', 20, 0, 0, '综合训练区', '消耗1次', 1, 0.00, 'admin', '2026-06-15 10:00:00', '2026-06-15 10:00:00'),
+('高端普拉提_储值卡课程', 103, 1, '2026-06-19 19:00:00', '2026-06-19 20:00:00', 15, 0, 0, '普拉提教室', '消耗储值20元', 0, 20.00, 'admin', '2026-06-15 10:00:00', '2026-06-15 10:00:00');
+
+-- G3: V11 取消预约场景
+INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, description, point_card_amount, stored_value_amount, create_by, created_at, updated_at) VALUES
+('晚间瑜伽_取消测试', 101, 1, '2026-06-19 19:00:00', '2026-06-19 20:00:00', 20, 0, 0, '瑜伽教室', '用于测试取消预约功能', 1, 30.00, 'admin', '2026-06-15 10:00:00', '2026-06-15 10:00:00');
+
+-- G4: V12 时间冲突场景
+INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, description, point_card_amount, stored_value_amount, create_by, created_at, updated_at) VALUES
+('时间冲突测试_A_13点-15点', 102, 2, '2026-06-18 13:00:00', '2026-06-18 15:00:00', 20, 0, 0, '综合训练区', '测试用团课A', 1, 0.00, 'admin', '2026-06-15 10:00:00', '2026-06-15 10:00:00'),
+('时间冲突测试_B_14点-16点', 103, 1, '2026-06-18 14:00:00', '2026-06-18 16:00:00', 15, 0, 0, '普拉提教室', '与团课A时间重叠', 1, 0.00, 'admin', '2026-06-15 10:00:00', '2026-06-15 10:00:00'),
+('时间冲突测试_C_10点-12点', 101, 1, '2026-06-18 10:00:00', '2026-06-18 12:00:00', 15, 0, 0, '瑜伽教室', '与A/B不冲突', 1, 0.00, 'admin', '2026-06-15 10:00:00', '2026-06-15 10:00:00');
+
+-- G5: V14 数据统计场景(今天)
+INSERT INTO group_course (id, course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, cover_image, description, created_at, updated_at) VALUES
+(3001, '瑜伽入门', 1, 1, '2026-06-16 08:00:00', '2026-06-16 09:00:00', 20, 15, 0, '健身房A区', 'https://example.com/yoga.jpg', '适合初学者的瑜伽课程', '2026-06-15 10:00:00', '2026-06-15 10:00:00'),
+(3002, '动感单车', 2, 2, '2026-06-16 09:30:00', '2026-06-16 10:30:00', 25, 20, 0, '健身房B区', 'https://example.com/spinning.jpg', '高强度有氧运动', '2026-06-15 11:00:00', '2026-06-15 11:00:00'),
+(3003, '普拉提', 3, 1, '2026-06-16 14:00:00', '2026-06-16 15:00:00', 15, 10, 0, '健身房C区', 'https://example.com/pilates.jpg', '核心力量训练', '2026-06-15 12:00:00', '2026-06-15 12:00:00');
+
+-- ============================================================
+-- Section H: 团课预约记录
+-- ============================================================
+
+-- H1: V11 取消预约测试
+INSERT INTO group_course_booking (course_id, member_id, member_card_id, booking_time, status, course_name, course_start_time, course_end_time, location, created_at, updated_at) VALUES
+((SELECT id FROM group_course WHERE course_name = '晚间瑜伽_取消测试'), (SELECT id FROM member_user WHERE member_no = 'MEM_TEST_ZHANG'), (SELECT id FROM member_card_record WHERE member_id = (SELECT id FROM member_user WHERE member_no = 'MEM_TEST_ZHANG')), '2026-06-16 11:00:00', '0', '晚间瑜伽_取消测试', '2026-06-19 19:00:00', '2026-06-19 20:00:00', '瑜伽教室', '2026-06-16 11:00:00', '2026-06-16 11:00:00'),
+((SELECT id FROM group_course WHERE course_name = '晚间瑜伽_取消测试'), (SELECT id FROM member_user WHERE member_no = 'MEM_TEST_LI'), (SELECT id FROM member_card_record WHERE member_id = (SELECT id FROM member_user WHERE member_no = 'MEM_TEST_LI')), '2026-06-16 11:30:00', '0', '晚间瑜伽_取消测试', '2026-06-19 19:00:00', '2026-06-19 20:00:00', '瑜伽教室', '2026-06-16 11:30:00', '2026-06-16 11:30:00'),
+((SELECT id FROM group_course WHERE course_name = '晚间瑜伽_取消测试'), (SELECT id FROM member_user WHERE member_no = 'MEM_TEST_WANG'), (SELECT id FROM member_card_record WHERE member_id = (SELECT id FROM member_user WHERE member_no = 'MEM_TEST_WANG')), '2026-06-16 12:00:00', '0', '晚间瑜伽_取消测试', '2026-06-19 19:00:00', '2026-06-19 20:00:00', '瑜伽教室', '2026-06-16 12:00:00', '2026-06-16 12:00:00');
+
+UPDATE group_course SET current_members = current_members + 3 WHERE course_name = '晚间瑜伽_取消测试';
+
+-- H2: V14 数据统计预约 - 今天(6月16日)
+INSERT INTO group_course_booking (id, member_id, member_card_id, course_id, booking_time, status, created_at, updated_at) VALUES
+(4001, 1001, 1, 3001, '2026-06-16 08:00:00', '2', '2026-06-15 20:00:00', '2026-06-16 08:30:00'),
+(4002, 1002, 2, 3001, '2026-06-16 08:00:00', '2', '2026-06-15 21:00:00', '2026-06-16 08:30:00'),
+(4003, 1003, 3, 3001, '2026-06-16 08:00:00', '3', '2026-06-15 22:00:00', '2026-06-16 09:00:00'),
+(4004, 1004, 4, 3002, '2026-06-16 09:30:00', '2', '2026-06-15 19:00:00', '2026-06-16 09:30:00'),
+(4005, 1005, 5, 3002, '2026-06-16 09:30:00', '1', '2026-06-15 20:30:00', '2026-06-16 09:00:00'),
+(4006, 1006, 6, 3002, '2026-06-16 09:30:00', '2', '2026-06-15 21:30:00', '2026-06-16 09:30:00'),
+(4007, 1007, 7, 3003, '2026-06-16 14:00:00', '2', '2026-06-15 22:30:00', '2026-06-16 14:00:00'),
+(4008, 1008, 8, 3003, '2026-06-16 14:00:00', '3', '2026-06-16 08:00:00', '2026-06-16 14:00:00'),
+(4009, 1009, 9, 3003, '2026-06-16 14:00:00', '2', '2026-06-16 09:00:00', '2026-06-16 14:00:00'),
+(4010, 1010, 10, 3001, '2026-06-16 08:00:00', '2', '2026-06-16 07:00:00', '2026-06-16 08:00:00'),
+(4011, 1011, 11, 3002, '2026-06-16 09:30:00', '1', '2026-06-16 08:30:00', '2026-06-16 09:00:00'),
+(4012, 1012, 12, 3003, '2026-06-16 14:00:00', '2', '2026-06-16 10:00:00', '2026-06-16 14:00:00');
+
+-- H3: V14 数据统计预约 - 昨天(6月15日)
+INSERT INTO group_course_booking (id, member_id, member_card_id, course_id, booking_time, status, created_at, updated_at) VALUES
+(4013, 1001, 1, 3001, '2026-06-15 08:00:00', '2', '2026-06-14 20:00:00', '2026-06-15 08:30:00'),
+(4014, 1002, 2, 3001, '2026-06-15 08:00:00', '2', '2026-06-14 21:00:00', '2026-06-15 08:30:00'),
+(4015, 1003, 3, 3002, '2026-06-15 09:30:00', '3', '2026-06-14 22:00:00', '2026-06-15 09:30:00'),
+(4016, 1004, 4, 3002, '2026-06-15 09:30:00', '2', '2026-06-14 19:00:00', '2026-06-15 09:30:00'),
+(4017, 1005, 5, 3003, '2026-06-15 14:00:00', '1', '2026-06-14 20:30:00', '2026-06-15 13:00:00'),
+(4018, 1006, 6, 3003, '2026-06-15 14:00:00', '2', '2026-06-14 21:30:00', '2026-06-15 14:00:00');
+
+-- H4: V14 数据统计预约 - 前天(6月14日)
+INSERT INTO group_course_booking (id, member_id, member_card_id, course_id, booking_time, status, created_at, updated_at) VALUES
+(4019, 1001, 1, 3002, '2026-06-14 09:30:00', '2', '2026-06-13 20:00:00', '2026-06-14 09:30:00'),
+(4020, 1002, 2, 3002, '2026-06-14 09:30:00', '2', '2026-06-13 21:00:00', '2026-06-14 09:30:00'),
+(4021, 1003, 3, 3003, '2026-06-14 14:00:00', '2', '2026-06-13 22:00:00', '2026-06-14 14:00:00'),
+(4022, 1004, 4, 3003, '2026-06-14 14:00:00', '3', '2026-06-13 19:00:00', '2026-06-14 14:00:00');
+
+-- ============================================================
+-- Section I: 签到记录(来源 V14,基于 2026-06-16)
+-- ============================================================
+
+INSERT INTO sign_in_record (id, member_id, sign_in_time, sign_in_type, sign_in_status, source, is_delete) VALUES
+(2001, 1001, '2026-06-16 08:00:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false),
+(2002, 1002, '2026-06-16 08:15:00', 'MANUAL', 'SUCCESS', 'PC_BACKEND', false),
+(2003, 1003, '2026-06-16 08:30:00', 'FACE', 'SUCCESS', 'MINI_PROGRAM', false),
+(2004, 1004, '2026-06-16 09:00:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false),
+(2005, 1005, '2026-06-16 09:15:00', 'MANUAL', 'SUCCESS', 'PC_BACKEND', false),
+(2006, 1006, '2026-06-16 09:30:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false),
+(2007, 1007, '2026-06-16 10:00:00', 'FACE', 'SUCCESS', 'MINI_PROGRAM', false),
+(2008, 1008, '2026-06-16 10:15:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false),
+(2009, 1009, '2026-06-16 10:30:00', 'MANUAL', 'SUCCESS', 'PC_BACKEND', false),
+(2010, 1010, '2026-06-16 11:00:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false),
+(2011, 1011, '2026-06-16 11:15:00', 'FACE', 'FAIL', 'MINI_PROGRAM', false),
+(2012, 1012, '2026-06-16 11:30:00', 'MANUAL', 'SUCCESS', 'PC_BACKEND', false);
+
+INSERT INTO sign_in_record (id, member_id, sign_in_time, sign_in_type, sign_in_status, source, is_delete) VALUES
+(2013, 1001, '2026-06-15 07:30:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false),
+(2014, 1002, '2026-06-15 08:00:00', 'MANUAL', 'SUCCESS', 'PC_BACKEND', false),
+(2015, 1003, '2026-06-15 08:30:00', 'FACE', 'SUCCESS', 'MINI_PROGRAM', false),
+(2016, 1004, '2026-06-15 09:00:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false),
+(2017, 1005, '2026-06-15 09:30:00', 'MANUAL', 'SUCCESS', 'PC_BACKEND', false),
+(2018, 1006, '2026-06-15 10:00:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false),
+(2019, 1007, '2026-06-15 10:30:00', 'FACE', 'SUCCESS', 'MINI_PROGRAM', false),
+(2020, 1008, '2026-06-15 11:00:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false);
+
+INSERT INTO sign_in_record (id, member_id, sign_in_time, sign_in_type, sign_in_status, source, is_delete) VALUES
+(2021, 1001, '2026-06-14 07:00:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false),
+(2022, 1002, '2026-06-14 07:30:00', 'MANUAL', 'SUCCESS', 'PC_BACKEND', false),
+(2023, 1003, '2026-06-14 08:00:00', 'FACE', 'FAIL', 'MINI_PROGRAM', false),
+(2024, 1004, '2026-06-14 08:30:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false),
+(2025, 1005, '2026-06-14 09:00:00', 'MANUAL', 'SUCCESS', 'PC_BACKEND', false);
+
+-- ============================================================
+-- Section J: 团课推荐测试数据(来源 V18,基于 2026-06-16)
+-- ============================================================
+
+INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES
+((SELECT id FROM group_course WHERE course_name = '极速燃脂单车'), '本周热门推荐', '极速燃脂单车课程,跟随音乐节奏变换阻力和速度,体验爬坡与冲刺的快感,一节课消耗800大卡!', '教练专业,课程内容丰富,深受学员喜爱,燃脂效果显著', 20, true, 'admin', '2026-06-16 10:00:00', '2026-06-16 10:00:00');
+
+INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES
+((SELECT id FROM group_course WHERE course_name = '清晨流瑜伽'), '新手友好推荐', '清晨流瑜伽课程,适合有一定基础的学员,通过流畅的体式连接呼吸,唤醒身体能量。', '适合新手入门,教练耐心指导,课程节奏适中', 15, true, 'admin', '2026-06-16 11:00:00', '2026-06-16 11:00:00');
+
+INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES
+((SELECT id FROM group_course WHERE course_name = '燃脂搏击'), '高强度燃脂', '燃脂搏击课程,高强度间歇训练,配合音乐快速燃脂,释放压力。', '高强度训练,适合进阶学员,快速燃脂塑形', 10, false, 'admin', '2026-06-16 12:00:00', '2026-06-16 12:00:00');
+
+INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES
+((SELECT id FROM group_course WHERE course_name = '哈他瑜伽'), '基础瑜伽推荐', '基础哈他瑜伽课程,适合所有级别学员,通过基础体式练习提升身体柔韧性和平衡能力。', '零基础友好,适合所有健身水平,放松身心', 12, true, 'coach_li', '2026-06-16 13:00:00', '2026-06-16 13:00:00');
+
+INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES
+((SELECT id FROM group_course WHERE course_name = '蜜桃臀塑造'), '塑形热门课程', '蜜桃臀塑造课程,针对性训练臀部肌肉群,打造完美曲线。', '专业私教指导,动作标准,效果显著,深受女性学员喜爱', 18, true, 'coach_li', '2026-06-10 09:15:00', '2026-06-10 09:15:00');
+
+INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES
+((SELECT id FROM group_course WHERE course_name = '午间冥想放松'), '午间放松推荐', '午间冥想放松课程,通过呼吸和正念冥想,深度放松身心,缓解工作压力。', '适合上班族,午间放松充电,提升下午工作效率', 8, true, 'admin', '2026-06-12 09:00:00', '2026-06-12 09:00:00');
+
+INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES
+((SELECT id FROM group_course WHERE course_name = '极速燃脂单车'), '减脂首选课程', '想要快速减脂?极速燃脂单车是你的最佳选择!专业教练带领,科学训练计划。', '减脂效果最佳,课程强度适中,适合想要快速瘦身的学员', 16, true, 'coach_zhang', '2026-06-16 14:00:00', '2026-06-16 14:00:00');
+
+INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES
+((SELECT id FROM group_course WHERE course_name = '清晨流瑜伽'), '晨练优选', '清晨流瑜伽,唤醒身体能量,开启活力一天!适合晨练爱好者。', '晨练最佳选择,提升身体活力,改善精神状态', 14, true, 'coach_wang', '2026-06-16 15:00:00', '2026-06-16 15:00:00');
+
+INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES
+((SELECT id FROM group_course WHERE course_name = '哈他瑜伽'), '身心平衡推荐', '哈他瑜伽课程,通过体式练习和呼吸控制,达到身心平衡,提升整体健康水平。', '改善身体柔韧性,增强核心力量,提升身体协调性', 11, true, 'coach_li', '2026-06-16 16:00:00', '2026-06-16 16:00:00');
+
+INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES
+((SELECT id FROM group_course WHERE course_name = '午间冥想放松'), '职场减压课程', '午间冥想放松,专为职场人士设计,快速缓解工作压力,提升工作状态。', '职场减压首选,课程时间短,效果显著', 9, false, 'admin', '2026-06-12 10:00:00', '2026-06-12 10:00:00');
+
+-- ============================================================
+-- Section K: 统计说明
+-- ============================================================
+-- 今日(2026-06-16)统计预期:
+-- 新增会员: 3 (1005, 1006, 1007)
+-- 签到会员: 12
+-- 预约总数: 12 (4001-4012),取消: 2,出席: 8,缺席: 2
+-- 签到总数: 12,成功: 11,失败: 1
+
+-- ============================================================
+-- Section L: 测试场景汇总
+-- ============================================================
+-- 1. V7 团课预约场景:
+-- - 极速燃脂单车: 明天,0人,可预约
+-- - 清晨流瑜伽: 6月20日,5/15人,可预约
+-- - 燃脂搏击: 6月18日,满员,不可预约
+-- - 哈他瑜伽: 今天17:45,距开始<30min,不可预约
+-- - 周末冥想修复: 6月28日,已取消(status=1)
+-- - 蜜桃臀塑造: 6月10日,已结束(status=2)
+-- - 午间冥想放松: 6月12日,已结束(status=2)
+--
+-- 2. V10 会员卡预约资格校验:
+-- - 用户A: 无会员卡 → "无可用会员卡"
+-- - 用户B: 时长卡过期 → "会员卡已过期"
+-- - 会员C: 时长卡有效 → 可正常预约
+-- - 会员D: 次数为0 → "剩余次数不足"
+-- - 会员E: 次数充足 → 可正常预约
+-- - 会员F: 储值卡30元,课程需20元 → 可正常预约
+-- - 会员G: 储值卡200元,课程需20元 → 可正常预约
+--
+-- 3. V11 取消预约:
+-- - 张三(时长卡)、李四(次数卡)、王五(储值卡) 预约了晚间瑜伽_取消测试
+-- - 取消后应恢复对应卡状态,释放课程名额
+--
+-- 4. V12 时间冲突检测:
+-- - 团课A: 6/18 13:00-15:00, 团课B: 6/18 14:00-16:00 → 时间重叠
+-- - 团课C: 6/18 10:00-12:00 → 与A/B不冲突
+--
+-- 5. V14 数据统计:
+-- - 3天签到/预约数据,覆盖今日/昨日/前日
+--
+-- 6. V18 团课推荐:
+-- - 10条推荐记录,覆盖不同优先级和启用状态
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V13__Create_SignInRecord_table.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V13__Create_SignInRecord_table.sql
deleted file mode 100644
index ca01bc4..0000000
--- a/gym-manage-api/manage-db/src/main/resources/db/migration/V13__Create_SignInRecord_table.sql
+++ /dev/null
@@ -1,99 +0,0 @@
--- ============================================
--- 签到记录表(sign_in_record)
--- ============================================
-
--- Step 1: 创建 sign_in_record 表
-CREATE TABLE IF NOT EXISTS sign_in_record (
- -- ========== 主键和基础字段 ==========
- id BIGSERIAL PRIMARY KEY, -- 自增主键
- created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 记录创建时间
- updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 记录更新时间
-
- -- ========== 会员相关字段 ==========
- member_id BIGINT NOT NULL, -- 会员ID,关联member_user表
- member_card_id BIGINT, -- 签到时使用的会员卡ID
-
- -- ========== 签到核心字段 ==========
- sign_in_time TIMESTAMP NOT NULL, -- 签到入场时间
- sign_in_type VARCHAR(20) NOT NULL, -- 签到方式:QR_CODE-扫码签到,MANUAL-手动签到,FACE-人脸识别
- sign_in_status VARCHAR(20) NOT NULL DEFAULT 'SUCCESS', -- 签到状态:SUCCESS-成功,FAILED-失败
-
- -- ========== 验证和错误信息 ==========
- verification_details TEXT, -- JSONB格式,存储会员卡验证时的快照数据
- fail_reason VARCHAR(500), -- 失败时的具体原因文案
-
- -- ========== 操作人信息 ==========
- operator_id BIGINT, -- 操作人ID(前台人员),自助签到时为NULL
- operator_name VARCHAR(100), -- 操作人姓名冗余
-
- -- ========== 设备和环境信息 ==========
- device_info VARCHAR(200), -- 签到设备标识或型号
- ip_address VARCHAR(50), -- 客户端IP地址
- source VARCHAR(20) NOT NULL, -- 签到来源:MINI_PROGRAM-小程序扫码,PC_BACKEND-后台管理端
-
- -- ========== 软删除字段 ==========
- is_delete BOOLEAN DEFAULT FALSE -- 软删除标识:false-未删除,true-已删除
-);
-
--- Step 2: 创建索引
--- 会员ID索引(加速按会员查询签到记录)
-CREATE INDEX IF NOT EXISTS idx_sign_in_record_member_id ON sign_in_record(member_id);
-
--- 签到时间索引(加速按时间范围查询)
-CREATE INDEX IF NOT EXISTS idx_sign_in_record_sign_in_time ON sign_in_record(sign_in_time);
-
--- 签到状态索引(加速按状态筛选)
-CREATE INDEX IF NOT EXISTS idx_sign_in_record_sign_in_status ON sign_in_record(sign_in_status);
-
--- 会员卡ID索引(加速按会员卡查询)
-CREATE INDEX IF NOT EXISTS idx_sign_in_record_member_card_id ON sign_in_record(member_card_id);
-
--- 操作人ID索引(加速按操作人查询)
-CREATE INDEX IF NOT EXISTS idx_sign_in_record_operator_id ON sign_in_record(operator_id);
-
--- 签到来源索引(加速按来源统计)
-CREATE INDEX IF NOT EXISTS idx_sign_in_record_source ON sign_in_record(source);
-
--- 软删除索引(加速查询未删除的记录)
-CREATE INDEX IF NOT EXISTS idx_sign_in_record_is_delete ON sign_in_record(is_delete);
-
--- 复合索引:会员ID + 签到时间(加速会员签到历史查询)
-CREATE INDEX IF NOT EXISTS idx_sign_in_record_member_time ON sign_in_record(member_id, sign_in_time);
-
--- 复合索引:签到状态 + 签到时间(加速统计数据查询)
-CREATE INDEX IF NOT EXISTS idx_sign_in_record_status_time ON sign_in_record(sign_in_status, sign_in_time);
-
--- 复合索引:签到日期(用于每日统计,使用表达式索引)
-CREATE INDEX IF NOT EXISTS idx_sign_in_record_sign_in_date ON sign_in_record(DATE(sign_in_time));
-
--- Step 3: 添加外键约束(可选,根据业务需求决定)
--- 注意:如果member_user表使用了软删除,外键约束可能需要谨慎使用
--- ALTER TABLE sign_in_record
--- ADD CONSTRAINT fk_sign_in_record_member
--- FOREIGN KEY (member_id) REFERENCES member_user(id) ON DELETE SET NULL;
-
--- Step 4: 添加注释
-COMMENT ON TABLE sign_in_record IS '会员到店签到记录表';
-
-COMMENT ON COLUMN sign_in_record.id IS '自增主键';
-COMMENT ON COLUMN sign_in_record.created_at IS '记录创建时间';
-COMMENT ON COLUMN sign_in_record.updated_at IS '记录更新时间';
-
-COMMENT ON COLUMN sign_in_record.member_id IS '会员ID,关联member_user表';
-COMMENT ON COLUMN sign_in_record.member_card_id IS '签到时使用的会员卡ID';
-
-COMMENT ON COLUMN sign_in_record.sign_in_time IS '签到入场时间';
-COMMENT ON COLUMN sign_in_record.sign_in_type IS '签到方式:QR_CODE-扫码签到,MANUAL-手动签到,FACE-人脸识别';
-COMMENT ON COLUMN sign_in_record.sign_in_status IS '签到状态:SUCCESS-成功,FAILED-失败';
-
-COMMENT ON COLUMN sign_in_record.verification_details IS 'JSON格式,存储会员卡验证时的快照数据(包含卡类型、剩余次数/金额、有效期等)';
-COMMENT ON COLUMN sign_in_record.fail_reason IS '失败时的具体原因文案';
-
-COMMENT ON COLUMN sign_in_record.operator_id IS '操作人ID(前台人员),自助签到时为NULL';
-COMMENT ON COLUMN sign_in_record.operator_name IS '操作人姓名冗余(避免关联查询)';
-
-COMMENT ON COLUMN sign_in_record.device_info IS '签到设备标识或型号';
-COMMENT ON COLUMN sign_in_record.ip_address IS '客户端IP地址';
-COMMENT ON COLUMN sign_in_record.source IS '签到来源:MINI_PROGRAM-小程序扫码,PC_BACKEND-后台管理端';
-
-COMMENT ON COLUMN sign_in_record.is_delete IS '软删除标识:false-未删除,true-已删除';
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V14__Insert_DataCount_Test_Data.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V14__Insert_DataCount_Test_Data.sql
deleted file mode 100644
index 01ccc12..0000000
--- a/gym-manage-api/manage-db/src/main/resources/db/migration/V14__Insert_DataCount_Test_Data.sql
+++ /dev/null
@@ -1,120 +0,0 @@
--- 数据统计模块测试数据
--- 用于测试会员、预约、签到统计接口
-
--- 插入测试会员数据
-INSERT INTO member_user (id, member_no, nickname, phone, created_at, updated_at, is_deleted) VALUES
-(1001, 'M20260601001', '张三', '13800138001', '2026-06-01 08:00:00', '2026-06-01 08:00:00', false),
-(1002, 'M20260601002', '李四', '13800138002', '2026-06-01 09:00:00', '2026-06-01 09:00:00', false),
-(1003, 'M20260602003', '王五', '13800138003', '2026-06-02 10:00:00', '2026-06-02 10:00:00', false),
-(1004, 'M20260603004', '赵六', '13800138004', '2026-06-03 11:00:00', '2026-06-03 11:00:00', false),
-(1005, 'M20260609005', '钱七', '13800138005', '2026-06-09 08:00:00', '2026-06-09 08:00:00', false),
-(1006, 'M20260609006', '孙八', '13800138006', '2026-06-09 09:00:00', '2026-06-09 09:00:00', false),
-(1007, 'M20260609007', '周九', '13800138007', '2026-06-09 10:00:00', '2026-06-09 10:00:00', false),
-(1008, 'M20260604008', '吴十', '13800138008', '2026-06-04 14:00:00', '2026-06-04 14:00:00', false),
-(1009, 'M20260605009', '郑十一', '13800138009', '2026-06-05 15:00:00', '2026-06-05 15:00:00', false),
-(1010, 'M20260606010', '王十二', '13800138010', '2026-06-06 16:00:00', '2026-06-06 16:00:00', false),
-(1011, 'M20260607011', '陈十三', '13800138011', '2026-06-07 17:00:00', '2026-06-07 17:00:00', false),
-(1012, 'M20260608012', '刘十四', '13800138012', '2026-06-08 18:00:00', '2026-06-08 18:00:00', false)
-ON CONFLICT (id) DO NOTHING;
-
--- 插入测试签到记录数据 (今天的数据)
-INSERT INTO sign_in_record (id, member_id, sign_in_time, sign_in_type, sign_in_status, source, is_delete) VALUES
-(2001, 1001, '2026-06-09 08:00:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false),
-(2002, 1002, '2026-06-09 08:15:00', 'MANUAL', 'SUCCESS', 'PC_BACKEND', false),
-(2003, 1003, '2026-06-09 08:30:00', 'FACE', 'SUCCESS', 'MINI_PROGRAM', false),
-(2004, 1004, '2026-06-09 09:00:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false),
-(2005, 1005, '2026-06-09 09:15:00', 'MANUAL', 'SUCCESS', 'PC_BACKEND', false),
-(2006, 1006, '2026-06-09 09:30:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false),
-(2007, 1007, '2026-06-09 10:00:00', 'FACE', 'SUCCESS', 'MINI_PROGRAM', false),
-(2008, 1008, '2026-06-09 10:15:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false),
-(2009, 1009, '2026-06-09 10:30:00', 'MANUAL', 'SUCCESS', 'PC_BACKEND', false),
-(2010, 1010, '2026-06-09 11:00:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false),
-(2011, 1011, '2026-06-09 11:15:00', 'FACE', 'FAIL', 'MINI_PROGRAM', false),
-(2012, 1012, '2026-06-09 11:30:00', 'MANUAL', 'SUCCESS', 'PC_BACKEND', false)
-ON CONFLICT (id) DO NOTHING;
-
--- 插入测试签到记录数据 (昨天的数据)
-INSERT INTO sign_in_record (id, member_id, sign_in_time, sign_in_type, sign_in_status, source, is_delete) VALUES
-(2013, 1001, '2026-06-08 07:30:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false),
-(2014, 1002, '2026-06-08 08:00:00', 'MANUAL', 'SUCCESS', 'PC_BACKEND', false),
-(2015, 1003, '2026-06-08 08:30:00', 'FACE', 'SUCCESS', 'MINI_PROGRAM', false),
-(2016, 1004, '2026-06-08 09:00:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false),
-(2017, 1005, '2026-06-08 09:30:00', 'MANUAL', 'SUCCESS', 'PC_BACKEND', false),
-(2018, 1006, '2026-06-08 10:00:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false),
-(2019, 1007, '2026-06-08 10:30:00', 'FACE', 'SUCCESS', 'MINI_PROGRAM', false),
-(2020, 1008, '2026-06-08 11:00:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false)
-ON CONFLICT (id) DO NOTHING;
-
--- 插入测试签到记录数据 (前天的数据)
-INSERT INTO sign_in_record (id, member_id, sign_in_time, sign_in_type, sign_in_status, source, is_delete) VALUES
-(2021, 1001, '2026-06-07 07:00:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false),
-(2022, 1002, '2026-06-07 07:30:00', 'MANUAL', 'SUCCESS', 'PC_BACKEND', false),
-(2023, 1003, '2026-06-07 08:00:00', 'FACE', 'FAIL', 'MINI_PROGRAM', false),
-(2024, 1004, '2026-06-07 08:30:00', 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', false),
-(2025, 1005, '2026-06-07 09:00:00', 'MANUAL', 'SUCCESS', 'PC_BACKEND', false)
-ON CONFLICT (id) DO NOTHING;
-
--- 插入测试团课数据
-INSERT INTO group_course (id, course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, cover_image, description, created_at, updated_at) VALUES
-(3001, '瑜伽入门', 1, 1, '2026-06-09 08:00:00', '2026-06-09 09:00:00', 20, 15, 0, '健身房A区', 'https://example.com/yoga.jpg', '适合初学者的瑜伽课程', '2026-06-01 10:00:00', '2026-06-01 10:00:00'),
-(3002, '动感单车', 2, 2, '2026-06-09 09:30:00', '2026-06-09 10:30:00', 25, 20, 0, '健身房B区', 'https://example.com/spinning.jpg', '高强度有氧运动', '2026-06-01 11:00:00', '2026-06-01 11:00:00'),
-(3003, '普拉提', 3, 1, '2026-06-09 14:00:00', '2026-06-09 15:00:00', 15, 10, 0, '健身房C区', 'https://example.com/pilates.jpg', '核心力量训练', '2026-06-01 12:00:00', '2026-06-01 12:00:00')
-ON CONFLICT (id) DO NOTHING;
-
--- 插入测试团课预约数据 (今天的数据)
-INSERT INTO group_course_booking (id, member_id, member_card_id, course_id, booking_time, status, created_at, updated_at) VALUES
-(4001, 1001, 1, 3001, '2026-06-09 08:00:00', '2', '2026-06-08 20:00:00', '2026-06-09 08:30:00'),
-(4002, 1002, 2, 3001, '2026-06-09 08:00:00', '2', '2026-06-08 21:00:00', '2026-06-09 08:30:00'),
-(4003, 1003, 3, 3001, '2026-06-09 08:00:00', '3', '2026-06-08 22:00:00', '2026-06-09 09:00:00'),
-(4004, 1004, 4, 3002, '2026-06-09 09:30:00', '2', '2026-06-08 19:00:00', '2026-06-09 09:30:00'),
-(4005, 1005, 5, 3002, '2026-06-09 09:30:00', '1', '2026-06-08 20:30:00', '2026-06-09 09:00:00'),
-(4006, 1006, 6, 3002, '2026-06-09 09:30:00', '2', '2026-06-08 21:30:00', '2026-06-09 09:30:00'),
-(4007, 1007, 7, 3003, '2026-06-09 14:00:00', '2', '2026-06-08 22:30:00', '2026-06-09 14:00:00'),
-(4008, 1008, 8, 3003, '2026-06-09 14:00:00', '3', '2026-06-09 08:00:00', '2026-06-09 14:00:00'),
-(4009, 1009, 9, 3003, '2026-06-09 14:00:00', '2', '2026-06-09 09:00:00', '2026-06-09 14:00:00'),
-(4010, 1010, 10, 3001, '2026-06-09 08:00:00', '2', '2026-06-09 07:00:00', '2026-06-09 08:00:00'),
-(4011, 1011, 11, 3002, '2026-06-09 09:30:00', '1', '2026-06-09 08:30:00', '2026-06-09 09:00:00'),
-(4012, 1012, 12, 3003, '2026-06-09 14:00:00', '2', '2026-06-09 10:00:00', '2026-06-09 14:00:00')
-ON CONFLICT (id) DO NOTHING;
-
--- 插入测试团课预约数据 (昨天的数据)
-INSERT INTO group_course_booking (id, member_id, member_card_id, course_id, booking_time, status, created_at, updated_at) VALUES
-(4013, 1001, 1, 3001, '2026-06-08 08:00:00', '2', '2026-06-07 20:00:00', '2026-06-08 08:30:00'),
-(4014, 1002, 2, 3001, '2026-06-08 08:00:00', '2', '2026-06-07 21:00:00', '2026-06-08 08:30:00'),
-(4015, 1003, 3, 3002, '2026-06-08 09:30:00', '3', '2026-06-07 22:00:00', '2026-06-08 09:30:00'),
-(4016, 1004, 4, 3002, '2026-06-08 09:30:00', '2', '2026-06-07 19:00:00', '2026-06-08 09:30:00'),
-(4017, 1005, 5, 3003, '2026-06-08 14:00:00', '1', '2026-06-07 20:30:00', '2026-06-08 13:00:00'),
-(4018, 1006, 6, 3003, '2026-06-08 14:00:00', '2', '2026-06-07 21:30:00', '2026-06-08 14:00:00')
-ON CONFLICT (id) DO NOTHING;
-
--- 插入测试团课预约数据 (前天的数据)
-INSERT INTO group_course_booking (id, member_id, member_card_id, course_id, booking_time, status, created_at, updated_at) VALUES
-(4019, 1001, 1, 3002, '2026-06-07 09:30:00', '2', '2026-06-06 20:00:00', '2026-06-07 09:30:00'),
-(4020, 1002, 2, 3002, '2026-06-07 09:30:00', '2', '2026-06-06 21:00:00', '2026-06-07 09:30:00'),
-(4021, 1003, 3, 3003, '2026-06-07 14:00:00', '2', '2026-06-06 22:00:00', '2026-06-07 14:00:00'),
-(4022, 1004, 4, 3003, '2026-06-07 14:00:00', '3', '2026-06-06 19:00:00', '2026-06-07 14:00:00')
-ON CONFLICT (id) DO NOTHING;
-
--- 预期统计结果 (今日):
--- 会员统计:
--- 新增会员: 3 (1005, 1006, 1007)
--- 活跃会员: 12 (所有会员今日或近期有活动)
--- 总会员数: 12
--- 签到会员: 12
--- 预约会员: 9
--- 取消会员: 2
-
--- 预约统计:
--- 总预约: 12
--- 取消: 2
--- 出席: 8
--- 缺席: 2
--- 出席率: 8/10 = 80%
--- 取消率: 2/12 = 16.67%
-
--- 签到统计:
--- 总签到: 12
--- 成功: 11
--- 失败: 1
--- 成功率: 11/12 = 91.67%
--- 类型分布: QR_CODE=5, MANUAL=4, FACE=3
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V18__Insert_GroupCourse_Recommend_test_data.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V18__Insert_GroupCourse_Recommend_test_data.sql
deleted file mode 100644
index 87f53c6..0000000
--- a/gym-manage-api/manage-db/src/main/resources/db/migration/V18__Insert_GroupCourse_Recommend_test_data.sql
+++ /dev/null
@@ -1,43 +0,0 @@
--- ============================================
--- 团课推荐测试数据
--- ============================================
-
--- 推荐数据1: 极速燃脂单车 - 高优先级推荐(热门课程)
-INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES
- (1, '本周热门推荐', '极速燃脂单车课程,跟随音乐节奏变换阻力和速度,体验爬坡与冲刺的快感,一节课消耗800大卡!', '教练专业,课程内容丰富,深受学员喜爱,燃脂效果显著', 20, true, 'admin', '2026-06-15 10:00:00', '2026-06-15 10:00:00');
-
--- 推荐数据2: 清晨流瑜伽 - 中等优先级推荐(适合新手)
-INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES
- (2, '新手友好推荐', '清晨流瑜伽课程,适合有一定基础的学员,通过流畅的体式连接呼吸,唤醒身体能量。', '适合新手入门,教练耐心指导,课程节奏适中', 15, true, 'admin', '2026-06-15 11:00:00', '2026-06-15 11:00:00');
-
--- 推荐数据3: 燃脂搏击 - 低优先级推荐(满员课程,已禁用)
-INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES
- (3, '高强度燃脂', '燃脂搏击课程,高强度间歇训练,配合音乐快速燃脂,释放压力。', '高强度训练,适合进阶学员,快速燃脂塑形', 10, false, 'admin', '2026-06-15 12:00:00', '2026-06-15 12:00:00');
-
--- 推荐数据4: 哈他瑜伽 - 中等优先级推荐(基础课程)
-INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES
- (4, '基础瑜伽推荐', '基础哈他瑜伽课程,适合所有级别学员,通过基础体式练习提升身体柔韧性和平衡能力。', '零基础友好,适合所有健身水平,放松身心', 12, true, 'coach_li', '2026-06-15 13:00:00', '2026-06-15 13:00:00');
-
--- 推荐数据5: 蜜桃臀塑造 - 高优先级推荐(热门课程,但课程已结束)
-INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES
- (6, '塑形热门课程', '蜜桃臀塑造课程,针对性训练臀部肌肉群,打造完美曲线。', '专业私教指导,动作标准,效果显著,深受女性学员喜爱', 18, true, 'coach_li', '2026-05-25 09:15:00', '2026-05-25 09:15:00');
-
--- 推荐数据6: 午间冥想放松 - 低优先级推荐(放松课程)
-INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES
- (7, '午间放松推荐', '午间冥想放松课程,通过呼吸和正念冥想,深度放松身心,缓解工作压力。', '适合上班族,午间放松充电,提升下午工作效率', 8, true, 'admin', '2026-05-25 09:00:00', '2026-05-25 09:00:00');
-
--- 推荐数据7: 极速燃脂单车 - 第二个推荐(不同角度推荐)
-INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES
- (1, '减脂首选课程', '想要快速减脂?极速燃脂单车是你的最佳选择!专业教练带领,科学训练计划。', '减脂效果最佳,课程强度适中,适合想要快速瘦身的学员', 16, true, 'coach_zhang', '2026-06-15 14:00:00', '2026-06-15 14:00:00');
-
--- 推荐数据8: 清晨流瑜伽 - 第二个推荐(不同角度推荐)
-INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES
- (2, '晨练优选', '清晨流瑜伽,唤醒身体能量,开启活力一天!适合晨练爱好者。', '晨练最佳选择,提升身体活力,改善精神状态', 14, true, 'coach_wang', '2026-06-15 15:00:00', '2026-06-15 15:00:00');
-
--- 推荐数据9: 哈他瑜伽 - 第二个推荐(不同角度推荐)
-INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES
- (4, '身心平衡推荐', '哈他瑜伽课程,通过体式练习和呼吸控制,达到身心平衡,提升整体健康水平。', '改善身体柔韧性,增强核心力量,提升身体协调性', 11, true, 'coach_li', '2026-06-15 16:00:00', '2026-06-15 16:00:00');
-
--- 推荐数据10: 午间冥想放松 - 第二个推荐(不同角度推荐,已禁用)
-INSERT INTO group_course_recommend (course_id, recommend_title, recommend_content, recommend_reason, priority, is_active, create_by, created_at, updated_at) VALUES
- (7, '职场减压课程', '午间冥想放松,专为职场人士设计,快速缓解工作压力,提升工作状态。', '职场减压首选,课程时间短,效果显著', 9, false, 'admin', '2026-05-25 10:00:00', '2026-05-25 10:00:00');
\ No newline at end of file
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V20__Add_deleted_at_to_member_user.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V20__Add_deleted_at_to_member_user.sql
new file mode 100644
index 0000000..445525b
--- /dev/null
+++ b/gym-manage-api/manage-db/src/main/resources/db/migration/V20__Add_deleted_at_to_member_user.sql
@@ -0,0 +1,9 @@
+-- ============================================
+-- V20: 为 member_user 表添加 deleted_at 字段
+-- 原因:BaseEntity 已映射该字段,但 member_user 表缺失
+-- ============================================
+
+ALTER TABLE member_user
+ ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMP;
+
+COMMENT ON COLUMN member_user.deleted_at IS '软删除时间';
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V21__Add_updated_at_to_member_card_transactions.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V21__Add_updated_at_to_member_card_transactions.sql
new file mode 100644
index 0000000..ea780bd
--- /dev/null
+++ b/gym-manage-api/manage-db/src/main/resources/db/migration/V21__Add_updated_at_to_member_card_transactions.sql
@@ -0,0 +1,9 @@
+-- ============================================
+-- V21: 为 member_card_transactions 表添加 updated_at 字段
+-- 原因:MemberCardTransaction 继承 BaseEntity 已映射该字段,但表缺失
+-- ============================================
+
+ALTER TABLE member_card_transactions
+ ADD COLUMN IF NOT EXISTS updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
+
+COMMENT ON COLUMN member_card_transactions.updated_at IS '更新时间';
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V22__Add_Business_Permissions.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V22__Add_Business_Permissions.sql
new file mode 100644
index 0000000..d038da2
--- /dev/null
+++ b/gym-manage-api/manage-db/src/main/resources/db/migration/V22__Add_Business_Permissions.sql
@@ -0,0 +1,100 @@
+-- ============================================
+-- V22: 新增业务模块权限
+-- 描述: 为会员、会员卡、团课、签到、数据统计、员工管理等业务模块创建权限
+-- ============================================
+
+-- ============================================
+-- 会员管理权限
+-- ============================================
+INSERT INTO sys_permission (permission_name, permission_code, resource, action, description, status, create_by, update_by, created_at, updated_at) VALUES
+('会员查看', 'business:member:view', '/api/admin/members', 'GET', '查看会员列表及详情', 1, 'system', 'system', NOW(), NOW()),
+('会员编辑', 'business:member:edit', '/api/admin/member', 'PUT', '编辑会员信息', 1, 'system', 'system', NOW(), NOW()),
+('会员手机修改', 'business:member:phone', '/api/admin/member/phone', 'POST', '管理员修改会员手机号', 1, 'system', 'system', NOW(), NOW());
+
+-- ============================================
+-- 会员卡管理权限
+-- ============================================
+INSERT INTO sys_permission (permission_name, permission_code, resource, action, description, status, create_by, update_by, created_at, updated_at) VALUES
+('会员卡查看', 'business:memberCard:view', '/api/member-cards', 'GET', '查看会员卡列表及详情', 1, 'system', 'system', NOW(), NOW()),
+('会员卡创建', 'business:memberCard:create', '/api/member-cards', 'POST', '创建会员卡类型', 1, 'system', 'system', NOW(), NOW()),
+('会员卡编辑', 'business:memberCard:edit', '/api/member-cards', 'PUT', '编辑会员卡类型', 1, 'system', 'system', NOW(), NOW()),
+('会员卡删除', 'business:memberCard:delete', '/api/member-cards', 'DELETE', '删除会员卡类型', 1, 'system', 'system', NOW(), NOW()),
+('会员卡购买', 'business:memberCard:purchase', '/api/member-card-records/purchase', 'POST', '为会员购买/绑定会员卡', 1, 'system', 'system', NOW(), NOW()),
+('会员卡退卡', 'business:memberCard:refund', '/api/member-card-records/refund', 'POST', '会员卡退卡/解绑', 1, 'system', 'system', NOW(), NOW());
+
+-- ============================================
+-- 团课管理权限
+-- ============================================
+INSERT INTO sys_permission (permission_name, permission_code, resource, action, description, status, create_by, update_by, created_at, updated_at) VALUES
+('团课查看', 'business:groupCourse:view', '/api/groupCourse', 'GET', '查看团课列表及详情', 1, 'system', 'system', NOW(), NOW()),
+('团课创建', 'business:groupCourse:create', '/api/groupCourse', 'POST', '创建团课', 1, 'system', 'system', NOW(), NOW()),
+('团课编辑', 'business:groupCourse:edit', '/api/groupCourse', 'PUT', '编辑团课信息', 1, 'system', 'system', NOW(), NOW()),
+('团课删除', 'business:groupCourse:delete', '/api/groupCourse', 'DELETE', '删除团课', 1, 'system', 'system', NOW(), NOW()),
+('团课取消', 'business:groupCourse:cancel', '/api/groupCourse/cancel', 'POST', '取消团课', 1, 'system', 'system', NOW(), NOW());
+
+-- ============================================
+-- 团课类型管理权限
+-- ============================================
+INSERT INTO sys_permission (permission_name, permission_code, resource, action, description, status, create_by, update_by, created_at, updated_at) VALUES
+('团课类型查看', 'business:groupCourseType:view', '/api/groupCourse/types', 'GET', '查看团课类型列表', 1, 'system', 'system', NOW(), NOW()),
+('团课类型创建', 'business:groupCourseType:create', '/api/groupCourse/types', 'POST', '创建团课类型', 1, 'system', 'system', NOW(), NOW()),
+('团课类型编辑', 'business:groupCourseType:edit', '/api/groupCourse/types', 'PUT', '编辑团课类型', 1, 'system', 'system', NOW(), NOW()),
+('团课类型删除', 'business:groupCourseType:delete', '/api/groupCourse/types', 'DELETE', '删除团课类型', 1, 'system', 'system', NOW(), NOW());
+
+-- ============================================
+-- 团课推荐管理权限
+-- ============================================
+INSERT INTO sys_permission (permission_name, permission_code, resource, action, description, status, create_by, update_by, created_at, updated_at) VALUES
+('团课推荐查看', 'business:groupCourseRecommend:view', '/api/groupCourse/recommend', 'GET', '查看推荐团课列表', 1, 'system', 'system', NOW(), NOW()),
+('团课推荐创建', 'business:groupCourseRecommend:create', '/api/groupCourse/recommend', 'POST', '创建推荐团课', 1, 'system', 'system', NOW(), NOW()),
+('团课推荐编辑', 'business:groupCourseRecommend:edit', '/api/groupCourse/recommend', 'PUT', '编辑推荐团课', 1, 'system', 'system', NOW(), NOW()),
+('团课推荐删除', 'business:groupCourseRecommend:delete', '/api/groupCourse/recommend', 'DELETE', '删除推荐团课', 1, 'system', 'system', NOW(), NOW());
+
+-- ============================================
+-- 团课预约管理权限
+-- ============================================
+INSERT INTO sys_permission (permission_name, permission_code, resource, action, description, status, create_by, update_by, created_at, updated_at) VALUES
+('团课预约查看', 'business:groupCourseBooking:view', '/api/groupCourse/bookings', 'GET', '查看团课预约记录', 1, 'system', 'system', NOW(), NOW()),
+('团课预约创建', 'business:groupCourseBooking:create', '/api/groupCourse/book', 'POST', '预约团课', 1, 'system', 'system', NOW(), NOW()),
+('团课预约取消', 'business:groupCourseBooking:cancel', '/api/groupCourse/booking/cancel', 'POST', '取消团课预约', 1, 'system', 'system', NOW(), NOW());
+
+-- ============================================
+-- 签到管理权限
+-- ============================================
+INSERT INTO sys_permission (permission_name, permission_code, resource, action, description, status, create_by, update_by, created_at, updated_at) VALUES
+('签到查看', 'business:checkIn:view', '/api/checkIn/records', 'GET', '查看签到记录', 1, 'system', 'system', NOW(), NOW()),
+('签到操作', 'business:checkIn:create', '/api/checkIn', 'POST', '执行签到操作', 1, 'system', 'system', NOW(), NOW()),
+('签到导出', 'business:checkIn:export', '/api/checkIn/records/export', 'GET', '导出签到记录', 1, 'system', 'system', NOW(), NOW());
+
+-- ============================================
+-- 数据统计权限
+-- ============================================
+INSERT INTO sys_permission (permission_name, permission_code, resource, action, description, status, create_by, update_by, created_at, updated_at) VALUES
+('数据统计查看', 'business:dataCount:view', '/api/datacount', 'GET', '查看数据统计报表', 1, 'system', 'system', NOW(), NOW()),
+('数据统计导出', 'business:dataCount:export', '/api/datacount/export', 'GET', '导出统计报表', 1, 'system', 'system', NOW(), NOW());
+
+-- ============================================
+-- 员工管理权限
+-- ============================================
+INSERT INTO sys_permission (permission_name, permission_code, resource, action, description, status, create_by, update_by, created_at, updated_at) VALUES
+('员工查看', 'business:employee:view', '/api/employees/page', 'GET', '查看员工列表', 1, 'system', 'system', NOW(), NOW()),
+('员工创建', 'business:employee:create', '/api/users', 'POST', '创建员工账号', 1, 'system', 'system', NOW(), NOW()),
+('员工编辑', 'business:employee:edit', '/api/users', 'PUT', '编辑员工信息', 1, 'system', 'system', NOW(), NOW()),
+('员工删除', 'business:employee:delete', '/api/users', 'DELETE', '删除/禁用员工账号', 1, 'system', 'system', NOW(), NOW()),
+('员工分配角色', 'business:employee:assignRole', '/api/users/roles', 'POST', '为员工分配角色', 1, 'system', 'system', NOW(), NOW());
+
+-- ============================================
+-- 为超级管理员角色分配所有业务权限
+-- ============================================
+INSERT INTO sys_role_permission (role_id, permission_id, create_by, update_by, created_at, updated_at)
+SELECT 1, id, 'system', 'system', NOW(), NOW()
+FROM sys_permission
+WHERE permission_code LIKE 'business:%'
+ AND status = 1
+ AND id NOT IN (SELECT permission_id FROM sys_role_permission WHERE role_id = 1);
+
+-- ============================================
+-- 重置序列值
+-- ============================================
+SELECT setval('sys_permission_id_seq', (SELECT COALESCE(MAX(id), 1) FROM sys_permission));
+SELECT setval('sys_role_permission_id_seq', (SELECT COALESCE(MAX(id), 1) FROM sys_role_permission));
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V23__Add_Coach_Test_Data.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V23__Add_Coach_Test_Data.sql
new file mode 100644
index 0000000..fa0167c
--- /dev/null
+++ b/gym-manage-api/manage-db/src/main/resources/db/migration/V23__Add_Coach_Test_Data.sql
@@ -0,0 +1,93 @@
+-- ============================================================
+-- V23: 教练账号测试数据
+-- 描述: 创建教练角色、教练用户及相关团课测试数据
+-- 日期: 2026-06-22
+-- ============================================================
+
+-- ============================================================
+-- 1. 新增教练专属权限
+-- 教练只能查看自己教授的团课/预约/签到信息,不能修改数据库
+-- ============================================================
+INSERT INTO sys_permission (permission_name, permission_code, resource, action, description, status, create_by, update_by, created_at, updated_at) VALUES
+('查看自己的团课', 'coach:course:view', '/api/coach/courses', 'GET', '查看教练自己教授的团课列表及详情', 1, 'system', 'system', NOW(), NOW()),
+('查看自己团课的预约', 'coach:booking:view', '/api/coach/bookings', 'GET', '查看教练自己团课的会员预约记录', 1, 'system', 'system', NOW(), NOW()),
+('查看自己团课的签到', 'coach:signIn:view', '/api/coach/signins', 'GET', '查看教练自己团课的会员签到记录', 1, 'system', 'system', NOW(), NOW()),
+('教练执行签到', 'coach:signIn:create', '/api/coach/signins', 'POST', '教练为自己团课的会员执行签到操作', 1, 'system', 'system', NOW(), NOW())
+ON CONFLICT (permission_code) DO NOTHING;
+
+-- ============================================================
+-- 2. 教练角色
+-- ============================================================
+INSERT INTO sys_role (id, role_name, role_key, role_sort, status, create_by, update_by, created_at, updated_at)
+VALUES (5, '教练', 'coach', 5, 1, 'system', 'system', NOW(), NOW())
+ON CONFLICT (role_key) DO NOTHING;
+
+-- ============================================================
+-- 3. 教练用户(密码: Test@123)
+-- ============================================================
+INSERT INTO sys_user (id, username, password, email, phone, nickname, status, create_by, update_by, created_at, updated_at)
+VALUES (20, 'coach', '$2a$12$nZ1EMUpZQljbnEdIKzH72eHlDJKUmHmHppnTTVth/SlHs5VpSAr8C', 'coach@novalon.com', '13900000001', '张教练', 1, 'system', 'system', NOW(), NOW())
+ON CONFLICT (username) DO NOTHING;
+
+-- ============================================================
+-- 4. 教练角色关联
+-- ============================================================
+INSERT INTO user_role (user_id, role_id, created_by, created_at)
+VALUES (20, 5, 'system', NOW())
+ON CONFLICT (user_id, role_id) DO NOTHING;
+
+-- ============================================================
+-- 5. 为教练角色分配有限权限
+-- 教练只能查看自己教授的团课、查看自己团课的预约/签到信息、为自己团课的会员签到
+-- 不能对数据库进行写操作(签到属于业务操作,非数据库直接写入)
+-- ============================================================
+INSERT INTO sys_role_permission (role_id, permission_id, create_by, update_by, created_at, updated_at)
+SELECT 5, id, 'system', 'system', NOW(), NOW()
+FROM sys_permission
+WHERE permission_code IN (
+ 'coach:course:view', -- 查看自己的团课
+ 'coach:booking:view', -- 查看自己团课的预约信息
+ 'coach:signIn:view', -- 查看自己团课的签到信息
+ 'coach:signIn:create', -- 为自己团课的会员签到
+ 'system:notice:view' -- 查看系统公告
+)
+AND NOT EXISTS (
+ SELECT 1 FROM sys_role_permission WHERE role_id = 5 AND permission_id = sys_permission.id
+);
+
+-- ============================================================
+-- 6. 为教练分配今天的测试团课
+-- ============================================================
+INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, description, point_card_amount, create_by, created_at, updated_at)
+VALUES
+('核心力量训练', 20, (SELECT id FROM group_course_type WHERE type_name = '核心训练' LIMIT 1),
+ CURRENT_DATE + TIME '08:00', CURRENT_DATE + TIME '09:00', 15, 3, '0', '健身房B区', '针对核心肌群的专项训练,适合所有水平学员', 1, 'system', NOW(), NOW()),
+
+('瑜伽入门', 20, (SELECT id FROM group_course_type WHERE type_name = '瑜伽入门' LIMIT 1),
+ CURRENT_DATE + TIME '10:00', CURRENT_DATE + TIME '11:00', 12, 5, '0', '瑜伽教室', '基础的瑜伽体式教学,新课上线欢迎参加', 1, 'system', NOW(), NOW()),
+
+('动感单车', 20, (SELECT id FROM group_course_type WHERE type_name = '动感单车' LIMIT 1),
+ CURRENT_DATE + TIME '14:00', CURRENT_DATE + TIME '15:00', 20, 8, '0', '单车房', '高强度燃脂骑行,动感音乐带你飞', 2, 'system', NOW(), NOW()),
+
+('晚间普拉提', 20, (SELECT id FROM group_course_type WHERE type_name = '普拉提' LIMIT 1),
+ CURRENT_DATE + TIME '18:00', CURRENT_DATE + TIME '19:00', 10, 2, '0', '普拉提教室', '注重核心力量和身体控制,放松一天疲劳', 1, 'system', NOW(), NOW());
+
+-- ============================================================
+-- 7. 为教练创建一个未来的团课(方便测试时间表)
+-- ============================================================
+INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, description, point_card_amount, create_by, created_at, updated_at)
+VALUES
+('HIIT燃脂训练', 20, (SELECT id FROM group_course_type WHERE type_name = 'HIIT训练' LIMIT 1),
+ CURRENT_DATE + INTERVAL '1 day' + TIME '08:00', CURRENT_DATE + INTERVAL '1 day' + TIME '09:00',
+ 15, 0, '0', '综合训练区', '高强度间歇训练,挑战心肺极限', 2, 'system', NOW(), NOW()),
+
+('周末瑜伽', 20, (SELECT id FROM group_course_type WHERE type_name = '瑜伽进阶' LIMIT 1),
+ CURRENT_DATE + INTERVAL '3 days' + TIME '09:00', CURRENT_DATE + INTERVAL '3 days' + TIME '10:30',
+ 12, 0, '0', '瑜伽教室', '针对有基础的进阶瑜伽课程', 1, 'system', NOW(), NOW());
+
+-- ============================================================
+-- 8. 重置序列值
+-- ============================================================
+SELECT setval('sys_role_id_seq', GREATEST((SELECT COALESCE(MAX(id), 0) FROM sys_role), 5));
+SELECT setval('sys_user_id_seq', GREATEST((SELECT COALESCE(MAX(id), 0) FROM sys_user), 20));
+SELECT setval('group_course_id_seq', (SELECT COALESCE(MAX(id), 0) FROM group_course));
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V24__Add_Second_Coach_Test_Data.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V24__Add_Second_Coach_Test_Data.sql
new file mode 100644
index 0000000..9398aef
--- /dev/null
+++ b/gym-manage-api/manage-db/src/main/resources/db/migration/V24__Add_Second_Coach_Test_Data.sql
@@ -0,0 +1,50 @@
+-- ============================================================
+-- V24: 新增第二个测试教练账号(李教练)
+-- 描述:
+-- 1. 新增测试教练用户 coachli(李教练)
+-- 2. 为李教练分配教练角色(继承教练的有限权限)
+-- 3. 为李教练分配未来一周的测试团课
+-- 日期: 2026-06-22
+-- ============================================================
+
+-- ============================================================
+-- 1. 第二个测试教练账号(密码: Test@123)
+-- ============================================================
+INSERT INTO sys_user (id, username, password, email, phone, nickname, status, create_by, update_by, created_at, updated_at)
+VALUES (21, 'coachli', '$2a$12$nZ1EMUpZQljbnEdIKzH72eHlDJKUmHmHppnTTVth/SlHs5VpSAr8C', 'coachli@novalon.com', '13900000002', '李教练', 1, 'system', 'system', NOW(), NOW())
+ON CONFLICT (username) DO NOTHING;
+
+-- ============================================================
+-- 2. 为李教练分配教练角色(role_id=5)
+-- 继承教练角色的所有有限权限
+-- ============================================================
+INSERT INTO user_role (user_id, role_id, created_by, created_at)
+VALUES (21, 5, 'system', NOW())
+ON CONFLICT (user_id, role_id) DO NOTHING;
+
+-- ============================================================
+-- 3. 为李教练分配测试团课(未来一周的课程)
+-- ============================================================
+INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, description, point_card_amount, create_by, created_at, updated_at)
+VALUES
+('入门瑜伽', 21, (SELECT id FROM group_course_type WHERE type_name = '瑜伽入门' LIMIT 1),
+ CURRENT_DATE + INTERVAL '2 days' + TIME '09:00', CURRENT_DATE + INTERVAL '2 days' + TIME '10:00',
+ 15, 0, '0', '瑜伽教室', '适合零基础学员的入门瑜伽课程', 1, 'system', NOW(), NOW()),
+
+('燃脂搏击', 21, (SELECT id FROM group_course_type WHERE type_name = '动感单车' LIMIT 1),
+ CURRENT_DATE + INTERVAL '2 days' + TIME '15:00', CURRENT_DATE + INTERVAL '2 days' + TIME '16:00',
+ 20, 0, '0', '综合训练区', '高强度搏击操,燃烧卡路里', 2, 'system', NOW(), NOW()),
+
+('肩颈理疗', 21, (SELECT id FROM group_course_type WHERE type_name = '普拉提' LIMIT 1),
+ CURRENT_DATE + INTERVAL '4 days' + TIME '10:00', CURRENT_DATE + INTERVAL '4 days' + TIME '11:00',
+ 12, 0, '0', '普拉提教室', '针对久坐人群的肩颈放松课程', 1, 'system', NOW(), NOW()),
+
+('晨间有氧', 21, (SELECT id FROM group_course_type WHERE type_name = 'HIIT训练' LIMIT 1),
+ CURRENT_DATE + INTERVAL '5 days' + TIME '07:00', CURRENT_DATE + INTERVAL '5 days' + TIME '08:00',
+ 20, 0, '0', '有氧区', '清晨燃脂有氧训练,开启活力一天', 2, 'system', NOW(), NOW());
+
+-- ============================================================
+-- 4. 重置序列值
+-- ============================================================
+SELECT setval('sys_user_id_seq', GREATEST((SELECT COALESCE(MAX(id), 0) FROM sys_user), 21));
+SELECT setval('group_course_id_seq', (SELECT COALESCE(MAX(id), 0) FROM group_course));
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V6__Create_GroupCourse_table.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V6__Create_GroupCourse_table.sql
index 0e26497..714cb41 100644
--- a/gym-manage-api/manage-db/src/main/resources/db/migration/V6__Create_GroupCourse_table.sql
+++ b/gym-manage-api/manage-db/src/main/resources/db/migration/V6__Create_GroupCourse_table.sql
@@ -13,6 +13,8 @@ CREATE TABLE IF NOT EXISTS group_course (
max_members INTEGER DEFAULT 20,
current_members INTEGER DEFAULT 0,
status VARCHAR(1) DEFAULT '0',
+ actual_start_time TIMESTAMP,
+ actual_end_time TIMESTAMP,
location VARCHAR(255),
cover_image VARCHAR(500),
description TEXT,
@@ -50,7 +52,9 @@ COMMENT ON COLUMN group_course.start_time IS '开始时间';
COMMENT ON COLUMN group_course.end_time IS '结束时间';
COMMENT ON COLUMN group_course.max_members IS '最大参与人数';
COMMENT ON COLUMN group_course.current_members IS '当前参与人数';
-COMMENT ON COLUMN group_course.status IS '状态(0正常 1已取消 2已结束)';
+COMMENT ON COLUMN group_course.status IS '状态(0正常 1已取消 2已结束 3进行中 4超时)';
+COMMENT ON COLUMN group_course.actual_start_time IS '实际开课时间(教练点击开课时记录)';
+COMMENT ON COLUMN group_course.actual_end_time IS '实际结课时间(教练点击结课时记录)';
COMMENT ON COLUMN group_course.location IS '上课地点';
COMMENT ON COLUMN group_course.cover_image IS '封面图URL';
COMMENT ON COLUMN group_course.description IS '课程描述';
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V8__Create_Member_And_MemberCard.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V7__Create_Member_And_MemberCard.sql
similarity index 100%
rename from gym-manage-api/manage-db/src/main/resources/db/migration/V8__Create_Member_And_MemberCard.sql
rename to gym-manage-api/manage-db/src/main/resources/db/migration/V7__Create_Member_And_MemberCard.sql
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V7__Insert_GroupCourse_test_data.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V7__Insert_GroupCourse_test_data.sql
deleted file mode 100644
index ee7adaa..0000000
--- a/gym-manage-api/manage-db/src/main/resources/db/migration/V7__Insert_GroupCourse_test_data.sql
+++ /dev/null
@@ -1,28 +0,0 @@
--- 场景1: 0人预约,可预约(正常状态,6月15日,距开始还有14天)
-INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, cover_image, description, create_by, created_at, updated_at) VALUES
- ('极速燃脂单车', 104, 2, '2026-06-02 16:45:00', '2026-06-15 20:20:00', 25, 0, 0, '单车房', '/images/spinning.jpg', '跟随音乐节奏变换阻力和速度,体验爬坡与冲刺的快感,一节课消耗800大卡。', 'admin', '2026-06-01 11:00:00', '2026-06-01 11:00:00');
-
--- 场景2: 已有人预约,可预约(正常状态,6月12日,5/15人)
-INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, cover_image, description, create_by, created_at, updated_at) VALUES
- ('清晨流瑜伽', 101, 1, '2026-06-12 09:00:00', '2026-06-12 10:30:00', 15, 5, 0, 'A座3楼瑜伽教室', '/images/yoga_flow.jpg', '适合有一定基础的学员,通过流畅的体式连接呼吸,唤醒身体能量。', 'admin', '2026-06-01 10:00:00', '2026-06-01 10:00:00');
-
--- 场景3: 满员,不可预约(正常状态但已满员,6月10日,20/20人)
-INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, cover_image, description, create_by, created_at, updated_at) VALUES
- ('燃脂搏击', 102, 2, '2026-06-10 18:30:00', '2026-06-10 19:30:00', 20, 20, 0, '综合训练区', '/images/kickboxing.jpg', '高强度间歇训练,配合音乐快速燃脂,释放压力。名额已满,无法预约。', 'coach_zhang', '2026-06-01 14:30:00', '2026-06-01 14:30:00');
-
--- 场景4: 超出可预约时间,不可预约(正常状态但距开始不足30分钟)
--- 当前时间: 2026-06-01 15:00,课程开始: 2026-06-01 15:20
-INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, cover_image, description, create_by, created_at, updated_at) VALUES
- ('哈他瑜伽', 101, 1, '2026-06-01 15:20:00', '2026-06-01 16:50:00', 12, 3, 0, '瑜伽教室B', '/images/hatha_yoga.jpg', '基础哈他瑜伽,适合所有级别。距开始不足30分钟,已停止预约。', 'coach_li', '2026-06-01 08:00:00', '2026-06-01 08:00:00');
-
--- 场景5: 课程已取消,不可预约(status=1)
-INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, cover_image, description, create_by, created_at, updated_at) VALUES
- ('周末冥想修复', 101, 1, '2026-06-20 15:00:00', '2026-06-20 16:00:00', 12, 3, 1, '冥想室', '/images/meditation.jpg', '通过呼吸和正念冥想,深度放松身心。该课程已被取消。', 'coach_wang', '2026-05-28 08:00:00', '2026-05-28 08:00:00');
-
--- 场景6: 课程已结束,不可预约(status=2)
-INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, cover_image, description, create_by, created_at, updated_at) VALUES
- ('蜜桃臀塑造', 103, 3, '2026-05-30 19:00:00', '2026-05-30 20:00:00', 10, 8, 2, '私教专区', '/images/glute.jpg', '针对性训练臀部肌肉群,课程已于5月30日结束,无法预约。', 'coach_li', '2026-05-20 09:15:00', '2026-05-20 09:15:00');
-
--- 场景7(可选): 已结束但未满员的课程(status=2,即使有空位也不可预约)
-INSERT INTO group_course (course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, cover_image, description, create_by, created_at, updated_at) VALUES
- ('午间冥想放松', 101, 1, '2026-05-31 12:00:00', '2026-05-31 13:00:00', 15, 6, 2, '冥想室', '/images/meditation_noon.jpg', '午间冥想课程,已于5月31日结束。', 'admin', '2026-05-25 09:00:00', '2026-05-25 09:00:00');
\ No newline at end of file
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V9__Add_GroupCourse_Booking_Snapshot_Fields.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V8__Add_GroupCourse_Booking_Snapshot_Fields.sql
similarity index 100%
rename from gym-manage-api/manage-db/src/main/resources/db/migration/V9__Add_GroupCourse_Booking_Snapshot_Fields.sql
rename to gym-manage-api/manage-db/src/main/resources/db/migration/V8__Add_GroupCourse_Booking_Snapshot_Fields.sql
diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V13__Create_sign_in_record_table.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V9__Create_sign_in_record_table.sql
similarity index 100%
rename from gym-manage-api/manage-db/src/main/resources/db/migration/V13__Create_sign_in_record_table.sql
rename to gym-manage-api/manage-db/src/main/resources/db/migration/V9__Create_sign_in_record_table.sql
diff --git a/gym-manage-api/pom.xml b/gym-manage-api/pom.xml
index 8746a4a..69441c7 100644
--- a/gym-manage-api/pom.xml
+++ b/gym-manage-api/pom.xml
@@ -47,6 +47,7 @@
gym-checkIn
gym-dataCount
gym-auth
+ gym-payment