【易售小程序项目】商品详情展示+评论、评论展示、评论点赞+商品收藏【后端基于若依管理系统开发】

简介: 【易售小程序项目】商品详情展示+评论、评论展示、评论点赞+商品收藏【后端基于若依管理系统开发】

界面效果

【说明】

  • 界面中商品的图片来源于闲鱼,若侵权请联系删除

【商品详情】

【评论】

界面实现

工具js

该工具类的作用是,给定一个图片的url地址,计算出图片的高宽比,计算高宽比的作用是让图片可以按照正常比例显示

/**
 * 获取uuid
 */
export default {
  /**
   * 获取高宽比 乘以 100%
   */
  getAspectRatio(url) {
    uni.getImageInfo({
      src: url,
      success: function(res) {
        let aspectRatio = res.height * 100.0 / res.width;
        // console.log("aspectRatio:" + aspectRatio);
        return aspectRatio + "%";
      }
    });
  },
}
export default {
  /**
   * 日期格式化
   */
  formatDateToString(date) {
   return new Date(date).toLocaleString();
  },
}

页面

<template>
  <view class="container">
    <u-toast ref="uToast"></u-toast>
    <view class="userItem">
      <view class="userProfile">
        <u--image :src="productVo.avatar" width="35" height="35" shape="circle"></u--image>
        <view style="width: 10px;"></view>
        <view>
          <view class="nickname">{{productVo.nickname}}</view>
          <view class="other">10分钟前来过 广东工业大学大学城校区</view>
        </view>
      </view>
      <view class="follow" @click="follow" v-if="hadFollow==false">
        <view>
          <u-icon name="plus" color="#ffffff" style="font-weight: bold;" size="15"></u-icon>
        </view>
        <view style="margin-left: 10rpx;font-size: 15px;">
          关 注
        </view>
      </view>
      <view class="followed" @click="cancelFollow" v-else>
        <view style="font-size: 15px;color: #C2C2C2;">
          已 关 注
        </view>
      </view>
    </view>
    <view class="productItem">
      <view class="top">
        <view class="price">¥<text class="number">{{productVo.price}}</text>/{{productVo.unit}}</view>
        <view class="browseInformation">
          {{product.starNum}}人想要 | {{product.readNum}}个浏览
        </view>
      </view>
      <view class="productDetail">
        {{productVo.description}}
      </view>
      <u--image :showLoading="true" v-for="(pic,index) in productVo.picList" :src="pic" width="100%"
        :height="getAspectRatio(pic)" radius="10" mode="widthFix"></u--image>
    </view>
    <view class="commentView">
      <view style="color: #3D3D3D;">
        {{commentNum}}条评论
      </view>
      <view v-for="(commentItem,index) in commentVoList">
        <view class="commentItem">
          <view style="display: flex;">
            <u--image :src="commentItem.userAvatar" width="30" height="30" shape="circle"></u--image>
            <view style="width: 10px;"></view>
            <view @click="clickShowBottomPopup(1, commentItem.id,commentItem.userNickName)">
              <view class="nickname">{{commentItem.userNickName}}</view>
              <view class="content">
                {{commentItem.content}}
              </view>
              <view class="dateAndPosition">{{formatDateToString(commentItem.createTime)}}</view>
            </view>
          </view>
          <view style="display: inline-block;text-align: center;">
            <u-icon name="thumb-up" size="28" @click="likeComment(commentItem.id,commentItem)"
              v-if="commentItem.isLike==0"></u-icon>
            <u-icon name="thumb-up-fill" color="#2B92FF" size="28"
              @click="cancelLikeComment(commentItem.id,commentItem)" v-else></u-icon>
            <view style="font-size: 12px;color: #B9B9B9;">
              {{commentItem.likeNum}}
            </view>
          </view>
        </view>
        <view class="sonCommentItem" v-for="(commentItem1,index1) in commentItem.children">
          <view style="display: flex;">
            <u--image :src="commentItem1.userAvatar" width="30" height="30" shape="circle"></u--image>
            <view style="width: 10px;"></view>
            <view @click="clickShowBottomPopup(1, commentItem1.id,commentItem1.userNickName)">
              <view class="nickname">{{commentItem1.userNickName}}</view>
              <view class="content">
                <text style="font-size: 14px;">
                  回复了<text style="color:#B9B9B9 ;">{{commentItem1.toUserNickName}}</text>:
                </text>
                <text>
                  {{ commentItem1.content }}
                </text>
              </view>
              <view class="dateAndPosition">{{formatDateToString(commentItem1.createTime)}}</view>
            </view>
          </view>
          <view style="display: inline-block;text-align: center;">
            <u-icon name="thumb-up" size="28" @click="likeComment(commentItem1.id,commentItem1)"
              v-if="commentItem1.isLike==0"></u-icon>
            <u-icon name="thumb-up-fill" color="#2B92FF" size="28"
              @click="cancelLikeComment(commentItem1.id, commentItem1)" v-else></u-icon>
            <view style="font-size: 12px;color: #B9B9B9;">
              {{commentItem1.likeNum}}
            </view>
          </view>
        </view>
      </view>
    </view>
    <view class="footer">
      <view>
        <view class="item" @click="clickShowBottomPopup(0, productVo.id,)">
          <u-icon name="chat" size="28"></u-icon>
          <view class="comment">评论</view>
        </view>
        <view class="item" @click="starProduct()" v-if="hadStar==false">
          <u-icon name="star" size="28"></u-icon>
          <view class="comment">我想要</view>
        </view>
        <view class="item" @click="cancelStar()" v-if="hadStar==true">
          <u-icon name="star-fill" color="#2B92FF" size="28"></u-icon>
          <view class="comment" style="color: #2B92FF">已收藏</view>
        </view>
      </view>
      <view class="chat">
        <u-icon name="chat" color="#ffffff" size="18"></u-icon>
        <view style="width: 5px;"></view>
        私 聊
      </view>
    </view>
    <!-- 底部弹出框:用于输入评论 -->
    <!-- @close="this.showBottomPopup=false" 点击遮罩层关闭弹框  -->
    <u-popup :show="showBottomPopup" mode="bottom" :round="10" @close="this.showBottomPopup=false">
      <view class="commentPopup">
        <u--textarea v-model="comment.content" :placeholder="commentPlaceHolder" autoHeight height="200"
          border="surround"></u--textarea>
        <view class="commentButton" @click="commitComment()">
          <u-icon name="chat" color="#ffffff" size="18"></u-icon>
          <view style="width: 5px;"></view>
          评 论
        </view>
      </view>
    </u-popup>
  </view>
</template>
<script>
  import pictureApi from "@/utils/picture.js";
  import {
    addFollow,
    hadFollowSomeone,
    cancelFollowSomeone
  } from "@/api/market/follow.js";
  import {
    starProduct,
    cancelStar,
    hadStar
  } from "@/api/market/star.js";
  import {
    addComment,
    listCommentVoOfProduct
  } from "@/api/market/comment.js";
  import dateUtil from "@/utils/date.js";
  import {
    likeComment,
    cancelLikeComment
  } from "@/api/market/commentLike.js"
  import {
    getProduct
  } from "@/api/market/prodct.js"
  export default {
    data() {
      return {
        productVo: {},
        product: {},
        // 是否已经关注商品主人
        hadFollow: false,
        // 是否已经收藏商品
        hadStar: false,
        // 是否显示底部弹出框
        showBottomPopup: false,
        // 评论
        comment: {
          itemId: undefined,
          type: undefined,
          content: '',
          isTop: 0
        },
        // 存储商品对应的评论集合
        commentVoList: [],
        // 评论数量
        commentNum: undefined,
        commentPlaceHolder: "",
      }
    },
    methods: {
      /**
       * 获取高宽比 乘以 100%
       */
      getAspectRatio(url) {
        // uni.getImageInfo({
        //  src: url,
        //  success: function(res) {
        //    let aspectRatio = res.height * 100.0 / res.width;
        //    // console.log("aspectRatio:" + aspectRatio);
        //    return aspectRatio + "%";
        //  }
        // });
        return pictureApi.getAspectRatio(url);
      },
      /**
       * 关注用户
       */
      follow() {
        let data = {
          followedId: this.productVo.userId
        }
        addFollow(data).then(res => {
          this.hadFollow = true;
          this.$refs.uToast.show({
            type: 'success',
            message: "关注成功",
            duration: 300
          })
        }).catch(err => {
          this.$refs.uToast.show({
            type: 'error',
            message: err.msg,
            duration: 300
          })
        })
      },
      /**
       * 取消关注
       */
      cancelFollow() {
        cancelFollowSomeone(this.productVo.userId).then(res => {
          this.hadFollow = false;
          this.$refs.uToast.show({
            type: 'success',
            message: "取消关注成功",
            duration: 300
          })
        })
      },
      /**
       * 查询是否已经关注了用户
       */
      searchWhetherFollow() {
        hadFollowSomeone(this.productVo.userId).then(res => {
          // console.log("res:" + JSON.stringify(res));
          this.hadFollow = res.hadFollow;
          // console.log("this.hadFollow :" + this.hadFollow);
        })
      },
      /**
       * 收藏商品
       */
      starProduct() {
        starProduct(this.productVo.id).then(res => {
          this.hadStar = true;
          this.getProduct();
          this.$refs.uToast.show({
            type: 'success',
            message: "收藏成功",
            duration: 300
          })
        })
      },
      /**
       * 取消收藏
       */
      cancelStar() {
        cancelStar(this.productVo.id).then(res => {
          this.hadStar = false;
          this.getProduct();
          this.$refs.uToast.show({
            type: 'success',
            message: "取消收藏成功",
            duration: 300
          })
        })
      },
      /**
       * 点赞评论
       */
      likeComment(commentId, comment) {
        // console.log("comment:" + JSON.stringify(comment))
        likeComment(commentId).then(res => {
          comment.isLike = 1;
          comment.likeNum += 1;
          this.$refs.uToast.show({
            type: 'success',
            message: "点赞成功",
            duration: 300
          })
        })
      },
      /**
       * 取消点赞评论
       */
      cancelLikeComment(commentId, comment) {
        cancelLikeComment(commentId).then(res => {
          comment.isLike = 0;
          comment.likeNum -= 1;
          this.$refs.uToast.show({
            type: 'success',
            message: "取消点赞成功",
            duration: 300
          })
        })
      },
      /**
       * 查询是否已经关注了用户
       */
      searchWhetherStar() {
        hadStar(this.productVo.id).then(res => {
          // console.log("res:" + JSON.stringify(res));
          this.hadStar = res.hadStar;
          // console.log("this.hadFollow :" + this.hadFollow);
        })
      },
      /**
       * 显示底部弹出框
       */
      clickShowBottomPopup(type, itemId, username = undefined) {
        this.showBottomPopup = true;
        this.comment.type = type;
        this.comment.itemId = itemId;
        if (type == 0) {
          this.commentPlaceHolder = "想要了解更多信息,可以评论让商品主人看见哟";
        } else {
          this.commentPlaceHolder = "正在回复" + username + "";
        }
      },
      /**
       * 发表评论
       */
      commitComment() {
        // console.log("发送评论,comment:" + JSON.stringify(this.comment))
        addComment(this.comment).then(res => {
          this.showBottomPopup = false;
          this.comment.content = '';
          this.listCommentVoOfProduct();
          this.$refs.uToast.show({
            type: 'success',
            message: "评论发送成功",
            duration: 300
          })
        })
      },
      /**
       * 获取商品对应的所有评论
       */
      listCommentVoOfProduct() {
        listCommentVoOfProduct(this.productVo.id).then(res => {
          // console.log("listCommentVoOfProduct:" + JSON.stringify(res));
          this.commentVoList = res.tree;
          this.commentNum = res.commentNum;
        })
      },
      /**
       * 格式化日期
       * @param {Object} date
       */
      formatDateToString(dateStr) {
        let date = new Date(dateStr);
        // 月份需要加一
        return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
      },
      /**
       * 获取商品详细信息,同时增加阅读量
       */
      getProduct() {
        getProduct(this.productVo.id).then(res => {
          console.log("product:" + JSON.stringify(res.data));
          this.product = res.data;
        })
      }
    },
    onLoad(e) {
      this.productVo = JSON.parse(decodeURIComponent(e.productVo));
      this.searchWhetherFollow();
      this.searchWhetherStar();
      this.listCommentVoOfProduct();
      this.getProduct();
      // console.log("productVo:" + JSON.stringify(productVo));
    }
  }
</script>
<style lang="scss">
  .container {
    // padding: 20rpx;
    background: #F7F7F7;
    .userItem {
      display: flex;
      align-items: center;
      justify-content: space-between;
      background: #ffffff;
      padding: 20rpx;
      .userProfile {
        display: flex;
        .nickname {
          color: #202020;
          font-weight: bold;
          font-size: 14px;
        }
        .other {
          color: #A6A4A5;
          font-size: 11px;
        }
      }
      .follow {
        display: flex;
        align-items: center;
        font-weight: bold;
        color: #ffffff;
        background: #2B92FF;
        border-radius: 20px;
        padding: 4px 8px;
      }
      .followed {
        background: #F6F6F6;
        border-radius: 20px;
        padding: 4px 8px;
      }
    }
    .productItem {
      background: #ffffff;
      padding: 20rpx;
      .top {
        display: flex;
        align-items: center;
        justify-content: space-between;
        .price {
          color: #F84442;
          font-weight: bold;
          .number {
            font-size: 30px;
          }
        }
        .browseInformation {
          color: #A6A4A5;
          font-size: 14px;
        }
      }
      .productDetail {
        margin-top: 20rpx;
        margin-bottom: 10rpx;
        color: #4C4C4C;
        font-size: 15px;
        line-height: 30px;
        font-weight: bold;
      }
    }
    .commentView {
      margin-top: 10px;
      // 用来预留展示 footer 的高度,不然footer会挡住评论
      margin-bottom: calc(60px + 10rpx);
      background: #ffffff;
      padding: 30rpx 30rpx;
      .nickname {
        font-size: 14px;
        color: #B9B9B9;
      }
      .content {
        margin: 5px;
        // 解决英文字符串、数字不换行的问题
        word-break: break-all;
        word-wrap: break-word;
      }
      .dateAndPosition {
        font-size: 11px;
        color: #B9B9B9;
      }
      .commentItem {
        display: flex;
        margin: 10px;
        justify-content: space-between;
      }
      .sonCommentItem {
        display: flex;
        margin: 10px 10px 10px 50px;
        justify-content: space-between;
      }
    }
    .footer {
      padding: 20rpx;
      position: fixed;
      // right: 20rpx;
      bottom: 0rpx;
      background: #ffffff;
      height: 60px;
      width: 710rpx;
      padding-top: 2px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      .item {
        display: inline-block;
        text-align: center;
        margin-right: 10px;
        .comment {
          font-size: 10px;
        }
      }
      .chat {
        display: flex;
        align-items: center;
        background-color: #2B92FF;
        border-radius: 20px;
        padding: 7px;
        color: #ffffff;
        // margin-right: 20px;
        font-size: 12px;
      }
    }
    .commentPopup {
      display: flex;
      padding: 10px;
      min-height: 200rpx;
      .commentButton {
        background-color: #2B92FF;
        border-radius: 5px;
        padding: 7px;
        color: #ffffff;
        font-size: 12px;
        height: 20px;
        display: flex;
        align-items: center;
      }
    }
  }
</style>

日期格式化

有时候后端传递过来的日期格式直接在前端页面中展示不太美观或简洁,那就可以自己写一个日期格式化方法,将日期转化为我们需要的格式来显示

/**
 * 格式化日期
 * @param {Object} date
 */
formatDateToString(dateStr) {
  let date = new Date(dateStr);
  // 月份需要加一
  return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
},

英文自动换行显示

.content {
  margin: 5px;
  // 解决英文字符串、数字不换行的问题
  word-break: break-all;
  word-wrap: break-word;
}

后端

收藏

Controller

为了便于商品数据的查询,我在数据库设计的时候给商品表增加了收藏数的冗余字段,因此每次收藏商品或者取消商品的收藏的同时,需要更新商品表的收藏数

/**
 * 收藏商品
 */
@PreAuthorize("@ss.hasPermi('market:star:star')")
@GetMapping("/starProduct/{productId}")
public AjaxResult starProduct(@PathVariable("productId") Long productId) {
    Star star = new Star();
    star.setUserId(getLoginUser().getUserId());
    star.setProductId(productId);
    boolean isStar = starService.addStar(star);
    if (isStar){
        // 需要将商品的收藏量+1
        productService.starNumPlusOne(productId);
    }
    return AjaxResult.success();
}

Service

package com.shm.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.domain.entity.Star;
import com.shm.mapper.StarMapper;
import com.shm.service.IStarService;
import org.springframework.stereotype.Service;
/**
* @author dam
* @description 针对表【collection(收藏表)】的数据库操作Service实现
* @createDate 2023-08-09 19:41:23
*/
@Service
public class IStarServiceImpl extends ServiceImpl<StarMapper, Star>
    implements IStarService {
    @Override
    public boolean addStar(Star star) {
        return baseMapper.addStar(star);
    }
}

mapper

public interface StarMapper extends BaseMapper<Star> {
    boolean addStar(@Param("star") Star star);
}

将商品添加收藏的时候,需要先判断同样的收藏数据不存在于数据库中才执行插入操作,否则如果用户网络卡顿并多次发送收藏请求,数据库会出现冗余的脏数据

<insert id="addStar">
    INSERT INTO `star` (`user_id`, `product_id`)
    SELECT #{star.userId},#{star.productId} FROM DUAL
    WHERE NOT EXISTS (
            SELECT 1 FROM `star`
            WHERE `user_id` = #{star.productId} AND `product_id` = #{star.productId} limit 1
        );
</insert>

评论

Controller

/**
 * 获取商品对应的所有评论
 *
 * @param productId
 * @return
 */
@PreAuthorize("@ss.hasPermi('market:comment:list')")
@GetMapping("/listCommentVoOfProduct/{productId}")
public AjaxResult listCommentVoOfProduct(@PathVariable("productId") Long productId) {
    // 查询出商品对应的所有评论数据
    List<CommentVo> commentVoList = commentService.listCommentVoOfProduct(productId, getLoginUser().getUserId());
    int commentNum = commentVoList.size();
    // 将评论数据封装成树形结构
    List<CommentVo> tree = commentService.buildTree(commentVoList);
    return AjaxResult.success().put("tree", tree).put("commentNum", commentNum);
}

Service

需要注意的是,这里的树形结构只有两层数据(针对商品的评论为一层,针对评论的所有评论为一层),因为小程序不方便显示太多层数据,否则宽度会非常大,用户需要反复滑动来查看完整的评论

@Override
public List<CommentVo> listCommentVoOfProduct(Long productId, Long userId) {
    return commentMapper.listCommentVoOfProduct(productId, userId);
}
/**
 * 将评论数据封装成树形结构
 *
 * @param commentVoList
 * @return
 */
@Override
public List<CommentVo> buildTree(List<CommentVo> commentVoList) {
    // 将所有父级评论过滤出来
    List<CommentVo> fatherList = commentVoList.stream().filter((item) -> {
        return item.getType() == 0;
    }).collect(Collectors.toList());
    commentVoList.removeAll(fatherList);
    // 为所有父级评论寻找孩子
    for (CommentVo father : fatherList) {
        father.setChildren(new ArrayList<>());
        this.searchSon(father.getId(), father.getUserNickName(), father.getChildren(), commentVoList);
    }
    return fatherList;
}
/**
 * 寻找孩子
 *
 * @param fatherId
 * @param children
 * @param commentVoList
 */
private void searchSon(Long fatherId, String fatherNickName, List<CommentVo> children, List<CommentVo> commentVoList) {
    for (CommentVo commentVo : commentVoList) {
        if (commentVo.getItemId().equals(fatherId)) {
            commentVo.setToUserNickName(fatherNickName);
            children.add(commentVo);
            this.searchSon(commentVo.getId(), commentVo.getUserNickName(), children, commentVoList);
        }
    }
}

Mapper

这段sql非常复杂,一次性将评论的主人昵称、头像、评论的点赞数量查出来了,同时还使用递归查询来不断查询出评论的子评论。我目前不能保证这段sql的效率,只是实现了功能,后面如果性能不足,我再想办法优化

<select id="listCommentVoOfProduct" resultType="com.ruoyi.common.core.domain.vo.CommentVo">
      SELECT
          ct.id,
          ct.user_id,
          ct.item_id,
          ct.type,
          ct.content,
          ct.create_time,
          u.nick_name AS userNickName,
          u.avatar AS userAvatar,
          CASE
              WHEN cl.user_id IS NULL THEN
                  0 ELSE 1
              END AS isLike,
          ct.LEVEL,
          COALESCE ( likeNum, 0 ) AS likeNum
      FROM
          (
              WITH RECURSIVE comment_tree AS (
                  SELECT
                      id,
                      user_id,
                      item_id,
                      type,
                      content,
                      create_time,
                      0 AS LEVEL
                  FROM
                      COMMENT
                  WHERE
                      item_id = #{productId} and type=0
                  UNION ALL
                  SELECT
                      c.id,
                      c.user_id,
                      c.item_id,
                      c.type,
                      c.content,
                      c.create_time,
                      ct.LEVEL + 1 AS LEVEL
                  FROM
                      COMMENT c
                          INNER JOIN comment_tree ct ON c.item_id = ct.id
                  WHERE
                      c.type = 1
              ) SELECT
                  *
              FROM
                  comment_tree
          ) ct
              LEFT JOIN ( SELECT comment_id, COUNT(*) AS likeNum FROM comment_like WHERE is_deleted = 0 GROUP BY comment_id ) pc ON ct.id = pc.comment_id
              LEFT JOIN sys_user AS u ON ct.user_id = u.user_id
              LEFT JOIN comment_like cl ON ct.id = cl.comment_id
              AND cl.user_id = #{userId} and cl.is_deleted =0
  </select>

商品

Controller

/**
 * 获取商品详细信息
 */
@PreAuthorize("@ss.hasPermi('market:product:query')")
@GetMapping(value = "/{id}")
@Transactional // 同时处理多个表,添加事务
public AjaxResult getInfo(@PathVariable("id") Long id) {
    // 首先判断用户有没有阅读该商品
    boolean isAdd = productReadService.addRead(new ProductRead(getLoginUser().getUserId(), id));
    if (isAdd) {
        // 需要将商品的阅读量+1
        productService.readNumPlusOne(id);
    }
    return success(productService.getById(id));
}

阅读

Service

<insert id="addRead">
    INSERT INTO `product_read` (`user_id`, `product_id`)
    SELECT #{productRead.userId},#{productRead.productId} FROM DUAL
    WHERE NOT EXISTS (
            SELECT 1 FROM `product_read`
            WHERE `user_id` = #{productRead.userId} AND `product_id` = #{productRead.productId} limit 1
        );
</insert>
目录
相关文章
预约按摩小程序开发,为什么很多上门按摩平台根本招聘不到优秀技师?
上门按摩平台面临招不到优秀技师的问题,主要原因是平台众多,技师选择多样。为解决此问题,平台可引入技师等级制度,根据订单数量和好评率划分高、低等级技师。高等级技师可享受70%-90%的高提成及首页推荐,这不仅能激励技师的积极性,还能帮助平台筛选出优质技师,提升服务质量和口碑,形成良性循环。
|
5天前
|
小程序 云计算 Android开发
发者社区 云计算 文章 正文 小程序开发与公众号用户关联推送消息(九)
发者社区 云计算 文章 正文 小程序开发与公众号用户关联推送消息(九)
22 3
|
10天前
|
小程序 云计算 开发者
|
11天前
|
小程序
|
12天前
|
小程序 数据安全/隐私保护
|
11天前
|
小程序
|
15天前
|
小程序
|
8天前
|
存储 SQL API
探索后端开发:构建高效API与数据库交互
【10月更文挑战第36天】在数字化时代,后端开发是连接用户界面和数据存储的桥梁。本文深入探讨如何设计高效的API以及如何实现API与数据库之间的无缝交互,确保数据的一致性和高性能。我们将从基础概念出发,逐步深入到实战技巧,为读者提供一个清晰的后端开发路线图。
|
7天前
|
JSON 前端开发 API
后端开发中的API设计与文档编写指南####
本文探讨了后端开发中API设计的重要性,并详细阐述了如何编写高效、可维护的API接口。通过实际案例分析,文章强调了清晰的API设计对于前后端分离项目的关键作用,以及良好的文档习惯如何促进团队协作和提升开发效率。 ####
|
9天前
|
存储 SQL 数据库
深入浅出后端开发之数据库优化实战
【10月更文挑战第35天】在软件开发的世界里,数据库性能直接关系到应用的响应速度和用户体验。本文将带你了解如何通过合理的索引设计、查询优化以及恰当的数据存储策略来提升数据库性能。我们将一起探索这些技巧背后的原理,并通过实际案例感受优化带来的显著效果。
28 4