项目的架构
网关:1路由转发 2.认证鉴权(token)3.统一处理(跨域)
Mysql:关系型数据库
ES:搜索数据库
Redis:页面级缓存,会话状态存储
GitLab:私有托管平台
K8S:自动化部署、扩展和管理容器化应用程序的开源系统
Jenkins:自动化部署
1.环境搭建
创建一个父工程:依赖管理 版本锁定
子工程:具体业务实现
Controller编写规则:
1.映射路径
2.请求方式
3.请求参数
4.响应结果-返回值
2.专辑模块
2.1专辑管理添加
1.统一返回结果
package com.atguigu.tingshu.common.result;
import lombok.Data;
/**
* 全局统一返回结果类
*
*/
@Data
public class Result<T> {
//返回码
private Integer code;
//返回消息
private String message;
//返回数据
private T data;
public Result(){}
// 返回数据
protected static <T> Result<T> build(T data) {
Result<T> result = new Result<T>();
if (data != null)
result.setData(data);
return result;
}
public static <T> Result<T> build(T body, Integer code, String message) {
Result<T> result = build(body);
result.setCode(code);
result.setMessage(message);
return result;
}
public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {
Result<T> result = build(body);
result.setCode(resultCodeEnum.getCode());
result.setMessage(resultCodeEnum.getMessage());
return result;
}
public static<T> Result<T> ok(){
return Result.ok(null);
}
/**
* 操作成功
* @param data baseCategory1List
* @param <T>
* @return
*/
public static<T> Result<T> ok(T data){
Result<T> result = build(data);
return build(data, ResultCodeEnum.SUCCESS);
}
public static<T> Result<T> fail(){
return Result.fail(null);
}
/**
* 操作失败
* @param data
* @param <T>
* @return
*/
public static<T> Result<T> fail(T data){
Result<T> result = build(data);
return build(data, ResultCodeEnum.FAIL);
}
public Result<T> message(String msg){
this.setMessage(msg);
return this;
}
public Result<T> code(Integer code){
this.setCode(code);
return this;
}
}
2. 统一结果状态信息类
package com.atguigu.tingshu.common.result;
import lombok.Getter;
/**
* 统一返回结果状态信息类
*
*/
@Getter
public enum ResultCodeEnum {
SUCCESS(200,"成功"),
FAIL(201, "失败"),
SERVICE_ERROR(2012, "服务异常"),
DATA_ERROR(204, "数据异常"),
ILLEGAL_REQUEST(205, "非法请求"),
REPEAT_SUBMIT(206, "重复提交"),
ARGUMENT_VALID_ERROR(210, "参数校验异常"),
SIGN_ERROR(300, "签名错误"),
SIGN_OVERDUE(301, "签名已过期"),
LOGIN_AUTH(208, "未登陆"),
PERMISSION(209, "没有权限"),
ACCOUNT_ERROR(214, "账号不正确"),
PASSWORD_ERROR(215, "密码不正确"),
PHONE_CODE_ERROR(215, "手机验证码不正确"),
LOGIN_MOBLE_ERROR( 216, "账号不正确"),
ACCOUNT_STOP( 216, "账号已停用"),
NODE_ERROR( 217, "该节点下有子节点,不可以删除"),
VOD_FILE_ID_ERROR( 220, "声音媒体id不正确"),
XXL_JOB_ERROR(210, "调度操作失败"),
ACCOUNT_LESS(220, "账户余额不足"),
ACCOUNT_LOCK_ERROR(221, "账户余额锁定失败"),
ACCOUNT_UNLOCK_ERROR(221, "账户余额解锁失败"),
ACCOUNT_MINUSLOCK_ERROR(221, "账户余额扣减失败"),
ACCOUNT_LOCK_REPEAT(221, "重复锁定"),
ACCOUNT_LOCK_RESULT_NULL(221, "锁定账号结果对象为空"),
ORDER_SUBMIT_REPEAT(221, "超时或重复提交订单"),
NO_BUY_NOT_SEE(230, "未购买不能观看"),
EXIST_NO_EXPIRE_LIVE(230, "当前存在未过期直播"),
REPEAT_BUY_ERROR(231, "已经购买过该专辑"),
;
private Integer code;
private String message;
private ResultCodeEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
3.全局异常处理
package com.atguigu.tingshu.common.handler;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 全局异常处理类
*
*/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public Result error(Exception e){
e.printStackTrace();
return Result.fail();
}
/**
* 自定义异常处理方法
* @param e
* @return
*/
@ExceptionHandler(GuiguException.class)
@ResponseBody
public Result error(GuiguException e){
return Result.build(null,e.getCode(), e.getMessage());
}
@ExceptionHandler({IllegalArgumentException.class})
@ResponseBody
public Result llegalArgumentException(Exception e) {
log.error("触发异常拦截: " + e.getMessage(), e);
return Result.build(null, ResultCodeEnum.ARGUMENT_VALID_ERROR);
}
@ExceptionHandler(value = BindException.class)
@ResponseBody
public Result error(BindException exception) {
BindingResult result = exception.getBindingResult();
Map<String, Object> errorMap = new HashMap<>();
List<FieldError> fieldErrors = result.getFieldErrors();
fieldErrors.forEach(error -> {
log.error("field: " + error.getField() + ", msg:" + error.getDefaultMessage());
errorMap.put(error.getField(), error.getDefaultMessage());
});
return Result.build(errorMap, ResultCodeEnum.ARGUMENT_VALID_ERROR);
}
@ExceptionHandler(value = MethodArgumentNotValidException.class)
@ResponseBody
public Result error(MethodArgumentNotValidException exception) {
BindingResult result = exception.getBindingResult();
Map<String, Object> errorMap = new HashMap<>();
List<FieldError> fieldErrors = result.getFieldErrors();
fieldErrors.forEach(error -> {
log.error("field: " + error.getField() + ", msg:" + error.getDefaultMessage());
errorMap.put(error.getField(), error.getDefaultMessage());
});
return Result.build(errorMap, ResultCodeEnum.ARGUMENT_VALID_ERROR);
}
}
4.自定义全局异常
package com.atguigu.tingshu.common.execption;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import lombok.Data;
/**
* 自定义全局异常类
*
*/
@Data
public class GuiguException extends RuntimeException {
private Integer code;
private String message;
/**
* 通过状态码和错误消息创建异常对象
* @param code
* @param message
*/
public GuiguException(Integer code, String message) {
super(message);
this.code = code;
this.message = message;
}
/**
* 接收枚举类型对象
* @param resultCodeEnum
*/
public GuiguException(ResultCodeEnum resultCodeEnum) {
super(resultCodeEnum.getMessage());
this.code = resultCodeEnum.getCode();
this.message = resultCodeEnum.getMessage();
}
@Override
public String toString() {
return "GuliException{" +
"code=" + code +
", message=" + this.getMessage() +
'}';
}
}
2.1.1 专辑分类列表(三级分类)
1.controller
package com.atguigu.tingshu.album.api;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.album.service.BaseCategoryService;
import com.atguigu.tingshu.common.result.Result;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@Tag(name = "分类管理")
@RestController
@RequestMapping(value = "/api/album")
@SuppressWarnings({"all"})
public class BaseCategoryApiController {
@Autowired
private BaseCategoryService baseCategoryService;
/**
* @description:
* @author: yanhongwei
* @date:
* @param: * @param null
* @return:
**/
/// api/album/category/getBaseCategoryList
@GetMapping(value = "/category/getBaseCategoryList")
public Result<List<JSONObject>> getBaseCategoryList() {
// public Result<List<Map<String, Object>>> getBaseCategoryList()
// JSONObject jsonObject = new JSONObject();
List<JSONObject> categoryList = baseCategoryService.getBaseCategoryList();
return Result.ok(categoryList);
}
}
2.impl
package com.atguigu.tingshu.album.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.album.mapper.BaseCategory1Mapper;
import com.atguigu.tingshu.album.mapper.BaseCategory2Mapper;
import com.atguigu.tingshu.album.mapper.BaseCategory3Mapper;
import com.atguigu.tingshu.album.mapper.BaseCategoryViewMapper;
import com.atguigu.tingshu.album.service.BaseCategoryService;
import com.atguigu.tingshu.model.album.BaseCategory1;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.http.client.utils.CloneUtils;
import org.apache.kafka.common.utils.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
@SuppressWarnings({"all"})
public class BaseCategoryServiceImpl extends ServiceImpl<BaseCategory1Mapper, BaseCategory1> implements BaseCategoryService {
@Autowired
private BaseCategoryViewMapper baseCategoryViewMapper;
@Override
public List<JSONObject> getBaseCategoryList() {
//创建一级集合,收集数据
List<JSONObject> allList = new ArrayList<>();
//查询所有分类
List<BaseCategoryView> baseCategoryViewsList = baseCategoryViewMapper.selectList(null);
//封装
if (CollectionUtil.isNotEmpty(baseCategoryViewsList)) {
//分组一级分类
Map<Long, List<BaseCategoryView>> collect1Map = baseCategoryViewsList.stream().collect(Collectors.groupingBy(BaseCategoryView::getCategory1Id));
for (Map.Entry<Long, List<BaseCategoryView>> Entry1 : collect1Map.entrySet()) {
//创建一级分类封装对象
JSONObject obj1 = new JSONObject();
//获取1级id 跟name
Long category1Id = Entry1.getKey();
List<BaseCategoryView> category2List = Entry1.getValue();
String category1Name = category2List.get(0).getCategory1Name();
obj1.put("categoryId", category1Id);
obj1.put("categoryName", category1Name);
//创建二级分类收集集合
List<JSONObject> array2 = new ArrayList<>();
//分组二级分类
Map<Long, List<BaseCategoryView>> collect2Map = category2List.stream().collect(Collectors.groupingBy(BaseCategoryView::getCategory2Id));
for (Map.Entry<Long, List<BaseCategoryView>> Entry2 : collect2Map.entrySet()) {
//创建二级分类封装对象
JSONObject obj2 = new JSONObject();
//获取2级分类id name
Long category2Id = Entry2.getKey();
List<BaseCategoryView> category3List = Entry2.getValue();
String category2Name = category3List.get(0).getCategory2Name();
obj2.put("categoryId", category2Id);
obj2.put("categoryName", category2Name);
List<JSONObject> array3 = category3List.stream().map(baseCategoryView -> {
JSONObject obj3 = new JSONObject();
obj3.put("categoryId", baseCategoryView.getCategory3Id());
obj3.put("categoryName", baseCategoryView.getCategory3Name());
return obj3;
}).collect(Collectors.toList());
obj2.put("categoryChild", array3);
//收集二级分类
array2.add(obj2);
}
//封装2级分类
obj1.put("categoryChild", array2);
//收集一级分类对象
allList.add(obj1);
}
System.out.println("collect = ");
}
return allList;
}
}
3.mapper
package com.atguigu.tingshu.album.mapper;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface BaseCategoryViewMapper extends BaseMapper<BaseCategoryView> {
}
4. 视图
创建视图封装一些sql语句,不需要再多表查询了
BaseCategoryView
#创建视图
create view view_category01 as
select c3.id,
c1.id category1_id,
c1.name category1_name,
c2.id category2_id,
c2.name category2_name,
c3.id category3_id,
c3.name category3_name,
c3.is_deleted,
c3.create_time,
c3.update_time
from base_category1 c1
inner join base_category2 c2 on c1.id = c2.category1_id
inner join base_category3 c3 on c2.id = c3.category2_id
//
//
package com.atguigu.tingshu.model.album;
import com.atguigu.tingshu.model.base.BaseEntity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* <p>
* BaseCategoryView
* </p>
*
* @author atguigu
*/
@Data
@Schema(description = "分类视图")
@TableName("base_category_view")
public class BaseCategoryView extends BaseEntity {
private static final long serialVersionUID = 1L;
@Schema(description = "一级分类编号")
@TableField("category1_id")
private Long category1Id;
@Schema(description = "一级分类名称")
@TableField("category1_name")
private String category1Name;
@Schema(description = "二级分类编号")
@TableField("category2_id")
private Long category2Id;
@Schema(description = "二级分类名称")
@TableField("category2_name")
private String category2Name;
@Schema(description = "三级分类编号")
@TableField("category3_id")
private Long category3Id;
@Schema(description = "三级分类名称")
@TableField("category3_name")
private String category3Name;
}

2.1.2 查询一级分类下面的标签
1.controller
@GetMapping(value = "category/findAttribute/{category1Id}")
public Result<List<BaseAttribute>> findAttribute(@PathVariable Long category1Id) {
List<BaseAttribute> attributeList = baseCategoryService.findAttribute(category1Id);
return Result.ok(attributeList);
}
2.impl
package com.atguigu.tingshu.album.service.impl;
import cn.hutool.core.collection.CollectionUtil;
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.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.http.client.utils.CloneUtils;
import org.apache.kafka.common.utils.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
@SuppressWarnings({"all"})
public class BaseCategoryServiceImpl extends ServiceImpl<BaseCategory1Mapper, BaseCategory1> implements BaseCategoryService {
@Autowired
private BaseAttributeMapper baseAttributeMapper;
@Override
public List<BaseAttribute> findAttribute(Long category1Id) {
return baseAttributeMapper.findAttribute(category1Id);
}
}
3.Mapper
package com.atguigu.tingshu.album.mapper;
import com.atguigu.tingshu.model.album.BaseAttribute;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface BaseAttributeMapper extends BaseMapper<BaseAttribute> {
List<BaseAttribute> findAttribute(Long category1Id);
}
4.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.atguigu.tingshu.album.mapper.BaseAttributeMapper">
<resultMap id="baseAttributeMap" type="com.atguigu.tingshu.model.album.BaseAttribute" autoMapping="true">
<id column="id" property="id"/>
<collection property="attributeValueList" ofType="com.atguigu.tingshu.model.album.BaseAttributeValue" autoMapping="true">
<id column="base_attribute_value_id" property="id"/>
</collection>
</resultMap>
<select id="findAttribute" resultMap="baseAttributeMap">
select
ba.id,
ba.category1_id,
ba.attribute_name,
bav.id base_attribute_value_id,
bav.attribute_id,
bav.value_name
from base_attribute ba left join base_attribute_value bav on bav.attribute_id = ba.id
where ba.category1_id = #{category1Id} and ba.is_deleted = 0
</select>
</mapper>
2.1.3 上传图片
1.controller
package com.atguigu.tingshu.album.api;
import com.atguigu.tingshu.album.service.FileUploadService;
import com.atguigu.tingshu.common.result.Result;
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.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@Tag(name = "上传管理接口")
@RestController
@RequestMapping("api/album")
public class FileUploadApiController {
@Autowired
private FileUploadService fileUploadService;
/**
* 将文件上传到分布式存储服务MinIO中
*
* @param file
* @return
*/
@Operation(summary = "将文件上传到分布式存储服务MinIO中")
@PostMapping("/fileUpload")
public Result<String> fileUpload(MultipartFile file) {
String fileUrl = fileUploadService.imageUpload(file);
return Result.ok(fileUrl);
}
}
2.impl
package com.atguigu.tingshu.album.service.impl;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.album.config.MinioConstantProperties;
import com.atguigu.tingshu.album.service.FileUploadService;
import com.atguigu.tingshu.common.execption.GuiguException;
import io.minio.*;
import io.minio.errors.MinioException;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
/**
* @version: java version 1.8
* @Author: Mr Orange
* @description:
* @date: 2025-02-16 16:17
*/
@Service
public class FileUploadServiceImpl implements FileUploadService {
@Autowired
private MinioClient minioClient;
@Autowired
private MinioConstantProperties minioConstantProperties;
@Override
@SneakyThrows
public String imageUpload(MultipartFile file) {
//校验是否为图片类型
//1.业务校验验证文件是否为图片,借助ImageIO读取图片。继续判断图片文件后缀名是否符合要求,图片长宽
BufferedImage read = ImageIO.read(file.getInputStream());
if (read == null) {
throw new GuiguException(400, "图片格式非法!");
}
// 判断桶是否存在
boolean found =
minioClient.bucketExists(BucketExistsArgs.builder().bucket(minioConstantProperties.getBucketName()).build());
if (!found) {
// Make a new bucket called 'asiatrip'.
minioClient.makeBucket(MakeBucketArgs.builder().bucket(minioConstantProperties.getBucketName()).build());
} else {
System.out.println("Bucket " + minioConstantProperties.getBucketName() + " already exists.");
}
//随机生成文件名称
String folderName = "/" + DateUtil.today() + "/";
//文件名
String fileName = IdUtil.randomUUID();
//获取后缀名
String extName = FileUtil.extName(file.getOriginalFilename()); //
//文件上传的路径
String objectName = folderName + fileName + "." + extName;
//2.文件上传
minioClient.putObject(
PutObjectArgs.builder().bucket(minioConstantProperties.getBucketName()).object(objectName).stream(
file.getInputStream(), file.getSize(), -1)
.contentType(file.getContentType())
.build());
return minioConstantProperties.getEndpointUrl()+"/"+minioConstantProperties.getBucketName()+objectName;
}
}
3.Nacos配置文件
4.MinioClientConfig 配置类
package com.atguigu.tingshu.album.config;
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @version: java version 1.8
* @Author: Mr Orange
* @description:
* @date: 2025-02-16 16:22
*/
@Configuration
public class MinioClientConfig {
@Autowired
private MinioConstantProperties minioConstantProperties;
@Bean
public MinioClient minioClient() {
return MinioClient.builder().
endpoint(minioConstantProperties.getEndpointUrl()).
credentials(minioConstantProperties.getAccessKey(), minioConstantProperties.getSecreKey()).
build();
}
}
5.MinioConstantProperties
package com.atguigu.tingshu.album.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix="minio") //读取节点
@Data
public class MinioConstantProperties {
private String endpointUrl;
private String accessKey;
private String secreKey;
private String bucketName;
}
2.1.4 添加专辑
1.controller
package com.atguigu.tingshu.album.api;
import com.atguigu.tingshu.album.service.AlbumInfoService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.vo.album.AlbumInfoVo;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "专辑管理")
@RestController
@RequestMapping("api/album")
@SuppressWarnings({"all"})
public class AlbumInfoApiController {
@Autowired
private AlbumInfoService albumInfoService;
// /api/album/albumInfo/saveAlbumInfo
@PostMapping("albumInfo/saveAlbumInfo")
public Result saveAlbumInfo(@RequestBody AlbumInfoVo albumInfoVo){
//获取用户id
Long userId = AuthContextHolder.getUserId();
albumInfoService.saveAlbumInfo(albumInfoVo,userId);
return Result.ok();
}
}
2.impl
将前端传递的参数分别添加到对应的3表当中
package com.atguigu.tingshu.album.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.atguigu.tingshu.album.mapper.AlbumAttributeValueMapper;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.service.AlbumInfoService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.vo.album.AlbumInfoVo;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Slf4j
@Service
@SuppressWarnings({"all"})
public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo> implements AlbumInfoService {
@Autowired
private AlbumInfoMapper albumInfoMapper;
@Autowired
private AlbumAttributeValueMapper albumAttributeValueMapper;
@Autowired
private AlbumStatMapper albumStatMapper;
@Transactional(rollbackFor = Exception.class)
@Override
public void saveAlbumInfo(AlbumInfoVo albumInfoVo,Long userId) {
//拷贝数据创建保存对象
AlbumInfo albumInfo = BeanUtil.copyProperties(albumInfoVo, AlbumInfo.class);
//设置用户ID
albumInfo.setUserId(userId);
//设置免费试听集数
albumInfo.setTracksForFree(5);
//设置状态 是否审核通过
albumInfo.setStatus(SystemConstant.ALBUM_STATUS_PASS);
//保存专辑信息
albumInfoMapper.insert(albumInfo);
//保存专辑属性
List<AlbumAttributeValue> albumAttributeValueVoList = albumInfo.getAlbumAttributeValueVoList();
//判断
if(CollectionUtil.isEmpty(albumAttributeValueVoList)){
throw new GuiguException(400,"专辑信息不完整");
}
for (AlbumAttributeValue albumAttributeValue : albumAttributeValueVoList) {
albumAttributeValue.setAlbumId(albumInfo.getId());
albumAttributeValueMapper.insert(albumAttributeValue);
}
//保存专辑统计信息
this.saveAlbumStat(albumInfo.getId(),SystemConstant.ALBUM_STAT_PLAY,0);
this.saveAlbumStat(albumInfo.getId(),SystemConstant.ALBUM_STAT_SUBSCRIBE,0);
this.saveAlbumStat(albumInfo.getId(),SystemConstant.ALBUM_STAT_BUY,0);
this.saveAlbumStat(albumInfo.getId(),SystemConstant.ALBUM_STAT_COMMENT,0);
}
private void saveAlbumStat(Long id, String albumStatPlay, int i) {
AlbumStat albumStat = new AlbumStat();
albumStat.setAlbumId(id);
albumStat.setStatType(albumStatPlay);
albumStat.setStatNum(i);
albumStatMapper.insert(albumStat);
}
}
2.2查看专辑列表
将专辑表跟统计表联合查询得到
select
ai.id albumId,
ai.album_title,
ai.cover_url,
ai.include_track_count,
ai.is_finished,
ai.status,
ast.stat_type,
ast.stat_num
from album_info ai
inner join album_stat as ast
on ai.id = ast.album_id
where ai.user_id = ?
and ai.status = ?
and ai.album_title like ?
and ai.is_deleted = 0
order by ai.id desc
不是前端需要的数据行转列
select
ai.id albumId,
ai.album_title,
ai.cover_url,
ai.include_track_count,
ai.is_finished,
ai.status,
# ast.stat_type,
# ast.stat_num,
max(if(ast.stat_type='0401',ast.stat_num,0)) playStatNum,
max(if(ast.stat_type='0402',ast.stat_num,0)) subscribeStatNum,
max(if(ast.stat_type='0403',ast.stat_num,0)) buyStatNum,
max(if(ast.stat_type='0404',ast.stat_num,0)) albumCommentStaNum
from album_info ai
inner join album_stat as ast
on ai.id = ast.album_id
where ai.user_id = ?
and ai.status = ?
and ai.album_title like ?
and ai.is_deleted = 0
group by ai.id
order by ai.id desc;
2.2.1controller
public class AlbumInfoApiController {
@Autowired
private AlbumInfoService albumInfoService;
// /api/album/albumInfo/findUserAlbumPage/{page}/{limit}
@PostMapping("albumInfo/findUserAlbumPage/{page}/{limit}")
public Result<Page<AlbumListVo>> findUserAlbumPage(@PathVariable Long page,
@PathVariable Long limit,
@RequestBody AlbumInfoQuery albumInfoQuery) {
//封装Page对象
Page<AlbumListVo> albumListVoPage = new Page<>(page, limit);
//获取用户Id
Long userId = AuthContextHolder.getUserId();
//封装查询对象条件
albumInfoQuery.setUserId(userId);
//调用service查询
albumListVoPage = albumInfoService.findUserAlbumPage(albumListVoPage, albumInfoQuery);
return Result.ok(albumListVoPage);
}
2.impl
public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo> implements AlbumInfoService {
@Autowired
private AlbumInfoMapper albumInfoMapper;
/*
* @description: 根据用户ID查询专辑列表
* @author: yanhongwei
* @date:
* @param: * @param null
* @return:
**/
@Override
public Page<AlbumListVo> findUserAlbumPage(Page<AlbumListVo> albumListVoPage, AlbumInfoQuery albumInfoQuery) {
return albumInfoMapper.selectUserAlbumPage(albumListVoPage,albumInfoQuery);
}}
3.Mapper
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.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface AlbumInfoMapper extends BaseMapper<AlbumInfo> {
Page<AlbumListVo> selectUserAlbumPage(@Param("albumListVoPage") Page<AlbumListVo> albumListVoPage, @Param("vo") AlbumInfoQuery albumInfoQuery);
}
4.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.atguigu.tingshu.album.mapper.AlbumInfoMapper">
<select id="selectUserAlbumPage" resultType="com.atguigu.tingshu.vo.album.AlbumListVo">
select
ai.id albumId,
ai.album_title,
ai.cover_url,
ai.include_track_count,
ai.is_finished,
ai.status,
max(if(ast.stat_type='0401',ast.stat_num,0)) playStatNum,
max(if(ast.stat_type='0402',ast.stat_num,0)) subscribeStatNum,
max(if(ast.stat_type='0403',ast.stat_num,0)) buyStatNum,
max(if(ast.stat_type='0404',ast.stat_num,0)) commentStatNum
from album_info ai
inner join album_stat as ast
on ai.id = ast.album_id
<where>
ai.user_id = #{vo.userId}
<if test="vo.status!= null and vo.status!='' ">
and ai.status = #{vo.status}
</if>
<if test="vo.albumTitle!= null and vo.albumTitle!='' ">
and ai.album_title like concat('%',#{vo.albumTitle},'%')
</if>
and ai.is_deleted = 0
</where>
group by ai.id
order by ai.id desc
</select>
</mapper>
实体类AlbumInfoQuery 前端的请求body
package com.atguigu.tingshu.query.album;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "专辑信息")
public class AlbumInfoQuery {
@Schema(description = "标题")
private String albumTitle;
@Schema(description = "状态")
private String status;
@Schema(description = "用户id")
private Long userId;
}
后端返回的类
package com.atguigu.tingshu.vo.album;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "用户专辑列表信息")
public class AlbumListVo {
@Schema(description = "专辑id")
private Long albumId;
@Schema(description = "标题")
private String albumTitle;
@Schema(description = "专辑封面原图,尺寸不固定,最大尺寸为960*960(像素)")
private String coverUrl;
@Schema(description = "专辑包含声音总数")
private Integer includeTrackCount;
@Schema(description = "专辑是否完结:0-否;1-完结;")
private String isFinished;
@Schema(description = "状态")
private String status;
@Schema(description = "播放量")
private Integer playStatNum;
@Schema(description = "订阅量")
private Integer subscribeStatNum;
@Schema(description = "购买量")
private Integer buyStatNum;
@Schema(description = "评论数")
private Integer commentStatNum;
}
2.3删除专辑
1.controller
public class AlbumInfoApiController {
@Autowired
private AlbumInfoService albumInfoService;
/*
* @description:删除专辑
* @author: yanhongwei
* @date:
* @param: