feat(themes): 新增推荐主题与用户主题批量删除功能
- 新增 getRecommendedThemes:按真实下载量降序返回8个未购买主题 - 新增 batchDeleteUserThemes:支持用户批量逻辑删除已购主题 - 补充接口注释与 Swagger 文档,开放 /themes/recommended 免鉴权路径
This commit is contained in:
@@ -91,7 +91,8 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
|||||||
"/themes/purchase",
|
"/themes/purchase",
|
||||||
"/themes/purchased",
|
"/themes/purchased",
|
||||||
"/themes/purchase/list",
|
"/themes/purchase/list",
|
||||||
"/themes/detail"
|
"/themes/detail",
|
||||||
|
"/themes/recommended"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
@@ -83,4 +83,12 @@ public class ThemesController {
|
|||||||
return ResultUtils.success(result);
|
return ResultUtils.success(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/recommended")
|
||||||
|
@Operation(summary = "推荐主题列表", description = "按真实下载数量降序返回推荐主题")
|
||||||
|
public BaseResponse<List<KeyboardThemesRespVO>> getRecommendedThemes() {
|
||||||
|
Long userId = StpUtil.getLoginIdAsLong();
|
||||||
|
List<KeyboardThemesRespVO> result = themesService.getRecommendedThemes(userId);
|
||||||
|
return ResultUtils.success(result);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.yolo.keyborad.controller;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import com.yolo.keyborad.common.BaseResponse;
|
||||||
|
import com.yolo.keyborad.common.ResultUtils;
|
||||||
|
import com.yolo.keyborad.model.dto.usertheme.BatchDeleteUserThemesReq;
|
||||||
|
import com.yolo.keyborad.service.KeyboardUserThemesService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @author: ziin
|
||||||
|
* @date: 2025/12/11
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@Slf4j
|
||||||
|
@RequestMapping("/user-themes")
|
||||||
|
@Tag(name = "用户主题")
|
||||||
|
public class UserThemesController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private KeyboardUserThemesService userThemesService;
|
||||||
|
|
||||||
|
@PostMapping("/batch-delete")
|
||||||
|
@Operation(summary = "批量删除用户主题", description = "批量逻辑删除用户主题")
|
||||||
|
public BaseResponse<Boolean> batchDeleteUserThemes(@RequestBody BatchDeleteUserThemesReq req) {
|
||||||
|
Long userId = StpUtil.getLoginIdAsLong();
|
||||||
|
userThemesService.batchDeleteUserThemes(userId, req.getThemeIds());
|
||||||
|
return ResultUtils.success(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.yolo.keyborad.model.dto.usertheme;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @author: ziin
|
||||||
|
* @date: 2025/12/11
|
||||||
|
*/
|
||||||
|
@Schema(description = "批量删除用户主题请求")
|
||||||
|
@Data
|
||||||
|
public class BatchDeleteUserThemesReq {
|
||||||
|
/**
|
||||||
|
* 主题ID列表
|
||||||
|
*/
|
||||||
|
@Schema(description = "主题ID列表")
|
||||||
|
private List<Long> themeIds;
|
||||||
|
}
|
||||||
@@ -29,4 +29,11 @@ public interface KeyboardThemesService extends IService<KeyboardThemes>{
|
|||||||
*/
|
*/
|
||||||
KeyboardThemesRespVO getThemeDetail(Long themeId, Long userId);
|
KeyboardThemesRespVO getThemeDetail(Long themeId, Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推荐主题列表(按真实下载数量降序)
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return 推荐主题列表
|
||||||
|
*/
|
||||||
|
List<KeyboardThemesRespVO> getRecommendedThemes(Long userId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,20 @@ package com.yolo.keyborad.service;
|
|||||||
|
|
||||||
import com.yolo.keyborad.model.entity.KeyboardUserThemes;
|
import com.yolo.keyborad.model.entity.KeyboardUserThemes;
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
/*
|
|
||||||
|
import java.util.List;
|
||||||
|
/*
|
||||||
* @author: ziin
|
* @author: ziin
|
||||||
* @date: 2025/12/11 13:31
|
* @date: 2025/12/11 13:31
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public interface KeyboardUserThemesService extends IService<KeyboardUserThemes>{
|
public interface KeyboardUserThemesService extends IService<KeyboardUserThemes>{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除用户主题(逻辑删除)
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param themeIds 主题ID列表
|
||||||
|
*/
|
||||||
|
void batchDeleteUserThemes(Long userId, List<Long> themeIds);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,16 +27,34 @@ public class KeyboardThemesServiceImpl extends ServiceImpl<KeyboardThemesMapper,
|
|||||||
@Lazy // 延迟加载,打破循环依赖
|
@Lazy // 延迟加载,打破循环依赖
|
||||||
private KeyboardThemePurchaseService purchaseService;
|
private KeyboardThemePurchaseService purchaseService;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据风格查询主题列表
|
||||||
|
* <p>查询规则:</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>当themeStyle为9999时,查询所有主题并按排序字段升序排列</li>
|
||||||
|
* <li>其他情况下,查询指定风格的主题</li>
|
||||||
|
* <li>查询结果均过滤已删除和未启用的主题</li>
|
||||||
|
* <li>返回的主题列表包含用户的购买状态</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param themeStyle 主题风格ID,9999表示查询所有风格
|
||||||
|
* @param userId 用户ID,用于判断主题购买状态
|
||||||
|
* @return 主题列表,包含主题详情和购买状态
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<KeyboardThemesRespVO> selectThemesByStyle(Long themeStyle, Long userId) {
|
public List<KeyboardThemesRespVO> selectThemesByStyle(Long themeStyle, Long userId) {
|
||||||
|
// 根据风格参数查询主题列表
|
||||||
List<KeyboardThemes> themesList;
|
List<KeyboardThemes> themesList;
|
||||||
if (themeStyle == 9999) {
|
if (themeStyle == 9999) {
|
||||||
|
// 查询所有主题,按排序字段升序
|
||||||
themesList = this.lambdaQuery()
|
themesList = this.lambdaQuery()
|
||||||
.eq(KeyboardThemes::getDeleted, false)
|
.eq(KeyboardThemes::getDeleted, false)
|
||||||
.eq(KeyboardThemes::getThemeStatus, true)
|
.eq(KeyboardThemes::getThemeStatus, true)
|
||||||
.orderByAsc(KeyboardThemes::getSort)
|
.orderByAsc(KeyboardThemes::getSort)
|
||||||
.list();
|
.list();
|
||||||
} else {
|
} else {
|
||||||
|
// 查询指定风格的主题
|
||||||
themesList = this.lambdaQuery()
|
themesList = this.lambdaQuery()
|
||||||
.eq(KeyboardThemes::getDeleted, false)
|
.eq(KeyboardThemes::getDeleted, false)
|
||||||
.eq(KeyboardThemes::getThemeStatus, true)
|
.eq(KeyboardThemes::getThemeStatus, true)
|
||||||
@@ -44,6 +62,7 @@ public class KeyboardThemesServiceImpl extends ServiceImpl<KeyboardThemesMapper,
|
|||||||
.list();
|
.list();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询用户已购买的主题ID集合
|
||||||
Set<Long> purchasedThemeIds = purchaseService.lambdaQuery()
|
Set<Long> purchasedThemeIds = purchaseService.lambdaQuery()
|
||||||
.eq(KeyboardThemePurchase::getUserId, userId)
|
.eq(KeyboardThemePurchase::getUserId, userId)
|
||||||
.eq(KeyboardThemePurchase::getPayStatus, (short) 1)
|
.eq(KeyboardThemePurchase::getPayStatus, (short) 1)
|
||||||
@@ -52,6 +71,7 @@ public class KeyboardThemesServiceImpl extends ServiceImpl<KeyboardThemesMapper,
|
|||||||
.map(KeyboardThemePurchase::getThemeId)
|
.map(KeyboardThemePurchase::getThemeId)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
// 转换为VO并设置购买状态
|
||||||
return themesList.stream().map(theme -> {
|
return themesList.stream().map(theme -> {
|
||||||
KeyboardThemesRespVO vo = BeanUtil.copyProperties(theme, KeyboardThemesRespVO.class);
|
KeyboardThemesRespVO vo = BeanUtil.copyProperties(theme, KeyboardThemesRespVO.class);
|
||||||
vo.setIsPurchased(purchasedThemeIds.contains(theme.getId()));
|
vo.setIsPurchased(purchasedThemeIds.contains(theme.getId()));
|
||||||
@@ -59,26 +79,73 @@ public class KeyboardThemesServiceImpl extends ServiceImpl<KeyboardThemesMapper,
|
|||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取主题详情
|
||||||
|
* <p>查询指定ID的主题详情,并返回用户对该主题的购买状态</p>
|
||||||
|
*
|
||||||
|
* @param themeId 主题ID
|
||||||
|
* @param userId 用户ID,用于判断购买状态
|
||||||
|
* @return 主题详情VO,包含主题信息和购买状态;如果主题不存在或已删除则返回null
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public KeyboardThemesRespVO getThemeDetail(Long themeId, Long userId) {
|
public KeyboardThemesRespVO getThemeDetail(Long themeId, Long userId) {
|
||||||
|
// 查询主题详情
|
||||||
KeyboardThemes theme = this.lambdaQuery()
|
KeyboardThemes theme = this.lambdaQuery()
|
||||||
.eq(KeyboardThemes::getId, themeId)
|
.eq(KeyboardThemes::getId, themeId)
|
||||||
.eq(KeyboardThemes::getDeleted, false)
|
.eq(KeyboardThemes::getDeleted, false)
|
||||||
.eq(KeyboardThemes::getThemeStatus, true)
|
.eq(KeyboardThemes::getThemeStatus, true)
|
||||||
.one();
|
.one();
|
||||||
|
|
||||||
|
// 主题不存在或已删除,返回null
|
||||||
if (theme == null) {
|
if (theme == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询用户是否购买该主题
|
||||||
boolean isPurchased = purchaseService.lambdaQuery()
|
boolean isPurchased = purchaseService.lambdaQuery()
|
||||||
.eq(KeyboardThemePurchase::getUserId, userId)
|
.eq(KeyboardThemePurchase::getUserId, userId)
|
||||||
.eq(KeyboardThemePurchase::getThemeId, themeId)
|
.eq(KeyboardThemePurchase::getThemeId, themeId)
|
||||||
.eq(KeyboardThemePurchase::getPayStatus, (short) 1)
|
.eq(KeyboardThemePurchase::getPayStatus, (short) 1)
|
||||||
.exists();
|
.exists();
|
||||||
|
|
||||||
|
// 转换为VO并设置购买状态
|
||||||
KeyboardThemesRespVO vo = BeanUtil.copyProperties(theme, KeyboardThemesRespVO.class);
|
KeyboardThemesRespVO vo = BeanUtil.copyProperties(theme, KeyboardThemesRespVO.class);
|
||||||
vo.setIsPurchased(isPurchased);
|
vo.setIsPurchased(isPurchased);
|
||||||
return vo;
|
return vo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
/*
|
||||||
|
获取推荐主题列表
|
||||||
|
<p>推荐规则:根据真实下载量降序排序,排除用户已购买的主题,最多返回8个主题</p>
|
||||||
|
|
||||||
|
@param userId 用户ID
|
||||||
|
* @return 推荐主题列表,包含主题详情和购买状态(推荐列表中的主题购买状态均为未购买)
|
||||||
|
*/
|
||||||
|
public List<KeyboardThemesRespVO> getRecommendedThemes(Long userId) {
|
||||||
|
// 查询用户已购买的主题ID集合
|
||||||
|
Set<Long> purchasedThemeIds = purchaseService.lambdaQuery()
|
||||||
|
.eq(KeyboardThemePurchase::getUserId, userId)
|
||||||
|
.eq(KeyboardThemePurchase::getPayStatus, (short) 1)
|
||||||
|
.list()
|
||||||
|
.stream()
|
||||||
|
.map(KeyboardThemePurchase::getThemeId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
// 查询推荐主题列表:未删除、已启用、未购买、按真实下载量降序、限制8条
|
||||||
|
return this.lambdaQuery()
|
||||||
|
.eq(KeyboardThemes::getDeleted, false)
|
||||||
|
.eq(KeyboardThemes::getThemeStatus, true)
|
||||||
|
.notIn(!purchasedThemeIds.isEmpty(), KeyboardThemes::getId, purchasedThemeIds)
|
||||||
|
.orderByDesc(KeyboardThemes::getRealDownloadCount)
|
||||||
|
.last("LIMIT 8")
|
||||||
|
.list()
|
||||||
|
.stream()
|
||||||
|
.map(theme -> {
|
||||||
|
KeyboardThemesRespVO vo = BeanUtil.copyProperties(theme, KeyboardThemesRespVO.class);
|
||||||
|
// 推荐列表中的主题均为未购买状态
|
||||||
|
vo.setIsPurchased(false);
|
||||||
|
return vo;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.yolo.keyborad.service.impl;
|
package com.yolo.keyborad.service.impl;
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.yolo.keyborad.model.entity.KeyboardUserThemes;
|
import com.yolo.keyborad.model.entity.KeyboardUserThemes;
|
||||||
@@ -15,4 +14,19 @@ import com.yolo.keyborad.service.KeyboardUserThemesService;
|
|||||||
@Service
|
@Service
|
||||||
public class KeyboardUserThemesServiceImpl extends ServiceImpl<KeyboardUserThemesMapper, KeyboardUserThemes> implements KeyboardUserThemesService{
|
public class KeyboardUserThemesServiceImpl extends ServiceImpl<KeyboardUserThemesMapper, KeyboardUserThemes> implements KeyboardUserThemesService{
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除用户主题(逻辑删除)
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param themeIds 主题ID列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void batchDeleteUserThemes(Long userId, List<Long> themeIds) {
|
||||||
|
this.lambdaUpdate()
|
||||||
|
.eq(KeyboardUserThemes::getUserId, userId)
|
||||||
|
.in(KeyboardUserThemes::getThemeId, themeIds)
|
||||||
|
.set(KeyboardUserThemes::getViewDeleted, true)
|
||||||
|
.update();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import com.yolo.keyborad.model.entity.KeyboardUserWallet;
|
|||||||
import com.yolo.keyborad.service.KeyboardUserWalletService;
|
import com.yolo.keyborad.service.KeyboardUserWalletService;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @author: ziin
|
* @author: ziin
|
||||||
@@ -33,9 +34,9 @@ public class KeyboardUserWalletServiceImpl extends ServiceImpl<KeyboardUserWalle
|
|||||||
|
|
||||||
private String formatBalance(BigDecimal balance) {
|
private String formatBalance(BigDecimal balance) {
|
||||||
if (balance.compareTo(new BigDecimal("10000")) >= 0) {
|
if (balance.compareTo(new BigDecimal("10000")) >= 0) {
|
||||||
BigDecimal kValue = balance.divide(new BigDecimal("1000"), 2, BigDecimal.ROUND_HALF_UP);
|
BigDecimal kValue = balance.divide(new BigDecimal("1000"), 2, RoundingMode.HALF_UP);
|
||||||
return kValue.stripTrailingZeros().toPlainString() + "K";
|
return kValue.stripTrailingZeros().toPlainString() + "K";
|
||||||
}
|
}
|
||||||
return balance.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString();
|
return balance.setScale(2, RoundingMode.HALF_UP).toPlainString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user