第8天 详情提交

This commit is contained in:
Y1NanPing 2025-07-28 15:14:51 +08:00
parent 93512b68d8
commit ed419a4bee
58 changed files with 797 additions and 182 deletions

View File

@ -3,11 +3,15 @@ package com.atguigu.tingshu.album;
import com.atguigu.tingshu.album.impl.AlbumDegradeFeignClient;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.BaseCategory3;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.List;
/**
* <p>
* 专辑模块远程调用Feign接口
@ -34,4 +38,13 @@ public interface AlbumFeignClient {
@GetMapping("/category/getCategoryView/{category3Id}")
public Result<BaseCategoryView> getCategoryView(@PathVariable Long category3Id);
@GetMapping("/category/findTopBaseCategory3/{category1Id}")
public Result<List<BaseCategory3>> findTopBaseCategory3(@PathVariable Long category1Id);
@GetMapping("/albumInfo/getAlbumStatVo/{albumId}")
public Result<AlbumStatVo>getAlbumStatVo(@PathVariable Long albumId);
}

View File

@ -4,10 +4,14 @@ package com.atguigu.tingshu.album.impl;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.BaseCategory3;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@Slf4j
public class AlbumDegradeFeignClient implements AlbumFeignClient {
@ -24,4 +28,16 @@ public class AlbumDegradeFeignClient implements AlbumFeignClient {
log.error("[专辑服务]提供远程调用方法getCategoryView执行服务降级");
return null;
}
@Override
public Result<List<BaseCategory3>> findTopBaseCategory3(Long category1Id) {
log.error("[专辑服务]提供远程调用方法findTopBaseCategory3执行服务降级");
return null;
}
@Override
public Result<AlbumStatVo> getAlbumStatVo(Long albumId) {
log.error("[专辑服务]提供远程调用方法getAlbumStatVo执行服务降级");
return null;
}
}

View File

@ -6,6 +6,11 @@ import com.atguigu.tingshu.vo.user.UserInfoVo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
import java.util.Map;
/**
* <p>
@ -24,4 +29,10 @@ public interface UserFeignClient {
@GetMapping("/userInfo/getUserInfoVo/{userId}")
public Result<UserInfoVo> getUserInfoVo(@PathVariable Long userId);
@PostMapping("/userInfo/userIsPaidTrack/{albumId}")
public Result<Map<Long, Integer>> userIsPaidTrack(
@PathVariable("albumId") Long albumId,
@RequestBody List<Long> needCheckPayStateTrackIdList
);
}

View File

@ -6,6 +6,10 @@ import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
@Slf4j
@Component
public class UserDegradeFeignClient implements UserFeignClient {
@ -15,4 +19,10 @@ public class UserDegradeFeignClient implements UserFeignClient {
log.error("[用户服务]提供远程调用方法getUserInfoVo执行服务降级");
return null;
}
@Override
public Result<Map<Long, Integer>> userIsPaidTrack(Long albumId, List<Long> needCheckPayStateTrackIdList) {
log.error("[用户服务]提供远程调用方法userIsPaidTrack执行服务降级");
return null;
}
}

View File

@ -8,6 +8,7 @@ import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.query.album.AlbumInfoQuery;
import com.atguigu.tingshu.vo.album.AlbumInfoVo;
import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
@ -17,6 +18,7 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@Tag(name = "专辑管理")
@RestController
@ -83,8 +85,6 @@ public class AlbumInfoApiController {
* 查询当前用户专辑列表
* @return
*/
/*这个接口是为了在声音列表中获取当前用户的所有专辑 有一个下拉框 在那里能显示内容*/
@GuiGuLogin
@Operation(summary = "查询当前用户专辑列表")
@ -98,6 +98,14 @@ public class AlbumInfoApiController {
return Result.ok(list);
}
@Operation(summary = "专辑状态统计")
@GetMapping("/albumInfo/getAlbumStatVo/{albumId}")
public Result<AlbumStatVo>getAlbumStatVo(@PathVariable Long albumId){
AlbumStatVo albumStatVo=albumInfoService.getAlbumStatVo(albumId);
return Result.ok(albumStatVo);
}

View File

@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.album.service.BaseCategoryService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.album.BaseAttribute;
import com.atguigu.tingshu.model.album.BaseCategory3;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@ -47,6 +48,24 @@ public class BaseCategoryApiController {
BaseCategoryView baseCategoryView = baseCategoryService.getCategoryView(category3Id);
return Result.ok(baseCategoryView);
}
@Operation(summary = "根据一级分类Id查询置顶7个三级分类列表")
@GetMapping("/category/findTopBaseCategory3/{category1Id}")
public Result<List<BaseCategory3>> findTopBaseCategory3(@PathVariable Long category1Id){
List<BaseCategory3> list=baseCategoryService.findTopBaseCategory3(category1Id);
return Result.ok(list);
}
/**
* 查询1级分类下包含所有二级以及三级分类
* @param category1Id
* @return
*/
@Operation(summary = "查询1级分类下包含所有二级以及三级分类")
@GetMapping("/category/getBaseCategoryList/{category1Id}")
public Result<JSONObject> getBaseCategoryListByCategory1Id(@PathVariable Long category1Id) {
JSONObject jsonObject = baseCategoryService.getBaseCategoryListByCategory1Id(category1Id);
return Result.ok(jsonObject);
}

View File

@ -18,7 +18,7 @@ public class FileUploadApiController {
@Autowired
private FileUploadService fileUploadService;
//这个接口是将文件上传到minio上
//这个接口是将文件上传到minio上 上传的时候用MultipartFile
@Operation(summary = "上传文件")
@PostMapping("/fileUpload")
public Result<String> fileUpload(@RequestParam("file") MultipartFile file) throws IOException {

View File

@ -7,6 +7,7 @@ import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.vo.album.AlbumTrackListVo;
import com.atguigu.tingshu.vo.album.TrackInfoVo;
import com.atguigu.tingshu.vo.album.TrackListVo;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -118,5 +119,31 @@ public class TrackInfoApiController {
return Result.ok();
}
/**
* 分页查询专辑下包含声音列表动态展示付费标识
*
* @param albumId 专辑ID
* @param page 页码
* @param limit 页大小
* @return 分页对象
*/
@GuiGuLogin(required = false) //无论用户是否登录都可以调用只要登录了就可以获取到用户ID
@Operation(summary = "分页查询专辑下包含声音列表(动态展示付费标识)")
@GetMapping("/trackInfo/findAlbumTrackPage/{albumId}/{page}/{limit}")
public Result<Page<AlbumTrackListVo>> findAlbumTrackPage(
@PathVariable Long albumId,
@PathVariable Long page,
@PathVariable Long limit
) {
//1.尝试获取当前用户ID-可能有值也可能没有值
Long userId = AuthContextHolder.getUserId();
//2.创建分页对象封装页码页大小
Page<AlbumTrackListVo> pageInfo = new Page<>(page, limit);
//3.调用业务逻辑层持久层封装当前页记录总页数等
pageInfo = trackInfoService.findAlbumTrackPage(pageInfo, userId, albumId);
//4.响应结果
return Result.ok(pageInfo);
}
}

View File

@ -3,6 +3,7 @@ package com.atguigu.tingshu.album.mapper;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.query.album.AlbumInfoQuery;
import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.ibatis.annotations.Mapper;
@ -12,4 +13,6 @@ import org.apache.ibatis.annotations.Param;
public interface AlbumInfoMapper extends BaseMapper<AlbumInfo> {
IPage<AlbumListVo> findUserAlbumPage(IPage<AlbumListVo> pageInfo, @Param("vo")AlbumInfoQuery albumInfoQuery);
AlbumStatVo getAlbumStatVo(Long albumId);
}

View File

@ -2,6 +2,7 @@ package com.atguigu.tingshu.album.mapper;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.vo.album.AlbumTrackListVo;
import com.atguigu.tingshu.vo.album.TrackListVo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -13,4 +14,6 @@ public interface TrackInfoMapper extends BaseMapper<TrackInfo> {
Page<TrackListVo> findUserTrackPage(Page<TrackListVo> pageInfo,@Param("vo")TrackInfoQuery trackInfoQuery);
Page<AlbumTrackListVo> findAlbumTrackPage(Page<AlbumTrackListVo> pageInfo, Long albumId);
}

View File

@ -4,6 +4,7 @@ import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.query.album.AlbumInfoQuery;
import com.atguigu.tingshu.vo.album.AlbumInfoVo;
import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
@ -27,4 +28,6 @@ public interface AlbumInfoService extends IService<AlbumInfo> {
void updateAlbumInfo(Long id, AlbumInfoVo albumInfoVo);
List<AlbumInfo> findUserAllAlbumList(Long userId);
AlbumStatVo getAlbumStatVo(Long albumId);
}

View File

@ -3,6 +3,7 @@ package com.atguigu.tingshu.album.service;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.model.album.BaseAttribute;
import com.atguigu.tingshu.model.album.BaseCategory1;
import com.atguigu.tingshu.model.album.BaseCategory3;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.baomidou.mybatisplus.extension.service.IService;
@ -16,4 +17,8 @@ public interface BaseCategoryService extends IService<BaseCategory1> {
List<BaseAttribute> findAttribute(Long category1Id);
BaseCategoryView getCategoryView(Long category3Id);
List<BaseCategory3> findTopBaseCategory3(Long category1Id);
JSONObject getBaseCategoryListByCategory1Id(Long category1Id);
}

View File

@ -2,6 +2,7 @@ package com.atguigu.tingshu.album.service;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.vo.album.AlbumTrackListVo;
import com.atguigu.tingshu.vo.album.TrackInfoVo;
import com.atguigu.tingshu.vo.album.TrackListVo;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -32,4 +33,7 @@ public interface TrackInfoService extends IService<TrackInfo> {
void updateTrackInfo(Long id, TrackInfoVo trackInfoVo);
void removeTrackInfo(Long id);
Page<AlbumTrackListVo> findAlbumTrackPage(Page<AlbumTrackListVo> pageInfo, Long userId, Long albumId);
}

View File

@ -20,6 +20,7 @@ import com.atguigu.tingshu.query.album.AlbumInfoQuery;
import com.atguigu.tingshu.vo.album.AlbumAttributeValueVo;
import com.atguigu.tingshu.vo.album.AlbumInfoVo;
import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
@ -224,4 +225,9 @@ public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo
}
@Override
public AlbumStatVo getAlbumStatVo(Long albumId) {
return albumInfoMapper.getAlbumStatVo(albumId);
}
}

View File

@ -1,11 +1,11 @@
package com.atguigu.tingshu.album.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.album.mapper.*;
import com.atguigu.tingshu.album.service.BaseCategoryService;
import com.atguigu.tingshu.model.album.BaseAttribute;
import com.atguigu.tingshu.model.album.BaseCategory1;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.atguigu.tingshu.model.album.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -113,4 +113,79 @@ public class BaseCategoryServiceImpl extends ServiceImpl<BaseCategory1Mapper, Ba
public BaseCategoryView getCategoryView(Long category3Id) {
return baseCategoryViewMapper.selectById(category3Id);
}
@Override
public List<BaseCategory3> findTopBaseCategory3(Long category1Id) {
List<BaseCategory2> baseCategory2List = baseCategory2Mapper.selectList( new LambdaQueryWrapper<BaseCategory2>()
.eq(BaseCategory2::getCategory1Id, category1Id)
.select(BaseCategory2::getId));
if (CollUtil.isNotEmpty(baseCategory2List)) {
List<Long> category2IdList = baseCategory2List.stream()
.map(BaseCategory2::getId).collect(Collectors.toList());
//2.根据二级分类ID列表查询置顶前7个个三级分类列表
LambdaQueryWrapper<BaseCategory3> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(BaseCategory3::getCategory2Id, category2IdList);
queryWrapper.eq(BaseCategory3::getIsTop, 1);
queryWrapper.orderByAsc(BaseCategory3::getOrderNum);
queryWrapper.last("limit 7");
return baseCategory3Mapper.selectList(queryWrapper);
}
return null;
}
@Override
public JSONObject getBaseCategoryListByCategory1Id(Long category1Id) {
//1.处理1级分类
//1.1 根据1级分类ID查询分类视图得到"1级"分类列表获取列表第一条记录即可
List<BaseCategoryView> baseCategory1List = baseCategoryViewMapper.selectList(
new LambdaQueryWrapper<BaseCategoryView>()
.eq(BaseCategoryView::getCategory1Id, category1Id)
);
//1.2 创建1级分类JSON对象
JSONObject jsonObject1 = new JSONObject();
//1.2.1 封装1级分类对象中分类ID
jsonObject1.put("categoryId", baseCategory1List.get(0).getCategory1Id());
//1.2.2 封装1级分类对象中分类名称
jsonObject1.put("categoryName", baseCategory1List.get(0).getCategory1Name());
//2.处理2级分类
//2.1 创建2级分类JSON对象集合
List<JSONObject> jsonObject2List = new ArrayList<>();
//2.2 "1级分类"列表按照2级分类ID进行分组得到"2级分类"Map
Map<Long, List<BaseCategoryView>> map2 = baseCategory1List.stream()
.collect(Collectors.groupingBy(BaseCategoryView::getCategory2Id));
//2.2 遍历"2级分类"Map
for (Map.Entry<Long, List<BaseCategoryView>> entry2 : map2.entrySet()) {
//2.3 创建2级分类JSON对象
JSONObject jsonObject2 = new JSONObject();
//2.3.1 封装2级分类JSON对象分类ID
jsonObject2.put("categoryId", entry2.getKey());
//2.3.2 封装2级分类JSON对象分类名称
jsonObject2.put("categoryName", entry2.getValue().get(0).getCategory2Name());
//2.4 将2级分类对象加入到2级分类集合中
jsonObject2List.add(jsonObject2);
//3.处理3级分类
//3.1 创建3级分类集合
List<JSONObject> jsonObject3List = new ArrayList<>();
//3.2 遍历"2级分类"列表
for (BaseCategoryView baseCategoryView : entry2.getValue()) {
//3.3 创建3级分类JSON对象
JSONObject jsonObject3 = new JSONObject();
//3.3.1 封装3级分类ID
jsonObject3.put("categoryId", baseCategoryView.getCategory3Id());
//3.3.2 封装3级分类名称
jsonObject3.put("categoryName", baseCategoryView.getCategory3Name());
//3.4 将3级分类对象加入到3级分类集合中
jsonObject3List.add(jsonObject3);
}
//3.5 将3级分类集合加入2级分类对象"categoryChild"属性中
jsonObject2.put("categoryChild", jsonObject3List);
}
//2.5 将2级分类集合加入到1级分类对象中"categoryChild"属性中
jsonObject1.put("categoryChild", jsonObject2List);
//4.响应1级分类对象
return jsonObject1;
}
}

View File

@ -1,6 +1,7 @@
package com.atguigu.tingshu.album.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
@ -12,9 +13,12 @@ import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.album.AlbumTrackListVo;
import com.atguigu.tingshu.vo.album.TrackInfoVo;
import com.atguigu.tingshu.vo.album.TrackListVo;
import com.atguigu.tingshu.vo.album.TrackMediaInfoVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -26,7 +30,13 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static com.atguigu.tingshu.common.constant.SystemConstant.ALBUM_PAY_TYPE_REQUIRE;
import static com.atguigu.tingshu.common.constant.SystemConstant.ALBUM_PAY_TYPE_VIPFREE;
@Slf4j
@Service
@ -43,6 +53,8 @@ public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo
private AuditService auditService;
@Autowired
private TrackStatMapper trackStatMapper;
@Autowired
private UserFeignClient userFeignClient;
@Override
@ -106,6 +118,7 @@ public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo
}
/**
* 保存声音统计信息
*
@ -121,6 +134,7 @@ public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo
trackStat.setStatNum(statNum);
trackStatMapper.insert(trackStat);
}
/**
* 分页条件查询当前用户声音列表
*
@ -216,5 +230,72 @@ public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo
}
@Override
public Page<AlbumTrackListVo> findAlbumTrackPage(Page<AlbumTrackListVo> pageInfo, Long userId, Long albumId) {
//1.分页查询专辑下声音列表 默认isShowPaidMark = false;
pageInfo = trackInfoMapper.findAlbumTrackPage(pageInfo, albumId);
//TODO 根据用户登录状态身份专辑付费类型是否购买综合判断付费标识
//2.根据专辑ID查询专辑信息得到专辑付费类型以及免费试听集数
AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
//付费类型: 0101-免费0102-vip免费0103-付费
String payType = albumInfo.getPayType();
Integer tracksForFree = albumInfo.getTracksForFree();
//3.处理未登录情况
if (userId == null) {
if (ALBUM_PAY_TYPE_VIPFREE.equals(payType) || ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
//将试听以外其他声音付费标识改为true
pageInfo.getRecords()
.stream()
.filter(track -> track.getOrderNum() > tracksForFree)
.forEach(track -> track.setIsShowPaidMark(true));
}
} else {
//4.处理已登录情况
//4.1 远程调用用户服务获取用户身份是否为VIP
Boolean isVIP = false;
UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
Assert.notNull(userInfoVo, "用户{}不存在", userId);
if (userInfoVo.getIsVip().intValue() == 1
&& userInfoVo.getVipExpireTime().after(new Date())) {
isVIP = true;
}
//先设定一个默认值 是否需要检查付费状态
Boolean isNeedCheckPayState = false;
//4.2 如果普通用户查看付费类型是VIP免费专辑则需要进一步判断声音购买情况
if (!isVIP && ALBUM_PAY_TYPE_VIPFREE.equals(payType)) {
isNeedCheckPayState = true;
}
//4.3 如果付费类型是付费无论什么用户都需要进一步判断声音购买情况
if (ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
isNeedCheckPayState = true;
}
//4.4 如果需要判断声音购买情况远程调用用户服务
if (isNeedCheckPayState) {
//4.4.1 获取当前页中去掉试听部分声音ID列表
List<Long> needCheckPayStateTrackIdList = pageInfo.getRecords()
.stream()
.filter(track -> track.getOrderNum() > tracksForFree)
.map(AlbumTrackListVo::getTrackId)
.collect(Collectors.toList());
//4.4.2 远程调用用户服务获取每个声音的购买情况
Map<Long, Integer> payStatusMap = userFeignClient.userIsPaidTrack(albumId, needCheckPayStateTrackIdList).getData();
//4.5 根据响应声音购买情况(去除掉试听部分声音ID)将未购买声音付费标识设置为true
pageInfo.getRecords()
.stream()
.filter(track -> track.getOrderNum() > tracksForFree)
.forEach(track-> track.setIsShowPaidMark(payStatusMap.get(track.getTrackId()) == 0));
}
}
return pageInfo;
}
}

View File

@ -34,5 +34,15 @@
</select>
<select id="getAlbumStatVo" resultType="com.atguigu.tingshu.vo.album.AlbumStatVo">
select
max(if(stat_type = '0401', stat_num, 0)) playStatNum,
max(if(stat_type = '0402', stat_num, 0)) subscribeStatNum,
max(if(stat_type = '0403', stat_num, 0)) buyStatNum,
max(if(stat_type = '0404', stat_num, 0)) commentStatNum
from album_stat where album_id = #{albumId} and is_deleted = 0
</select>
</mapper>

View File

@ -32,5 +32,23 @@
order by ti.id desc
</select>
<select id="findAlbumTrackPage" resultType="com.atguigu.tingshu.vo.album.AlbumTrackListVo">
select
ti.id track_id,
ti.track_title,
ti.media_duration,
ti.create_time,
ti.order_num,
max(if(stat_type = '0701', stat_num, 0)) playStatNum,
max(if(stat_type = '0702', stat_num, 0)) collectStatNum,
max(if(stat_type = '0703', stat_num, 0)) praiseStatNum,
max(if(stat_type = '0704', stat_num, 0)) commentStatNum
from track_info ti inner join track_stat stat
on stat.track_id = ti.id and stat.is_deleted = 0
where album_id = #{albumId} and status = '0501' and ti.is_deleted = 0
group by ti.id
order by ti.order_num
</select>
</mapper>

View File

@ -34,5 +34,15 @@
</select>
<select id="getAlbumStatVo" resultType="com.atguigu.tingshu.vo.album.AlbumStatVo">
select
max(if(stat_type = '0401', stat_num, 0)) playStatNum,
max(if(stat_type = '0402', stat_num, 0)) subscribeStatNum,
max(if(stat_type = '0403', stat_num, 0)) buyStatNum,
max(if(stat_type = '0404', stat_num, 0)) commentStatNum
from album_stat where album_id = #{albumId} and is_deleted = 0
</select>
</mapper>

View File

@ -32,5 +32,23 @@
order by ti.id desc
</select>
<select id="findAlbumTrackPage" resultType="com.atguigu.tingshu.vo.album.AlbumTrackListVo">
select
ti.id track_id,
ti.track_title,
ti.media_duration,
ti.create_time,
ti.order_num,
max(if(stat_type = '0701', stat_num, 0)) playStatNum,
max(if(stat_type = '0702', stat_num, 0)) collectStatNum,
max(if(stat_type = '0703', stat_num, 0)) praiseStatNum,
max(if(stat_type = '0704', stat_num, 0)) commentStatNum
from track_info ti inner join track_stat stat
on stat.track_id = ti.id and stat.is_deleted = 0
where album_id = #{albumId} and status = '0501' and ti.is_deleted = 0
group by ti.id
order by ti.order_num
</select>
</mapper>

View File

@ -9,6 +9,9 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@Tag(name = "搜索专辑管理")
@RestController
@RequestMapping("api/search")
@ -54,6 +57,17 @@ public class SearchApiController {
return Result.ok(albumSearchResponseVo);
}
/**
* 查询1级分类下置顶3级分类热度TOP6专辑
* @param category1Id
* @return [{"baseCategory3":{三级分类对象},list:[专辑列表]},,{其他6个置顶分类热门专辑Map}]
*/
@Operation(summary = "查询1级分类下置顶3级分类热度TOP6专辑")
@GetMapping("/albumInfo/channel/{category1Id}")
public Result<List<Map<String, Object>>> channel(@PathVariable Long category1Id) {
List<Map<String, Object>> list = searchService.channel(category1Id);
return Result.ok(list);
}
}

View File

@ -1,11 +1,17 @@
package com.atguigu.tingshu.search.api;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.search.service.ItemService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@Tag(name = "专辑详情管理")
@RestController
@RequestMapping("api/search")
@ -15,5 +21,19 @@ public class itemApiController {
@Autowired
private ItemService itemService;
/**
* 获取专辑详情页渲染所需参数
*
* @param albumId
* @return
*/
@Operation(summary = "获取专辑详情页渲染所需参数")
@GetMapping("/albumInfo/{albumId}")
public Result<Map<String, Object>> getItem(@PathVariable Long albumId) {
Map<String, Object> map = itemService.getItem(albumId);
return Result.ok(map);
}
}

View File

@ -1,7 +1,9 @@
package com.atguigu.tingshu.search.service;
import java.util.Map;
public interface ItemService {
Map<String, Object> getItem(Long albumId);
}

View File

@ -3,6 +3,9 @@ package com.atguigu.tingshu.search.service;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import java.util.List;
import java.util.Map;
public interface SearchService {
@ -11,4 +14,9 @@ public interface SearchService {
void lowerAlbum(Long albumId);
AlbumSearchResponseVo search(AlbumIndexQuery albumIndexQuery);
List<Map<String, Object>> channel(Long category1Id);
}

View File

@ -1,13 +1,79 @@
package com.atguigu.tingshu.search.service.impl;
import cn.hutool.core.lang.Assert;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.atguigu.tingshu.search.service.ItemService;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@Slf4j
@Service
@SuppressWarnings({"all"})
public class ItemServiceImpl implements ItemService {
@Autowired
private UserFeignClient userFeignClient;
@Autowired
private AlbumFeignClient albumFeignClient;
@Autowired
private Executor threadPoolTaskExecutor;
@Override
public Map<String, Object> getItem(Long albumId) {
//1.创建封装详情页map对象,由于多线程 hashmap线程不安全,所以我们使用concurrentHashMap
Map<String, Object> map = new ConcurrentHashMap<>();
//2.异步远程调用专辑服务查询专辑信息封装专辑信息
CompletableFuture<AlbumInfo> albumInfoCompletableFuture = CompletableFuture.supplyAsync(() -> {
AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();
Assert.notNull(albumInfo, "专辑{}不存在", albumId);
map.put("albumInfo", albumInfo);
return albumInfo;
}, threadPoolTaskExecutor);
//3.远程调用专辑服务查询分类信息封装分类对象
CompletableFuture<Void> categoryViewCompletableFuture = albumInfoCompletableFuture.thenAcceptAsync(albumInfo -> {
BaseCategoryView baseCategoryView = albumFeignClient.getCategoryView(albumInfo.getCategory3Id()).getData();
Assert.notNull(baseCategoryView, "分类{}不存在", albumInfo.getCategory3Id());
map.put("baseCategoryView", baseCategoryView);
}, threadPoolTaskExecutor);
//4.远程调用专辑服务查询统计数值封装统计对象
CompletableFuture<Void> albumStatCompletableFuture = CompletableFuture.runAsync(() -> {
AlbumStatVo albumStatVo = albumFeignClient.getAlbumStatVo(albumId).getData();
Assert.notNull(albumStatVo, "专辑统计{}不存在", albumId);
map.put("albumStatVo", albumStatVo);
}, threadPoolTaskExecutor);
//5.远程调用用户服务查询主播信息封装主播对象
CompletableFuture<Void> announcerCompletableFuture = albumInfoCompletableFuture.thenAcceptAsync(albumInfo -> {
UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(albumInfo.getUserId()).getData();
Assert.notNull(userInfoVo, "用户{}不存在", albumInfo.getUserId());
map.put("announcer", userInfoVo);
}, threadPoolTaskExecutor);
//6.采用异步编排对四项任务组合所有异步任务都必须执行完成
CompletableFuture.allOf(
albumInfoCompletableFuture,
categoryViewCompletableFuture,
albumStatCompletableFuture,
announcerCompletableFuture
).orTimeout(1, TimeUnit.SECONDS)
.join();
//7.响应map
return map;
}
}

View File

@ -2,17 +2,24 @@ package com.atguigu.tingshu.search.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.RandomUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.aggregations.LongTermsBucket;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.BaseCategory3;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
@ -28,9 +35,11 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
@ -162,7 +171,6 @@ public class SearchServiceImpl implements SearchService {
}
private static final String INDEX_NAME = "albuminfo";
@ -262,6 +270,7 @@ public class SearchServiceImpl implements SearchService {
//4.基于检索对象构建器对象返回实际检索请求对象
return builder.build();
}
private AlbumSearchResponseVo parseResult(SearchResponse<AlbumInfoIndex> searchResponse, AlbumIndexQuery albumIndexQuery) {
//1.创建响应VO对象
AlbumSearchResponseVo vo = new AlbumSearchResponseVo();
@ -297,4 +306,72 @@ public class SearchServiceImpl implements SearchService {
//4.响应VO对象
return vo;
}
@Override
public List<Map<String, Object>> channel(Long category1Id) {
try {
//1.远程调用专辑服务获取1级分类下7个置顶分类列表
//1.1 发起远程调用
List<BaseCategory3> baseCategory3List = albumFeignClient.findTopBaseCategory3(category1Id).getData();
Assert.notNull(baseCategory3List, "没有查询到分类列表");
//1.2 为了后续封装置顶分类热门专辑Map对象中 分类信息 将分类集合转为Map对象 key:分类ID value:分类对象
Map<Long, BaseCategory3> category3Map = baseCategory3List
.stream()
.collect(Collectors.toMap(BaseCategory3::getId, c3 -> c3));
//1.3 基于3级分类集合转为FieldValue封装每个三级分类ID
List<FieldValue> fieldValueList =
baseCategory3List.stream().map(c3 -> FieldValue.of(c3.getId())).collect(Collectors.toList());
//2.检索ES获取每个置顶分类及包含热度前六专辑列表
SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(
s -> s.index(INDEX_NAME)
.query(q -> q.terms(t -> t.field("category3Id").terms(t1 -> t1.value(fieldValueList))))
.aggregations(
"c3_agg", a -> a.terms(t -> t.field("category3Id").size(7))
.aggregations(
"top6", a1 -> a1.topHits(t -> t.size(6).sort(s1 -> s1.field(f -> f.field("hotScore").order(SortOrder.Desc))).source(s1 -> s1.filter(f -> f.excludes("attributeValueIndexList",
"hotScore",
"commentStatNum",
"buyStatNum",
"subscribeStatNum",
"announcerName"))))
)
)
.size(0),
AlbumInfoIndex.class);
//3.解析ES检索结果封装数据并返回
Map<String, Aggregate> aggregations = searchResponse.aggregations();
if (CollUtil.isNotEmpty(aggregations)) {
//3.1 将聚合结果转为LongTermsAggregate获取聚合buckets桶数组
List<LongTermsBucket> buckets = aggregations.get("c3_agg").lterms().buckets().array();
//3.2 遍历buckets桶数组每遍历一次产出置顶分类热门专辑Map对象
List<Map<String, Object>> mapList = buckets.stream().map(bucket -> {
//3.2.1 创建置顶分类热门专辑Map对象
Map<String, Object> map = new HashMap<>();
//3.2.2 封装三级分类对象
long category3Id = bucket.key();
map.put("baseCategory3", category3Map.get(category3Id));
//3.2.3 封装对应热度最高top6专辑列表 将子聚合类型转为TopHitsAggregate
List<AlbumInfoIndex> top6List = bucket.aggregations()
.get("top6")
.topHits()
.hits()
.hits()
.stream()
.map(hit -> {
//采用FastJson将专辑JSON转为索引库文档对象
return JSON.parseObject(hit.source().toString(), AlbumInfoIndex.class);
}).collect(Collectors.toList());
map.put("list", top6List);
return map;
}).collect(Collectors.toList());
return mapList;
}
return null;
} catch (IOException e) {
log.error("ES检索异常{}", e.getMessage());
throw new RuntimeException(e);
}
}}

View File

@ -1,15 +1,17 @@
package com.atguigu.tingshu.user.api;
import com.atguigu.tingshu.common.login.GuiGuLogin;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@Tag(name = "用户管理接口")
@RestController
@ -29,7 +31,26 @@ public class UserInfoApiController {
public Result<UserInfoVo> getUserInfoVo(@PathVariable Long userId){
UserInfoVo userInfoVo = userInfoService.getUserInfo(userId);
return Result.ok(userInfoVo);
}
/**
* 获取每一页声音列表提交声音ID集合对应购买状态
*
* @param userId 用户ID
* @param albumId 专辑ID
* @param needCheckPayStateTrackIdList 需要检查购买状态的声音ID集合
* @return {声音ID:1(已购),声音ID:0(未购)}
*/
@GuiGuLogin //对内微服务访问接口上游系统调用是否只要携带token 下游系统就可以获取用户ID
@Operation(summary = "获取每一页声音列表提交声音ID集合对应购买状态")
@PostMapping("/userInfo/userIsPaidTrack/{albumId}")
public Result<Map<Long, Integer>> userIsPaidTrack(
@PathVariable("albumId") Long albumId,
@RequestBody List<Long> needCheckPayStateTrackIdList
) {
Long userId = AuthContextHolder.getUserId();
Map<Long, Integer> map = userInfoService.userIsPaidTrack(userId, albumId, needCheckPayStateTrackIdList);
return Result.ok(map);
}
}

View File

@ -4,6 +4,7 @@ import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
import java.util.Map;
public interface UserInfoService extends IService<UserInfo> {
@ -13,4 +14,7 @@ public interface UserInfoService extends IService<UserInfo> {
UserInfoVo getUserInfo(Long userId);
void updateUser(UserInfoVo userInfoVo, Long userId);
Map<Long, Integer> userIsPaidTrack(Long userId, Long albumId, List<Long> needCheckPayStateTrackIdList);
}

View File

@ -3,13 +3,18 @@ package com.atguigu.tingshu.user.service.impl;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.model.user.UserPaidAlbum;
import com.atguigu.tingshu.model.user.UserPaidTrack;
import com.atguigu.tingshu.user.mapper.UserInfoMapper;
import com.atguigu.tingshu.user.mapper.UserPaidAlbumMapper;
import com.atguigu.tingshu.user.mapper.UserPaidTrackMapper;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -22,8 +27,10 @@ import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Slf4j
@Service
@ -38,6 +45,10 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> i
private RedisTemplate redisTemplate;
@Autowired
private RabbitService rabbitService;
@Autowired
private UserPaidAlbumMapper userPaidAlbumMapper;
@Autowired
private UserPaidTrackMapper userPaidTrackMapper;
@Override
public Map<String, String> wxLogin(String code) {
@ -101,4 +112,56 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> i
userInfoMapper.updateById(userInfo);
}
@Override
public Map<Long, Integer> userIsPaidTrack(Long userId, Long albumId, List<Long> needCheckPayStateTrackIdList) {
Map<Long, Integer> payStatusMap = new HashMap<>();
//1.根据用户ID+专辑ID查询已购专辑记录如果存在专辑购买记录将提交声音ID购买状态全部设置为1返回
Long count = userPaidAlbumMapper.selectCount(
new LambdaQueryWrapper<UserPaidAlbum>()
.eq(UserPaidAlbum::getUserId, userId)
.eq(UserPaidAlbum::getAlbumId, albumId)
);
if (count > 0) {
for (Long trackId : needCheckPayStateTrackIdList) {
payStatusMap.put(trackId, 1);
}
return payStatusMap;
}
//2.如果未购买专辑根据用户ID+专辑ID查询已购声音记录
List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(
new LambdaQueryWrapper<UserPaidTrack>()
.eq(UserPaidTrack::getUserId, userId)
.eq(UserPaidTrack::getAlbumId, albumId)
.select(UserPaidTrack::getTrackId)
);
//2.1 如果不存在购买声音记录将提交声音ID购买状态全部设置为0返回
if (CollUtil.isEmpty(userPaidTrackList)) {
for (Long trackID : needCheckPayStateTrackIdList) {
payStatusMap.put(trackID, 0);
}
return payStatusMap;
}
//2.2 如果存在购买声音记录将提交声音ID已购设置1未购买设置为0返回
//2.2.1 获取已购声音ID集合
List<Long> userPaidTrackIdList =
userPaidTrackList.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
//2.2.2 判断哪些是已购设置为1哪些是未购买设置为0
for (Long trackId : needCheckPayStateTrackIdList) {
if(userPaidTrackIdList.contains(trackId)){
//已购买
payStatusMap.put(trackId, 1);
}else{
//未购买
payStatusMap.put(trackId, 0);
}
}
return payStatusMap;
}
}