您的位置:首页 > 文旅 > 美景 > 网络赚钱平台_威海互联网公司_搜索引擎优化的内容包括_网店推广营销方案

网络赚钱平台_威海互联网公司_搜索引擎优化的内容包括_网店推广营销方案

2025/11/5 18:43:26 来源:https://blog.csdn.net/m0_63497607/article/details/147066706  浏览:    关键词:网络赚钱平台_威海互联网公司_搜索引擎优化的内容包括_网店推广营销方案
网络赚钱平台_威海互联网公司_搜索引擎优化的内容包括_网店推广营销方案

文章目录

  • 图片模块
    • 一、需求分析
    • 二、方案设计
      • 文件上传下载的实现
        • 对象存储
      • 创建图片的流程
      • 解析图片信息
    • 三、后端开发
      • COS存储桶准备
      • 通用类 manager - CosManager
      • 文件上传
      • 文件上传测试
      • 文件下载
      • 服务端文件下载代码demo
      • picture table 逆向工程
      • 图片上传
        • 1.数据模型
        • 2.通用文件上传服务
        • 3.图片上传服务
          • 接口
          • 实现类
        • 4.Controller
        • 5.测试
        • 扩展
      • 图片管理
        • 1.数据模型
        • 2.服务开发
        • 3.Controller
    • 四、模块开发细节

  • 上传创建图片

  • 图片信息编辑(标签/分类等)

  • 管理图片

  • 查看 / 搜索 图片列表

  • 查看图片详情

  • 图片下载

  • 用户上传创建图片

  • 审核图片

  • 导入图片(URL导入,批量抓取和创建)

  • 图片查询优化

  • 图片上传优化

  • 图片加载优化

  • 图片存储

图片模块

一、需求分析

优先确保用户能够查看图片功能的实现,上传功能暂时仅限管理员使用,保证系统的安全和稳定

管理员

  • 图片上传与创建,支持本地图片上传,填写相关信息,名称、简介、标签、分类等。系统自动解析图片的基础信息,便于检索。

  • 图片管理,管理员对图片信息进行编辑,例如修改名称、简洁、标签、分类等。

  • 图片修改

用户

  • 查看与搜索:用户可以按照关键词搜索图片,并支持按分类、标签筛选条件分页查看图片列表。
  • 查看图片详情:用户点击列表中的图片后,可进入详情页,查看图片的大图及相关信息,如名称、简介、分类、标签、其他图片信息(如宽高和格式等)。
  • 图片下载:用户在详情页可以点击 下载图片按钮,将图片保存本地。

二、方案设计

文件上传下载的实现

最简单的方式就是上传到后端服务器,用Java自带文件读写API就能实现。但是这种方式存在缺点:

  • 不利于扩展:单个服务器的存储是有限的
  • 不利于迁移
  • 不安全
  • 不利于管理

除了存储一些需要清理的临时文件,通常不会将用户的文件直接上传到服务器,更推荐用专业的第三方存储服务,最常用的就是对象存储。

对象存储

对象存储是一种存储海量文件的分布式存储服务,具有高扩展、低成本、可靠安全等优点。

例如Minio,AmazonS3,OSS,COS

创建图片的流程

上传图片文件+补充图片信息并保存到数据库中

  • 先上传再提交数据:用户上传图片,系统生成图片存储的url;然后在用户填写其他相关信息并提交后,才保存图片记录到数据库中。
  • 上传图片时保存记录:用户上传图片后,系统立即生成图片的完整数据记录,包括url和其他元数据,无需等待用户点击提交,图片信息立刻存储数据库,之后用户填写其他信息,相当于编辑了已有图片记录的信息。

第一种方法,如果用户取消提交会有存储残留。

第二种方法比较稳健,可以对图片溯源,并对用户上传做安全保障

解析图片信息

主流获取图片信息的方法主要由2种:

1.在后端服务器直接处理图片,比如Java库ImageIO、Python库pillow,还有更成熟的专业图像处理库OpenCV等。

2.通过第三方服务提取图片元数据。这里我们用腾讯的数据万象

三、后端开发

COS存储桶准备

配置

@Configuration
@ConfigurationProperties(prefix = "cos.client")
@Data
public class CosClientConfig {  /**  * 域名  */  private String host;  /**  * secretId  */  private String secretId;  /**  * 密钥(注意不要泄露)  */  private String secretKey;  /**  * 区域  */  private String region;  /**  * 桶名  */  private String bucket;@Beanpublic COSClient cosClient() {// 1 初始化用户身份信息(secretId, secretKey)。// SECRETID 和 SECRETKEY 请登录访问管理控制台 https://console.cloud.tencent.com/cam/capi 进行查看和管理COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);// 2 设置 bucket 的地域, COS 地域的简称请参见 https://cloud.tencent.com/document/product/436/6224// clientConfig 中包含了设置 region, https(默认 http), 超时, 代理等 set 方法, 使用可参见源码或者常见问题 Java SDK 部分。ClientConfig clientConfig = new ClientConfig(new Region(region));// 这里建议设置使用 https 协议// 从 5.6.54 版本开始,默认使用了 httpsclientConfig.setHttpProtocol(HttpProtocol.https);// 3 生成 cos 客户端。return new COSClient(cred, clientConfig);}
}
# 对象存储配置(需要从腾讯云获取)  
cos:  client:  host: xxx  secretId: xxx  secretKey: xxx  region: xxx  bucket: xxx

通用类 manager - CosManager

@Component  
public class CosManager {  @Resource  private CosClientConfig cosClientConfig;  @Resource  private COSClient cosClient;  // ... 一些操作 COS 的方法  
}

文件上传

public PutObjectResult putObject(String key,File file){PutObjectResult putObjectRequest = new PutObjectRequest(cosClientConfig.getBucket(),key,file);return cosClient.putObject(putObjectRequest);
}
/**  * 上传对象(附带图片信息)  *  * @param key  唯一键  * @param file 文件  */  
public PutObjectResult putPictureObject(String key, File file) {  PutObjectRequest putObjectRequest = new PutObjectRequest(cosClientConfig.getBucket(), key,  file);  // 对图片进行处理(获取基本信息也被视作为一种处理)  PicOperations picOperations = new PicOperations();  // 1 表示返回原图信息  picOperations.setIsPicInfo(1);  // 构造处理参数  putObjectRequest.setPicOperations(picOperations);  return cosClient.putObject(putObjectRequest);  
}

文件上传测试


@PostMapping("/test/upload")
public BaseResponse<String> testUploadFile(@RequestPart("file")MutipartFile multipartFile){//dirString filename = multipart.getOriginalFilename();String filepath = String.format("/test/%s",filename);File file = null;try{file = File.createTempFile(filepath,null);mutipartFile.transferTo(file);cosManager.putObject(filepath,file);return ResultUtils.success(filepath);}catch(Exception e){throw}finally{if(file!=null){file.delete()}}}

文件下载

COS介绍了2种文件下载方式。一种是直接下载COS文件到后端服务器(适合服务端处理文件),另一种是获取文件下载输入流(适合返回给前端用户)。

还可以通过URL路径访问,适用于单一的、可公开的资源,如用户头像,公开图片。

对于安全性要求高的场景,可以先通过后端进行权限校验,然后从COS下载文件到服务器,再返回给前端,这样可以在后端限制只有登录用户才能下载。

也可以后端先进行权限校验,然后返回给前端一个临时密钥,之后前端可以凭借该密钥直接从对象存储下载,不用经过服务端中转,性能更高。

当前的图片都是公开的,直接通过URL进行访问即可。(这里的负载就交给COS了)

服务端文件下载代码demo

/**  * 下载对象  *  * @param key 唯一键  */  
public COSObject getObject(String key) {  GetObjectRequest getObjectRequest = new GetObjectRequest(cosClientConfig.getBucket(), key);  return cosClient.getObject(getObjectRequest);  
}
@GetMapping("/test/download")
public void testDownloadFile(String filepath,HttpServletResponse response)
{COSObjectInputStream cosObjectInput = null;try{cosManager.getObject(filepath).var;cosObjectInput =   cosObject.getObjectContent();IOUtils.toByteArray(cosObjectInput).var;response.setContentType();response.setHeader();response.getOutPutStream().write(bytes);response.getOutPutStream().flush();}catch{}finally{if(cosObjectInput!=null){cosObjectInput.close();}}
}

picture table 逆向工程

优化picture 实体类

@TableName(value ="picture")  
@Data  
public class Picture implements Serializable {  /**  * id  */  @TableId(type = IdType.ASSIGN_ID)  private Long id;  /**  * 图片 url  */  private String url;  /**  * 图片名称  */  private String name;  /**  * 简介  */  private String introduction;  /**  * 分类  */  private String category;  /**  * 标签(JSON 数组)  */  private String tags;  /**  * 图片体积  */  private Long picSize;  /**  * 图片宽度  */  private Integer picWidth;  /**  * 图片高度  */  private Integer picHeight;  /**  * 图片宽高比例  */  private Double picScale;  /**  * 图片格式  */  private String picFormat;  /**  * 创建用户 id  */  private Long userId;  /**  * 创建时间  */  private Date createTime;  /**  * 编辑时间  */  private Date editTime;  /**  * 更新时间  */  private Date updateTime;  /**  * 是否删除  */  @TableLogic  private Integer isDelete;  @TableField(exist = false)  private static final long serialVersionUID = 1L;  
}

图片上传

1.数据模型

在dto下新建用于请求参数的类。由于图片需要支持重复上传(信息不变,只改变图片文件),所以要添加图片id参数:

@Data  
public class PictureUploadRequest implements Serializable {  /**  * 图片 id(用于修改)  */  private Long id;  private static final long serialVersionUID = 1L;  
}

在vo下新建视图类,可以额外关联用户信息。还可以编写实体类和视图类快速转换方法,便于后续快速传值。

@Data  
public class PictureVO implements Serializable {  /**  * id  */  private Long id;  /**  * 图片 url  */  private String url;  /**  * 图片名称  */  private String name;  /**  * 简介  */  private String introduction;  /**  * 标签  */  private List<String> tags;  /**  * 分类  */  private String category;  /**  * 文件体积  */  private Long picSize;  /**  * 图片宽度  */  private Integer picWidth;  /**  * 图片高度  */  private Integer picHeight;  /**  * 图片比例  */  private Double picScale;  /**  * 图片格式  */  private String picFormat;  /**  * 用户 id  */  private Long userId;  /**  * 创建时间  */  private Date createTime;  /**  * 编辑时间  */  private Date editTime;  /**  * 更新时间  */  private Date updateTime;  /**  * 创建用户信息  */  private UserVO user;  private static final long serialVersionUID = 1L;  /**  * 封装类转对象  */  public static Picture voToObj(PictureVO pictureVO) {  if (pictureVO == null) {  return null;  }  Picture picture = new Picture();  BeanUtils.copyProperties(pictureVO, picture);  // 类型不同,需要转换  picture.setTags(JSONUtil.toJsonStr(pictureVO.getTags()));  return picture;  }  /**  * 对象转封装类  */  public static PictureVO objToVo(Picture picture) {  if (picture == null) {  return null;  }  PictureVO pictureVO = new PictureVO();  BeanUtils.copyProperties(picture, pictureVO);  // 类型不同,需要转换  pictureVO.setTags(JSONUtil.toList(picture.getTags(), String.class));  return pictureVO;  }  
}
2.通用文件上传服务

虽然以及编写过通用对象存储操作类,但是还不够

问题:

  • 图片校验
  • 上传路径指定
  • 解析图片:数据万象服务

FileManager / FileService

1.由于文件校验规则较复杂,单独抽象为validPicture方法,对文件大小、类型校验。

2.文件上传时,会先在本地创建临时文件,无论是否创建成功,最后都要删除临时文件,否则会导致资源泄露。

3.可以跟自己的需求定义文件上传地址,此处添加了上传日期和UUID随机数,便于了解上传时间并防止文件重复(一般处理)。预留了一个uploadPathPrefix参数,由调用方指定上传文件到哪个目录。

4.不建议存储桶共享

public class FileService{@Resourceprivate CosClientConfig cosClientConfig;@Resourceprivate CosManager cosManager;/**  * 上传图片  *  * @param multipartFile    文件  * @param uploadPathPrefix 上传路径前缀  * @return  */  
public UploadPictureResult uploadPicture(MultipartFile multipartFile, String uploadPathPrefix) {  // 校验图片  validPicture(multipartFile);  // 图片上传地址  String uuid = RandomUtil.randomString(16);  String originFilename = multipartFile.getOriginalFilename();  String uploadFilename = String.format("%s_%s.%s", DateUtil.formatDate(new Date()), uuid,  FileUtil.getSuffix(originFilename));  String uploadPath = String.format("/%s/%s", uploadPathPrefix, uploadFilename);  File file = null;  try {  // 创建临时文件  file = File.createTempFile(uploadPath, null);  multipartFile.transferTo(file);  // 上传图片  PutObjectResult putObjectResult = cosManager.putPictureObject(uploadPath, file);  ImageInfo imageInfo = putObjectResult.getCiUploadResult().getOriginalInfo().getImageInfo();  // 封装返回结果  UploadPictureResult uploadPictureResult = new UploadPictureResult();  int picWidth = imageInfo.getWidth();  int picHeight = imageInfo.getHeight();  double picScale = NumberUtil.round(picWidth * 1.0 / picHeight, 2).doubleValue();  uploadPictureResult.setPicName(FileUtil.mainName(originFilename));  uploadPictureResult.setPicWidth(picWidth);  uploadPictureResult.setPicHeight(picHeight);  uploadPictureResult.setPicScale(picScale);  uploadPictureResult.setPicFormat(imageInfo.getFormat());  uploadPictureResult.setPicSize(FileUtil.size(file));  uploadPictureResult.setUrl(cosClientConfig.getHost() + "/" + uploadPath);  return uploadPictureResult;  } catch (Exception e) {  log.error("图片上传到对象存储失败", e);  throw new BusinessException(ErrorCode.SYSTEM_ERROR, "上传失败");  } finally {  this.deleteTempFile(file);  }  
}  /**  * 校验文件  *  * @param multipartFile multipart 文件  */  
public void validPicture(MultipartFile multipartFile) {  ThrowUtils.throwIf(multipartFile == null, ErrorCode.PARAMS_ERROR, "文件不能为空");  // 1. 校验文件大小  long fileSize = multipartFile.getSize();  final long ONE_M = 1024 * 1024L;  ThrowUtils.throwIf(fileSize > 2 * ONE_M, ErrorCode.PARAMS_ERROR, "文件大小不能超过 2M");  // 2. 校验文件后缀  String fileSuffix = FileUtil.getSuffix(multipartFile.getOriginalFilename());  // 允许上传的文件后缀  final List<String> ALLOW_FORMAT_LIST = Arrays.asList("jpeg", "jpg", "png", "webp");  ThrowUtils.throwIf(!ALLOW_FORMAT_LIST.contains(fileSuffix), ErrorCode.PARAMS_ERROR, "文件类型错误");  
}  /**  * 删除临时文件  */  
public void deleteTempFile(File file) {  if (file == null) {  return;  }  // 删除临时文件  boolean deleteResult = file.delete();  if (!deleteResult) {  log.error("file delete error, filepath = {}", file.getAbsolutePath());  }  
}}

图片解析包装类:

@Data  
public class UploadPictureResult {  /**  * 图片地址  */  private String url;  /**  * 图片名称  */  private String picName;  /**  * 文件体积  */  private Long picSize;  /**  * 图片宽度  */  private int picWidth;  /**  * 图片高度  */  private int picHeight;  /**  * 图片宽高比  */  private Double picScale;  /**  * 图片格式  */  private String picFormat;  }
3.图片上传服务
接口
/**  * 上传图片  *  * @param multipartFile  * @param pictureUploadRequest  * @param loginUser  * @return  */  
PictureVO uploadPicture(MultipartFile multipartFile,  PictureUploadRequest pictureUploadRequest,  User loginUser);
实现类

1.我们将所有图片都放到了public 目录下,并且为每个用户的图片存储到public/uid 的目录下

2.如果pid不为空,表示更新已有图片信息,需要判断图片是否存在,并且更新的时候要指定editTime编辑时间。可以调用Mybatis Plus 提供的saveOrUpdate方法兼容创建和更新操作

@Override  
public PictureVO uploadPicture(MultipartFile multipartFile, PictureUploadRequest pictureUploadRequest, User loginUser) {  ThrowUtils.throwIf(loginUser == null, ErrorCode.NO_AUTH_ERROR);  Long pictureId = null;if(pictureUploadRequest!=null){pictureId=pictureUploadRequest.getId();}if(pictureId!=null){pictureMapper.lambdaQuery().eq(Picture::getId,pictureId).exists();Throwif()}String.format("public/%s",loginUser.getId()).var; // mkdir by uidfileManager.uploadPicutre(mutipartFile,uploadPrefix).var;new Picture().var;set{url,name,size,width,height,scale,format,uid}if(picutreId!=null){set{pictureid,edittime}}mapper.saveOrUpdate()ThrowIf()return PictureVO.objToVo(picture)
}
4.Controller
/**  * 上传图片(可重新上传)  */  
@PostMapping("/upload")  
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)  
public BaseResponse<PictureVO> uploadPicture(  @RequestPart("file") MultipartFile multipartFile,  PictureUploadRequest pictureUploadRequest,  HttpServletRequest request) {  User loginUser = userService.getLoginUser(request);  PictureVO pictureVO = pictureService.uploadPicture(multipartFile, pictureUploadRequest, loginUser);  return ResultUtils.success(pictureVO);  
}
5.测试

当上传图片过大时侯,会触发报错,SpringBoot内嵌tomcat默认限制了请求中文件上传的大小。

spring:  # 开放更大的文件上传体积  servlet:  multipart:  max-file-size: 10MB
扩展

1.用枚举类 支持根据业务场景区分文件上传路径、校验规则等,从而复用FileManager。

2.目前文件上传的时候,会先在本地创建临时文件。如果不需要对文件进行额外处理、进一步提高性能,可以直接用流的方式将请求中的文件上传到COS。

3.补充更严格的校验,例如为支持的图片格式定义枚举,仅允许上传枚举定义的格式。

代码demo:

// 上传文件  
public static String uploadToCOS(MultipartFile multipartFile, String bucketName, String key) throws Exception {  // 创建 COS 客户端  COSClient cosClient = createCOSClient();  try (InputStream inputStream = multipartFile.getInputStream()) {  // 元信息配置  ObjectMetadata metadata = new ObjectMetadata();  metadata.setContentLength(multipartFile.getSize());  metadata.setContentType(multipartFile.getContentType());  // 创建上传请求  PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, inputStream, metadata);  // 上传文件  cosClient.putObject(putObjectRequest);  // 生成访问链接  return "https://" + bucketName + ".cos." + cosClient.getClientConfig().getRegion().getRegionName()  + ".myqcloud.com/" + key;  } finally {  cosClient.shutdown();  }  
}

图片管理

  • 管理员 deleteById
  • 管理员 update
  • 管理员 page
  • 管理员 getById
  • page desen
  • getById desen
  • update picture
1.数据模型

每一个请求都需要一个dto

图片更新请求,给管理员使用,tags类型改为List< String>便于前端上传

@Data  
public class PictureUpdateRequest implements Serializable {  /**  * id  */  private Long id;  /**  * 图片名称  */  private String name;  /**  * 简介  */  private String introduction;  /**  * 分类  */  private String category;  /**  * 标签  */  private List<String> tags;  private static final long serialVersionUID = 1L;  
}

图片修改

@Data  
public class PictureEditRequest implements Serializable {  /**  * id  */  private Long id;  /**  * 图片名称  */  private String name;  /**  * 简介  */  private String introduction;  /**  * 分类  */  private String category;  /**  * 标签  */  private List<String> tags;  private static final long serialVersionUID = 1L;  
}

图片查询

@EqualsAndHashCode(callSuper = true)  
@Data  
public class PictureQueryRequest extends PageRequest implements Serializable {  /**  * id  */  private Long id;  /**  * 图片名称  */  private String name;  /**  * 简介  */  private String introduction;  /**  * 分类  */  private String category;  /**  * 标签  */  private List<String> tags;  /**  * 文件体积  */  private Long picSize;  /**  * 图片宽度  */  private Integer picWidth;  /**  * 图片高度  */  private Integer picHeight;  /**  * 图片比例  */  private Double picScale;  /**  * 图片格式  */  private String picFormat;  /**  * 搜索词(同时搜名称、简介等)  */  private String searchText;  /**  * 用户 id  */  private Long userId;  private static final long serialVersionUID = 1L;  
}
2.服务开发

1.UserService编写判断用户是否为管理员的方法

/**  * 是否为管理员  *  * @param user  * @return  */  
boolean isAdmin(User user);
@Override  
public boolean isAdmin(User user) {  return user != null && UserRoleEnum.ADMIN.getValue().equals(user.getUserRole());  
}

查询包装类转换

@Override  
public QueryWrapper<Picture> getQueryWrapper(PictureQueryRequest pictureQueryRequest) {  QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();  if (pictureQueryRequest == null) {  return queryWrapper;  }  // 从对象中取值  Long id = pictureQueryRequest.getId();  String name = pictureQueryRequest.getName();  String introduction = pictureQueryRequest.getIntroduction();  String category = pictureQueryRequest.getCategory();  List<String> tags = pictureQueryRequest.getTags();  Long picSize = pictureQueryRequest.getPicSize();  Integer picWidth = pictureQueryRequest.getPicWidth();  Integer picHeight = pictureQueryRequest.getPicHeight();  Double picScale = pictureQueryRequest.getPicScale();  String picFormat = pictureQueryRequest.getPicFormat();  String searchText = pictureQueryRequest.getSearchText();  Long userId = pictureQueryRequest.getUserId();  String sortField = pictureQueryRequest.getSortField();  String sortOrder = pictureQueryRequest.getSortOrder();  // 从多字段中搜索  if (StrUtil.isNotBlank(searchText)) {  // 需要拼接查询条件  queryWrapper.and(qw -> qw.like("name", searchText)  .or()  .like("introduction", searchText)  );  }  queryWrapper.eq(ObjUtil.isNotEmpty(id), "id", id);  queryWrapper.eq(ObjUtil.isNotEmpty(userId), "userId", userId);  queryWrapper.like(StrUtil.isNotBlank(name), "name", name);  queryWrapper.like(StrUtil.isNotBlank(introduction), "introduction", introduction);  queryWrapper.like(StrUtil.isNotBlank(picFormat), "picFormat", picFormat);  queryWrapper.eq(StrUtil.isNotBlank(category), "category", category);  queryWrapper.eq(ObjUtil.isNotEmpty(picWidth), "picWidth", picWidth);  queryWrapper.eq(ObjUtil.isNotEmpty(picHeight), "picHeight", picHeight);  queryWrapper.eq(ObjUtil.isNotEmpty(picSize), "picSize", picSize);  queryWrapper.eq(ObjUtil.isNotEmpty(picScale), "picScale", picScale);  // JSON 数组查询  if (CollUtil.isNotEmpty(tags)) {  for (String tag : tags) {  queryWrapper.like("tags", "\"" + tag + "\"");  }  }  // 排序  queryWrapper.orderBy(StrUtil.isNotEmpty(sortField), sortOrder.equals("ascend"), sortField);  return queryWrapper;  
}

获取图片封装类相关方法

@Override  
public PictureVO getPictureVO(Picture picture, HttpServletRequest request) {  // 对象转封装类  PictureVO pictureVO = PictureVO.objToVo(picture);  // 关联查询用户信息  Long userId = picture.getUserId();  if (userId != null && userId > 0) {  User user = userService.getById(userId);  UserVO userVO = userService.getUserVO(user);  pictureVO.setUser(userVO);  }  return pictureVO;  
}

先获取到要查询的用户id列表,只发送一次查询用户表的请求,再将查询到的值设置到图片对象中。

/**  * 分页获取图片封装  */  
@Override  
public Page<PictureVO> getPictureVOPage(Page<Picture> picturePage, HttpServletRequest request) {  List<Picture> pictureList = picturePage.getRecords();  Page<PictureVO> pictureVOPage = new Page<>(picturePage.getCurrent(), picturePage.getSize(), picturePage.getTotal());  if (CollUtil.isEmpty(pictureList)) {  return pictureVOPage;  }  // 对象列表 => 封装对象列表  List<PictureVO> pictureVOList = pictureList.stream().map(PictureVO::objToVo).collect(Collectors.toList());  // 1. 关联查询用户信息  Set<Long> userIdSet = pictureList.stream().map(Picture::getUserId).collect(Collectors.toSet());  Map<Long, List<User>> userIdUserListMap = userService.listByIds(userIdSet).stream()  .collect(Collectors.groupingBy(User::getId));  // 2. 填充信息  pictureVOList.forEach(pictureVO -> {  Long userId = pictureVO.getUserId();  User user = null;  if (userIdUserListMap.containsKey(userId)) {  user = userIdUserListMap.get(userId).get(0);  }  pictureVO.setUser(userService.getUserVO(user));  });  pictureVOPage.setRecords(pictureVOList);  return pictureVOPage;  
}

图片数据校验

@Override  
public void validPicture(Picture picture) {  ThrowUtils.throwIf(picture == null, ErrorCode.PARAMS_ERROR);  // 从对象中取值  Long id = picture.getId();  String url = picture.getUrl();  String introduction = picture.getIntroduction();  // 修改数据时,id 不能为空,有参数则校验  ThrowUtils.throwIf(ObjUtil.isNull(id), ErrorCode.PARAMS_ERROR, "id 不能为空");  if (StrUtil.isNotBlank(url)) {  ThrowUtils.throwIf(url.length() > 1024, ErrorCode.PARAMS_ERROR, "url 过长");  }  if (StrUtil.isNotBlank(introduction)) {  ThrowUtils.throwIf(introduction.length() > 800, ErrorCode.PARAMS_ERROR, "简介过长");  }  
}
3.Controller
/**  * 删除图片  */  
@PostMapping("/delete")  
public BaseResponse<Boolean> deletePicture(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {  if (deleteRequest == null || deleteRequest.getId() <= 0) {  throw new BusinessException(ErrorCode.PARAMS_ERROR);  }  User loginUser = userService.getLoginUser(request);  long id = deleteRequest.getId();  // 判断是否存在  Picture oldPicture = pictureService.getById(id);  ThrowUtils.throwIf(oldPicture == null, ErrorCode.NOT_FOUND_ERROR);  // 仅本人或管理员可删除  if (!oldPicture.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {  throw new BusinessException(ErrorCode.NO_AUTH_ERROR);  }  // 操作数据库  boolean result = pictureService.removeById(id);  ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);  return ResultUtils.success(true);  
}  /**  * 更新图片(仅管理员可用)  */  
@PostMapping("/update")  
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)  
public BaseResponse<Boolean> updatePicture(@RequestBody PictureUpdateRequest pictureUpdateRequest) {  if (pictureUpdateRequest == null || pictureUpdateRequest.getId() <= 0) {  throw new BusinessException(ErrorCode.PARAMS_ERROR);  }  // 将实体类和 DTO 进行转换  Picture picture = new Picture();  BeanUtils.copyProperties(pictureUpdateRequest, picture);  // 注意将 list 转为 string  picture.setTags(JSONUtil.toJsonStr(pictureUpdateRequest.getTags()));  // 数据校验  pictureService.validPicture(picture);  // 判断是否存在  long id = pictureUpdateRequest.getId();  Picture oldPicture = pictureService.getById(id);  ThrowUtils.throwIf(oldPicture == null, ErrorCode.NOT_FOUND_ERROR);  // 操作数据库  boolean result = pictureService.updateById(picture);  ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);  return ResultUtils.success(true);  
}  /**  * 根据 id 获取图片(仅管理员可用)  */  
@GetMapping("/get")  
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)  
public BaseResponse<Picture> getPictureById(long id, HttpServletRequest request) {  ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);  // 查询数据库  Picture picture = pictureService.getById(id);  ThrowUtils.throwIf(picture == null, ErrorCode.NOT_FOUND_ERROR);  // 获取封装类  return ResultUtils.success(picture);  
}  /**  * 根据 id 获取图片(封装类)  */  
@GetMapping("/get/vo")  
public BaseResponse<PictureVO> getPictureVOById(long id, HttpServletRequest request) {  ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);  // 查询数据库  Picture picture = pictureService.getById(id);  ThrowUtils.throwIf(picture == null, ErrorCode.NOT_FOUND_ERROR);  // 获取封装类  return ResultUtils.success(pictureService.getPictureVO(picture, request));  
}  /**  * 分页获取图片列表(仅管理员可用)  */  
@PostMapping("/list/page")  
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)  
public BaseResponse<Page<Picture>> listPictureByPage(@RequestBody PictureQueryRequest pictureQueryRequest) {  long current = pictureQueryRequest.getCurrent();  long size = pictureQueryRequest.getPageSize();  // 查询数据库  Page<Picture> picturePage = pictureService.page(new Page<>(current, size),  pictureService.getQueryWrapper(pictureQueryRequest));  return ResultUtils.success(picturePage);  
}  /**  * 分页获取图片列表(封装类)  */  
@PostMapping("/list/page/vo")  
public BaseResponse<Page<PictureVO>> listPictureVOByPage(@RequestBody PictureQueryRequest pictureQueryRequest,  HttpServletRequest request) {  long current = pictureQueryRequest.getCurrent();  long size = pictureQueryRequest.getPageSize();  // 限制爬虫  ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);  // 查询数据库  Page<Picture> picturePage = pictureService.page(new Page<>(current, size),  pictureService.getQueryWrapper(pictureQueryRequest));  // 获取封装类  return ResultUtils.success(pictureService.getPictureVOPage(picturePage, request));  
}  /**  * 编辑图片(给用户使用)  */  
@PostMapping("/edit")  
public BaseResponse<Boolean> editPicture(@RequestBody PictureEditRequest pictureEditRequest, HttpServletRequest request) {  if (pictureEditRequest == null || pictureEditRequest.getId() <= 0) {  throw new BusinessException(ErrorCode.PARAMS_ERROR);  }  // 在此处将实体类和 DTO 进行转换  Picture picture = new Picture();  BeanUtils.copyProperties(pictureEditRequest, picture);  // 注意将 list 转为 string  picture.setTags(JSONUtil.toJsonStr(pictureEditRequest.getTags()));  // 设置编辑时间  picture.setEditTime(new Date());  // 数据校验  pictureService.validPicture(picture);  User loginUser = userService.getLoginUser(request);  // 判断是否存在  long id = pictureEditRequest.getId();  Picture oldPicture = pictureService.getById(id);  ThrowUtils.throwIf(oldPicture == null, ErrorCode.NOT_FOUND_ERROR);  // 仅本人或管理员可编辑  if (!oldPicture.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {  throw new BusinessException(ErrorCode.NO_AUTH_ERROR);  }  // 操作数据库  boolean result = pictureService.updateById(picture);  ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);  return ResultUtils.success(true);  
}

4.获取预置标签和分类

随着系统规模和数据不断扩大,可以再更改为使用配置中心或数据库动态管理这些数据,或者通过定时任务计算出热门图片分类和标签

@GetMapping("/tag_category")  
public BaseResponse<PictureTagCategory> listPictureTagCategory() {  PictureTagCategory pictureTagCategory = new PictureTagCategory();  List<String> tagList = Arrays.asList("热门", "搞笑", "生活", "高清", "艺术", "校园", "背景", "简历", "创意");  List<String> categoryList = Arrays.asList("模板", "电商", "表情包", "素材", "海报");  pictureTagCategory.setTagList(tagList);  pictureTagCategory.setCategoryList(categoryList);  return ResultUtils.success(pictureTagCategory);  
}

四、模块开发细节

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com