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

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

界面效果

【说明】

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

【商品详情】

【评论】

界面实现

工具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>
目录
相关文章
|
存储 缓存 负载均衡
后端开发中的性能优化策略
本文将探讨几种常见的后端性能优化策略,包括代码层面的优化、数据库查询优化、缓存机制的应用以及负载均衡的实现。通过这些方法,开发者可以显著提升系统的响应速度和处理能力,从而提供更好的用户体验。
646 6
|
JSON 自然语言处理 前端开发
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
784 72
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
|
10月前
|
存储 小程序 Java
热门小程序源码合集:微信抖音小程序源码支持PHP/Java/uni-app完整项目实践指南
小程序已成为企业获客与开发者创业的重要载体。本文详解PHP、Java、uni-app三大技术栈在电商、工具、服务类小程序中的源码应用,提供从开发到部署的全流程指南,并分享选型避坑与商业化落地策略,助力开发者高效构建稳定可扩展项目。
|
12月前
|
人工智能 小程序 前端开发
小程序、网站 vs. APP:成本差异究竟在哪里?技术栈如何决定项目上限?优雅草卓伊凡
小程序、网站 vs. APP:成本差异究竟在哪里?技术栈如何决定项目上限?优雅草卓伊凡
836 0
小程序、网站 vs. APP:成本差异究竟在哪里?技术栈如何决定项目上限?优雅草卓伊凡
|
人工智能 Java API
后端开发必看:零代码实现存量服务改造成MCP服务
本文介绍如何通过 **Nacos** 和 **Higress** 实现存量 Spring Boot 服务的零代码改造,使其支持 MCP 协议,供 AI Agent 调用。全程无需修改业务代码,仅通过配置完成服务注册、协议转换与工具映射,显著降低改造成本,提升服务的可集成性与智能化能力。
3320 1
|
存储 消息中间件 前端开发
PHP后端与uni-app前端协同的校园圈子系统:校园社交场景的跨端开发实践
校园圈子系统校园论坛小程序采用uni-app前端框架,支持多端运行,结合PHP后端(如ThinkPHP/Laravel),实现用户认证、社交关系管理、动态发布与实时聊天功能。前端通过组件化开发和uni.request与后端交互,后端提供RESTful API处理业务逻辑并存储数据于MySQL。同时引入Redis缓存热点数据,RabbitMQ处理异步任务,优化系统性能。核心功能包括JWT身份验证、好友系统、WebSocket实时聊天及活动管理,确保高效稳定的用户体验。
770 4
PHP后端与uni-app前端协同的校园圈子系统:校园社交场景的跨端开发实践
|
前端开发 Java 数据库连接
后端开发中的错误处理实践:原则与实战
在后端开发中,错误处理是保障系统稳定性的关键。本文介绍了错误分类、响应设计、统一处理机制及日志追踪等实践方法,帮助开发者提升系统的可维护性与排障效率,做到防患于未然。
|
前端开发 JavaScript 关系型数据库
2025 年前端与后端开发方向的抉择与展望-优雅草卓伊凡
2025 年前端与后端开发方向的抉择与展望-优雅草卓伊凡
975 5
2025 年前端与后端开发方向的抉择与展望-优雅草卓伊凡
|
人工智能 小程序 NoSQL
【一步步开发AI运动小程序】二十一、如何将AI运动项目配置持久化到后端?
本文介绍基于云智「Ai运动识别引擎」的运动配置持久化方案,旨在优化小程序或Uni APP中AI运动识别能力。通过将运动检测参数(如`Key`、`Name`、`TickMode`、`rules`或`samples`)持久化到后端,可避免因频繁调整运动参数而重新发布应用,提升用户体验。持久化数据结构支持规则和姿态样本存储,适用于关系数据库、文件或文档数据库(如MongoDB)。此外,云智还提供运动自动适配工具及「AI乐运动」产品,助力快速实现AI体育、全民健身等场景。
|
消息中间件 运维 安全
后端开发中的微服务架构实践与挑战####
在数字化转型的浪潮中,微服务架构凭借其高度的灵活性和可扩展性,成为众多企业重构后端系统的首选方案。本文将深入探讨微服务的核心概念、设计原则、关键技术选型及在实际项目实施过程中面临的挑战与解决方案,旨在为开发者提供一套实用的微服务架构落地指南。我们将从理论框架出发,逐步深入至技术细节,最终通过案例分析,揭示如何在复杂业务场景下有效应用微服务,提升系统的整体性能与稳定性。 ####
397 32