修复协议错误

This commit is contained in:
2026-06-29 20:34:25 +08:00
parent 44da3cab6e
commit 836f0e1cbf
12 changed files with 2094 additions and 93 deletions
@@ -53,6 +53,10 @@ public class GroupCourseBooking extends BaseDomain {
@Schema(description = "上课地点", example = "健身房A区")
private String location;
//封面图URL(非DB字段,由 Service 从 GroupCourse 填充)
@Schema(description = "封面图URL", example = "https://example.com/cover.jpg")
private String coverImage;
public Long getCourseId() {
return courseId;
}
@@ -132,4 +136,12 @@ public class GroupCourseBooking extends BaseDomain {
public void setLocation(String location) {
this.location = location;
}
public String getCoverImage() {
return coverImage;
}
public void setCoverImage(String coverImage) {
this.coverImage = coverImage;
}
}
@@ -7,6 +7,7 @@ import cn.novalon.gym.manage.groupcourse.handler.BookingSagaHandler;
import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseBookingRepository;
import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseRepository;
import cn.novalon.gym.manage.groupcourse.service.IGroupCourseBookingService;
import cn.novalon.gym.manage.groupcourse.util.OSSUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.r2dbc.core.DatabaseClient;
@@ -335,6 +336,18 @@ public class GroupCourseBookingService implements IGroupCourseBookingService {
public Flux<GroupCourseBooking> getBookingsByMemberId(Long memberId) {
logger.debug("查询会员预约记录:memberId={}", memberId);
return bookingRepository.findByMemberId(memberId)
.flatMap(booking -> {
// 从关联的 GroupCourse 获取封面图并转为预签名URL
if (booking.getCourseId() != null) {
return courseRepository.findByIdAndDeletedAtIsNull(booking.getCourseId())
.map(course -> {
booking.setCoverImage(OSSUtil.toCoverPresignedUrl(course.getCoverImage()));
return booking;
})
.defaultIfEmpty(booking);
}
return Mono.just(booking);
})
.doOnComplete(() -> logger.debug("查询完成:memberId={}", memberId));
}
@@ -5,6 +5,7 @@ import cn.novalon.gym.manage.groupcourse.domain.GroupCourseRecommend;
import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseRecommendRepository;
import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseRepository;
import cn.novalon.gym.manage.groupcourse.service.IGroupCourseRecommendService;
import cn.novalon.gym.manage.groupcourse.util.OSSUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@@ -134,6 +135,8 @@ public class GroupCourseRecommendService implements IGroupCourseRecommendService
return groupCourseRepository.findByIdAndDeletedAtIsNull(recommend.getCourseId())
.map(course -> {
// 将 OSS Key 转换为预签名URL,前端可直接加载
course.setCoverImage(OSSUtil.toCoverPresignedUrl(course.getCoverImage()));
recommend.setGroupCourse(course);
return recommend;
})
@@ -19,6 +19,7 @@ import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseRepository;
import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseTypeRepository;
import cn.novalon.gym.manage.groupcourse.service.IGroupCourseService;
import cn.novalon.gym.manage.groupcourse.util.QRCodeUtil;
import cn.novalon.gym.manage.groupcourse.util.OSSUtil;
import cn.novalon.gym.manage.member.entity.MemberCard;
import cn.novalon.gym.manage.member.entity.MemberCardRecord;
import cn.novalon.gym.manage.member.enums.MemberCardType;
@@ -32,6 +33,7 @@ import org.springframework.r2dbc.core.DatabaseClient;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import java.time.LocalDateTime;
import java.util.HashMap;
@@ -153,7 +155,7 @@ public class GroupCourseService implements IGroupCourseService {
detail.setCurrentMembers(course.getCurrentMembers());
detail.setStatus(course.getStatus());
detail.setLocation(course.getLocation());
detail.setCoverImage(course.getCoverImage());
detail.setCoverImage(OSSUtil.toCoverPresignedUrl(course.getCoverImage()));
detail.setDescription(course.getDescription());
detail.setStoredValueAmount(course.getStoredValueAmount());
detail.setQrCodePath(course.getQrCodePath());
@@ -178,7 +180,7 @@ public class GroupCourseService implements IGroupCourseService {
try {
GroupCourse groupCourse = objectMapper.readValue(cachedJson, GroupCourse.class);
logger.info("缓存命中 - findById: id={}", id);
return Mono.just(groupCourse);
return Mono.just(fillCoverPresignedUrl(groupCourse));
} catch (JsonProcessingException e) {
logger.warn("缓存解析失败,删除缓存 - id: {}, error: {}", id, e.getMessage());
return redisUtil.delete(cacheKey).then(Mono.empty());
@@ -188,6 +190,7 @@ public class GroupCourseService implements IGroupCourseService {
})
.switchIfEmpty(
groupCourseRepository.findByIdAndDeletedAtIsNull(id)
.map(this::fillCoverPresignedUrl)
.flatMap(groupCourse -> {
try {
String jsonData = objectMapper.writeValueAsString(groupCourse);
@@ -205,16 +208,19 @@ public class GroupCourseService implements IGroupCourseService {
@Override
public Flux<GroupCourse> findAll() {
return groupCourseRepository.findAll();
return groupCourseRepository.findAll()
.map(this::fillCoverPresignedUrl);
}
@Override
public Flux<GroupCourse> findAll(boolean includeDeleted) {
Flux<GroupCourse> flux;
if(includeDeleted){
return groupCourseRepository.findAll();
flux = groupCourseRepository.findAll();
}else{
return groupCourseRepository.findByDeletedAtIsNull();
flux = groupCourseRepository.findByDeletedAtIsNull();
}
return flux.map(this::fillCoverPresignedUrl);
}
@Override
@@ -234,6 +240,7 @@ public class GroupCourseService implements IGroupCourseService {
PageResponse<GroupCourse> pageResponse = objectMapper.readValue(cachedJson,
objectMapper.getTypeFactory().constructParametricType(PageResponse.class, GroupCourse.class));
logger.info("缓存命中 - findByPage: key={}", cacheKey);
fillCoverPresignedUrl(pageResponse);
return Mono.just(pageResponse);
} catch (JsonProcessingException e) {
logger.warn("缓存解析失败,删除缓存 - key: {}, error: {}", cacheKey, e.getMessage());
@@ -254,6 +261,7 @@ public class GroupCourseService implements IGroupCourseService {
return resultMono.flatMap(pageResponse -> {
try {
fillCoverPresignedUrl(pageResponse);
String jsonData = objectMapper.writeValueAsString(pageResponse);
return redisUtil.setWithExpire(cacheKey, jsonData, CACHE_EXPIRE_SECONDS)
.thenReturn(pageResponse)
@@ -572,8 +580,11 @@ public class GroupCourseService implements IGroupCourseService {
query.getTimePeriod(), query.getPriceSort(), query.getRemainingMost());
return groupCourseRepository.searchGroupCourses(query)
.doOnSuccess(result -> logger.info("多条件查询结果 - total={}, page={}, size={}",
result.getTotalElements(), result.getCurrentPage(), result.getPageSize()))
.doOnSuccess(result -> {
fillCoverPresignedUrl(result);
logger.info("多条件查询结果 - total={}, page={}, size={}",
result.getTotalElements(), result.getCurrentPage(), result.getPageSize());
})
.doOnError(error -> logger.error("多条件查询失败 - error: {}", error.getMessage()));
}
@@ -582,4 +593,23 @@ public class GroupCourseService implements IGroupCourseService {
.then(redisUtil.deleteByPattern(CACHE_KEY_ID_PREFIX + "*"))
.then(redisUtil.deleteByPattern(CACHE_KEY_DETAIL_PREFIX + "*")).then();
}
/**
* 将单个 GroupCourse 的 coverImage 从 OSS Key 转换为预签名URL
*/
private GroupCourse fillCoverPresignedUrl(GroupCourse course) {
if (course != null) {
course.setCoverImage(OSSUtil.toCoverPresignedUrl(course.getCoverImage()));
}
return course;
}
/**
* 将分页结果中所有 GroupCourse 的 coverImage 从 OSS Key 转换为预签名URL
*/
private void fillCoverPresignedUrl(PageResponse<GroupCourse> pageResponse) {
if (pageResponse != null && pageResponse.getContent() != null) {
pageResponse.getContent().forEach(this::fillCoverPresignedUrl);
}
}
}
@@ -23,7 +23,7 @@ public class OSSUtil {
private static final Logger logger = LoggerFactory.getLogger(OSSUtil.class);
// OSS配置信息
private static final String ENDPOINT = "oss-cn-beijing.aliyuncs.com";
private static final String ENDPOINT = "https://oss-cn-beijing.aliyuncs.com";
private static final String ACCESS_KEY_ID = "LTAI5t9wHCiH68Xjxg64Xx4Y";
private static final String ACCESS_KEY_SECRET = "isAfz1IFGAnV13LOIrVg19aPhY8aRq";
private static final String BUCKET_NAME = "ycc-filesaver";
@@ -37,6 +37,8 @@ public class OSSUtil {
// 预签名URL有效期(秒)
private static final long PRESIGN_EXPIRE_SECONDS = 300;
// 封面图预签名URL有效期(秒)- 1小时,确保前端列表页足够展示
private static final long COVER_PRESIGN_EXPIRE_SECONDS = 3600;
/**
* 上传文件到阿里云OSS(文件默认继承Bucket权限,不设置ACL)
@@ -171,4 +173,21 @@ public class OSSUtil {
public static String getPublicUrl(String ossKey) {
return OSS_URL_PREFIX + ossKey;
}
/**
* 将OSS Key(相对路径)转换为封面图预签名URL(1小时有效期)
* 如果已经是HTTP(S)完整URL则直接返回
*
* @param ossKey OSS对象Key或完整URL
* @return 可访问的预签名URLossKey为空时返回null
*/
public static String toCoverPresignedUrl(String ossKey) {
if (ossKey == null || ossKey.isBlank()) {
return null;
}
if (ossKey.startsWith("http://") || ossKey.startsWith("https://")) {
return ossKey;
}
return generatePresignedUrl(ossKey, COVER_PRESIGN_EXPIRE_SECONDS);
}
}
+1995 -72
View File
File diff suppressed because it is too large Load Diff