diff --git a/src/main/java/com/yolo/keyborad/controller/ThemesController.java b/src/main/java/com/yolo/keyborad/controller/ThemesController.java index 75b4846..32059de 100644 --- a/src/main/java/com/yolo/keyborad/controller/ThemesController.java +++ b/src/main/java/com/yolo/keyborad/controller/ThemesController.java @@ -12,6 +12,7 @@ import com.yolo.keyborad.model.vo.themes.KeyboardThemesRespVO; import com.yolo.keyborad.service.KeyboardThemePurchaseService; import com.yolo.keyborad.service.KeyboardThemeStylesService; import com.yolo.keyborad.service.KeyboardThemesService; +import com.yolo.keyborad.utils.RequestLocaleUtils; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; @@ -42,9 +43,14 @@ public class ThemesController { @GetMapping("/listByStyle") @Operation(summary = "按风格查询主题", description = "按主题风格查询主题列表接口") - public BaseResponse> listByStyle(@RequestParam("themeStyle") Long themeStyleId) { + public BaseResponse> listByStyle( + @RequestParam("themeStyle") Long themeStyleId, + @RequestHeader(value = "Accept-Language", required = false) String acceptLanguage + ) { Long userId = StpUtil.getLoginIdAsLong(); - return ResultUtils.success(themesService.selectThemesByStyle(themeStyleId,userId));} + String local = RequestLocaleUtils.resolveLanguage(acceptLanguage); + return ResultUtils.success(themesService.selectThemesByStyle(themeStyleId, userId, local)); + } @GetMapping("/listAllStyles") @Operation(summary = "查询所有主题风格", description = "查询所有主题风格列表接口") @@ -70,33 +76,48 @@ public class ThemesController { @GetMapping("/purchased") @Operation(summary = "查询已购买的主题", description = "查询当前用户已购买的主题列表") - public BaseResponse> getPurchasedThemes() { + public BaseResponse> getPurchasedThemes( + @RequestHeader(value = "Accept-Language", required = false) String acceptLanguage + ) { Long userId = StpUtil.getLoginIdAsLong(); - List result = themePurchaseService.getUserPurchasedThemes(userId); + String local = RequestLocaleUtils.resolveLanguage(acceptLanguage); + List result = themePurchaseService.getUserPurchasedThemes(userId, local); return ResultUtils.success(result); } @GetMapping("/detail") @Operation(summary = "查询主题详情", description = "根据主题ID查询主题详情") - public BaseResponse getThemeDetail(@RequestParam Long themeId) { + public BaseResponse getThemeDetail( + @RequestParam Long themeId, + @RequestHeader(value = "Accept-Language", required = false) String acceptLanguage + ) { Long userId = StpUtil.getLoginIdAsLong(); - KeyboardThemesRespVO result = themesService.getThemeDetail(themeId, userId); + String local = RequestLocaleUtils.resolveLanguage(acceptLanguage); + KeyboardThemesRespVO result = themesService.getThemeDetail(themeId, userId, local); return ResultUtils.success(result); } @GetMapping("/recommended") @Operation(summary = "推荐主题列表", description = "按真实下载数量降序返回推荐主题") - public BaseResponse> getRecommendedThemes(@RequestParam(required = false) Long themeId) { + public BaseResponse> getRecommendedThemes( + @RequestParam(required = false) Long themeId, + @RequestHeader(value = "Accept-Language", required = false) String acceptLanguage + ) { Long userId = StpUtil.getLoginIdAsLong(); - List result = themesService.getRecommendedThemes(userId, themeId); + String local = RequestLocaleUtils.resolveLanguage(acceptLanguage); + List result = themesService.getRecommendedThemes(userId, themeId, local); return ResultUtils.success(result); } @GetMapping("/search") @Operation(summary = "搜索主题", description = "根据主题名称模糊搜索主题") - public BaseResponse> searchThemes(@RequestParam String themeName) { + public BaseResponse> searchThemes( + @RequestParam String themeName, + @RequestHeader(value = "Accept-Language", required = false) String acceptLanguage + ) { Long userId = StpUtil.getLoginIdAsLong(); - List result = themesService.searchThemesByName(themeName, userId); + String local = RequestLocaleUtils.resolveLanguage(acceptLanguage); + List result = themesService.searchThemesByName(themeName, userId, local); return ResultUtils.success(result); } diff --git a/src/main/java/com/yolo/keyborad/service/KeyboardThemePurchaseService.java b/src/main/java/com/yolo/keyborad/service/KeyboardThemePurchaseService.java index 0e7ba70..9c2f92e 100644 --- a/src/main/java/com/yolo/keyborad/service/KeyboardThemePurchaseService.java +++ b/src/main/java/com/yolo/keyborad/service/KeyboardThemePurchaseService.java @@ -28,7 +28,7 @@ public interface KeyboardThemePurchaseService extends IService getUserPurchasedThemes(Long userId); + List getUserPurchasedThemes(Long userId, String local); /** * 恢复已删除的主题 diff --git a/src/main/java/com/yolo/keyborad/service/KeyboardThemesService.java b/src/main/java/com/yolo/keyborad/service/KeyboardThemesService.java index 6319ca0..0c66624 100644 --- a/src/main/java/com/yolo/keyborad/service/KeyboardThemesService.java +++ b/src/main/java/com/yolo/keyborad/service/KeyboardThemesService.java @@ -17,31 +17,35 @@ public interface KeyboardThemesService extends IService{ * 按主题风格查询主题列表(未删除且上架),包含用户购买状态 * @param themeStyle 主题风格 * @param userId 用户ID + * @param local 语言标识 * @return 主题列表 */ - List selectThemesByStyle(Long themeStyle, Long userId); + List selectThemesByStyle(Long themeStyle, Long userId, String local); /** * 查询主题详情 * @param themeId 主题ID * @param userId 用户ID + * @param local 语言标识 * @return 主题详情 */ - KeyboardThemesRespVO getThemeDetail(Long themeId, Long userId); + KeyboardThemesRespVO getThemeDetail(Long themeId, Long userId, String local); /** * 推荐主题列表(按真实下载数量降序) * @param userId 用户ID + * @param local 语言标识 * @return 推荐主题列表 */ - List getRecommendedThemes(Long userId,Long themeId); + List getRecommendedThemes(Long userId, Long themeId, String local); /** * 根据主题名称模糊搜索主题 * @param themeName 主题名称关键字 * @param userId 用户ID + * @param local 语言标识 * @return 主题列表 */ - List searchThemesByName(String themeName, Long userId); + List searchThemesByName(String themeName, Long userId, String local); } diff --git a/src/main/java/com/yolo/keyborad/service/impl/KeyboardThemePurchaseServiceImpl.java b/src/main/java/com/yolo/keyborad/service/impl/KeyboardThemePurchaseServiceImpl.java index 12a9c32..6e08e38 100644 --- a/src/main/java/com/yolo/keyborad/service/impl/KeyboardThemePurchaseServiceImpl.java +++ b/src/main/java/com/yolo/keyborad/service/impl/KeyboardThemePurchaseServiceImpl.java @@ -17,6 +17,7 @@ import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; import java.math.BigDecimal; import java.util.Date; @@ -204,7 +205,7 @@ public class KeyboardThemePurchaseServiceImpl extends ServiceImpl getUserPurchasedThemes(Long userId) { + public List getUserPurchasedThemes(Long userId, String local) { // 1. 从用户主题表查询主题ID列表 List themeIds = userThemesService.lambdaQuery() .eq(KeyboardUserThemes::getUserId, userId) @@ -224,6 +225,7 @@ public class KeyboardThemePurchaseServiceImpl extends ServiceImpl { diff --git a/src/main/java/com/yolo/keyborad/service/impl/KeyboardThemesServiceImpl.java b/src/main/java/com/yolo/keyborad/service/impl/KeyboardThemesServiceImpl.java index c9b8589..f3bb237 100644 --- a/src/main/java/com/yolo/keyborad/service/impl/KeyboardThemesServiceImpl.java +++ b/src/main/java/com/yolo/keyborad/service/impl/KeyboardThemesServiceImpl.java @@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; import java.util.List; import java.util.Set; @@ -29,6 +30,7 @@ public class KeyboardThemesServiceImpl extends ServiceImpl selectThemesByStyle(Long themeStyle, Long userId) { - // 尝试从Redis缓存读取 - String cacheKey = themeStyle == 9999 ? THEME_ALL_KEY : THEME_STYLE_KEY + themeStyle; - List themesList = null; - - try { - Object cached = redisTemplate.opsForValue().get(cacheKey); - if (cached != null) { - themesList = (List) cached; - log.debug("从缓存读取风格{}的主题列表,共{}个", themeStyle, themesList.size()); - } - } catch (Exception e) { - log.warn("读取主题缓存失败,将从数据库查询", e); - } - - // 缓存未命中,从数据库查询 - if (themesList == null) { - List themesFromDb; - if (themeStyle == 9999) { - // 查询所有主题,按排序字段升序 - themesFromDb = this.lambdaQuery() - .eq(KeyboardThemes::getDeleted, false) - .eq(KeyboardThemes::getThemeStatus, true) - .orderByAsc(KeyboardThemes::getSort) - .list(); - } else { - // 查询指定风格的主题 - themesFromDb = this.lambdaQuery() - .eq(KeyboardThemes::getDeleted, false) - .eq(KeyboardThemes::getThemeStatus, true) - .eq(KeyboardThemes::getThemeStyle, themeStyle) - .list(); - } - themesList = themesFromDb.stream() - .map(theme -> BeanUtil.copyProperties(theme, KeyboardThemesRespVO.class)) - .collect(Collectors.toList()); - log.debug("从数据库读取风格{}的主题列表,共{}个", themeStyle, themesList.size()); - } - - // 查询用户已购买的主题ID集合 - Set purchasedThemeIds = purchaseService.lambdaQuery() - .eq(KeyboardThemePurchase::getUserId, userId) - .eq(KeyboardThemePurchase::getPayStatus, (short) 1) - .list() - .stream() - .map(KeyboardThemePurchase::getThemeId) - .collect(Collectors.toSet()); - - // 设置购买状态并返回 - return themesList.stream().map(theme -> { - KeyboardThemesRespVO vo = BeanUtil.copyProperties(theme, KeyboardThemesRespVO.class); - vo.setIsPurchased(purchasedThemeIds.contains(theme.getId())); - return vo; - }).collect(Collectors.toList()); + public List selectThemesByStyle(Long themeStyle, Long userId, String local) { + List themesList = getThemesByStyle(themeStyle, local); + return markPurchasedThemes(themesList, userId); } /** @@ -120,12 +71,10 @@ public class KeyboardThemesServiceImpl extends ServiceImpl getRecommendedThemes(Long userId, Long themeId) { + public List getRecommendedThemes(Long userId, Long themeId, String local) { // 构建查询器 - LambdaQueryChainWrapper queryWrapper = this.lambdaQuery() - .eq(KeyboardThemes::getDeleted, false) - .eq(KeyboardThemes::getThemeStatus, true) + LambdaQueryChainWrapper queryWrapper = baseThemeQuery(local) .orderByDesc(KeyboardThemes::getRealDownloadCount); // 排除传入的当前主题ID @@ -171,48 +112,126 @@ public class KeyboardThemesServiceImpl extends ServiceImpl themesList = queryWrapper.list(); // 查询用户已购买的主题ID集合 - Set purchasedThemeIds = purchaseService.lambdaQuery() - .eq(KeyboardThemePurchase::getUserId, userId) - .eq(KeyboardThemePurchase::getPayStatus, (short) 1) - .list() - .stream() - .map(KeyboardThemePurchase::getThemeId) - .collect(Collectors.toSet()); + Set purchasedThemeIds = getPurchasedThemeIds(userId); // 只取前8条数据并设置购买状态 return themesList.stream() .limit(8) - .map(theme -> { - KeyboardThemesRespVO vo = BeanUtil.copyProperties(theme, KeyboardThemesRespVO.class); - // 设置主题的实际购买状态 - vo.setIsPurchased(purchasedThemeIds.contains(theme.getId())); - return vo; - }).collect(Collectors.toList()); + .map(theme -> toThemeResp(theme, purchasedThemeIds.contains(theme.getId()))) + .collect(Collectors.toList()); } @Override - public List searchThemesByName(String themeName, Long userId) { + public List searchThemesByName(String themeName, Long userId, String local) { // 根据主题名称模糊搜索 - List themesList = this.lambdaQuery() - .eq(KeyboardThemes::getDeleted, false) - .eq(KeyboardThemes::getThemeStatus, true) + List themesList = baseThemeQuery(local) .like(KeyboardThemes::getThemeName, themeName) .list(); // 查询用户已购买的主题ID集合 - Set purchasedThemeIds = purchaseService.lambdaQuery() + Set purchasedThemeIds = getPurchasedThemeIds(userId); + + // 转换为VO并设置购买状态 + return themesList.stream() + .map(theme -> toThemeResp(theme, purchasedThemeIds.contains(theme.getId()))) + .collect(Collectors.toList()); + } + + @SuppressWarnings("unchecked") + private List getThemesByStyle(Long themeStyle, String local) { + List cachedThemes = getCachedThemes(themeStyle, local); + if (cachedThemes != null) { + return cachedThemes; + } + + List themesFromDb = queryThemesByStyle(themeStyle, local); + List themesList = themesFromDb.stream() + .map(theme -> BeanUtil.copyProperties(theme, KeyboardThemesRespVO.class)) + .collect(Collectors.toList()); + log.debug("从数据库读取风格{}且语言{}的主题列表,共{}个", themeStyle, local, themesList.size()); + return themesList; + } + + @SuppressWarnings("unchecked") + private List getCachedThemes(Long themeStyle, String local) { + try { + Object cached = redisTemplate.opsForValue().get(buildCacheKey(themeStyle)); + if (cached == null) { + return null; + } + + List themesList = (List) cached; + List filteredThemes = filterThemesByLocal(themesList, local); + log.debug("从缓存读取风格{}且语言{}的主题列表,共{}个", themeStyle, local, filteredThemes.size()); + return filteredThemes; + } catch (Exception e) { + log.warn("读取主题缓存失败,将从数据库查询", e); + return null; + } + } + + private List queryThemesByStyle(Long themeStyle, String local) { + LambdaQueryChainWrapper queryWrapper = baseThemeQuery(local); + if (ALL_THEME_STYLE.equals(themeStyle)) { + return queryWrapper.orderByAsc(KeyboardThemes::getSort).list(); + } + return queryWrapper.eq(KeyboardThemes::getThemeStyle, themeStyle).list(); + } + + private LambdaQueryChainWrapper baseThemeQuery(String local) { + return this.lambdaQuery() + .eq(KeyboardThemes::getDeleted, false) + .eq(KeyboardThemes::getThemeStatus, true) + .eq(StringUtils.hasText(local), KeyboardThemes::getLocal, local); + } + + private List filterThemesByLocal(List themesList, String local) { + if (!StringUtils.hasText(local)) { + return themesList; + } + return themesList.stream() + .filter(theme -> local.equalsIgnoreCase(theme.getLocal())) + .collect(Collectors.toList()); + } + + private List markPurchasedThemes(List themesList, Long userId) { + Set purchasedThemeIds = getPurchasedThemeIds(userId); + return themesList.stream() + .map(theme -> toThemeResp(theme, purchasedThemeIds.contains(theme.getId()))) + .collect(Collectors.toList()); + } + + private Set getPurchasedThemeIds(Long userId) { + return purchaseService.lambdaQuery() .eq(KeyboardThemePurchase::getUserId, userId) .eq(KeyboardThemePurchase::getPayStatus, (short) 1) .list() .stream() .map(KeyboardThemePurchase::getThemeId) .collect(Collectors.toSet()); + } - // 转换为VO并设置购买状态 - return themesList.stream().map(theme -> { - KeyboardThemesRespVO vo = BeanUtil.copyProperties(theme, KeyboardThemesRespVO.class); - vo.setIsPurchased(purchasedThemeIds.contains(theme.getId())); - return vo; - }).collect(Collectors.toList()); + private boolean isThemePurchased(Long userId, Long themeId) { + return purchaseService.lambdaQuery() + .eq(KeyboardThemePurchase::getUserId, userId) + .eq(KeyboardThemePurchase::getThemeId, themeId) + .eq(KeyboardThemePurchase::getPayStatus, (short) 1) + .exists(); + } + + private KeyboardThemesRespVO toThemeResp(KeyboardThemes source, boolean isPurchased) { + KeyboardThemesRespVO vo = BeanUtil.copyProperties(source, KeyboardThemesRespVO.class); + vo.setIsPurchased(isPurchased); + return vo; + } + + private KeyboardThemesRespVO toThemeResp(KeyboardThemesRespVO source, boolean isPurchased) { + KeyboardThemesRespVO vo = BeanUtil.copyProperties(source, KeyboardThemesRespVO.class); + vo.setIsPurchased(isPurchased); + return vo; + } + + private String buildCacheKey(Long themeStyle) { + return ALL_THEME_STYLE.equals(themeStyle) ? THEME_ALL_KEY : THEME_STYLE_KEY + themeStyle; } } diff --git a/src/main/java/com/yolo/keyborad/utils/RequestLocaleUtils.java b/src/main/java/com/yolo/keyborad/utils/RequestLocaleUtils.java new file mode 100644 index 0000000..1ae16f8 --- /dev/null +++ b/src/main/java/com/yolo/keyborad/utils/RequestLocaleUtils.java @@ -0,0 +1,36 @@ +package com.yolo.keyborad.utils; + +import org.springframework.util.StringUtils; + +import java.util.Locale; + +public final class RequestLocaleUtils { + + private RequestLocaleUtils() { + } + + public static String resolveLanguage(String acceptLanguage) { + if (!StringUtils.hasText(acceptLanguage)) { + return defaultLanguage(); + } + + String preferredLanguage = acceptLanguage.split(",")[0].split(";")[0].trim(); + if (!StringUtils.hasText(preferredLanguage)) { + return defaultLanguage(); + } + + int separatorIndex = preferredLanguage.indexOf('-'); + if (separatorIndex < 0) { + separatorIndex = preferredLanguage.indexOf('_'); + } + + String language = separatorIndex > 0 + ? preferredLanguage.substring(0, separatorIndex) + : preferredLanguage; + return language.toLowerCase(Locale.ROOT); + } + + private static String defaultLanguage() { + return Locale.getDefault().getLanguage().toLowerCase(Locale.ROOT); + } +} diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index a5fc7e5..faa29e1 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -2,8 +2,8 @@ spring: datasource: driver-class-name: org.postgresql.Driver url: jdbc:postgresql://localhost:5432/keyborad_db - username: root - password: 123asd + username: keyborad_db + password: LjwYPLKKRm4Rz5r7 # 生产环境日志配置 logging: