Bootstrap

基于Spring Boot+Vue的助农销售平台(协同过滤算法、节流算法、支付宝沙盒支付、图形化分析)

🎈系统亮点:协同过滤算法、节流算法、支付宝沙盒支付、图形化分析;

一.系统开发工具与环境搭建

1.系统设计开发工具


后端使用Java编程语言的Spring boot框架
项目架构:B/S架构
运行环境:win10/win11、jdk17


前端:
技术:框架Vue.js;UI库:ElementUI;
开发工具:Visual Studio Code;



后端:
技术:Java语言、mybatis plus、Spring boot框架;
开发工具:IDEA 2023.3.3版本;



数据库:
数据库:mysql5.7/8.0
数据库工具:Navicat12版本;


二. 系统实现(部分展示)

1 客户模块

1.1 系统首页模块

首页展示官方精选的一些农产品种类和农产品,吸引客户的注意力。前端为了方便客户去查找其他的界面,在首页设置了农产品大全、农产圈、通知公告导航地址,方便客户点击直接请求url进行跳转等界面。同时,还为客户提供注册和登录入口,方便客户进行账号管理。

客户没登录前,可以浏览商品,但是不可以进行购买商品。当客户点击加入购物车,则会“请先登录,再操作”,随后前端会请求登录的url地址,跳转到登录页面,客户输入账号、密码,选择角色,输入验证码,点击“登录按钮”,前端将输入的数据作为参数,然后去请求/User/SignIn方法,进行登录操作。进入到商品详情页后,前端请求/Good/Get方法,返回商品详情的数据,展示在前台界面,商品详情页面包含农产品的详细描述、高清图片、规格参数、价格、库存、商品评价等信息。

后端关键代码展示如下:

@SneakyThrows
@Override
public GoodDto Get(GoodPagedInput input) {
if (input.getId() == null) {
return new GoodDto();
}
PagedResult<GoodDto> pagedResult = List(input);
return pagedResult.getTotalCount() > 0 ? pagedResult.getItems().stream().findFirst().get() : new GoodDto();
}

1.2 农产品大全模块

农产品大全,客户可以根据不同的类别浏览和筛选农产品。在页面的顶部放置了一个清晰的导航栏。导航栏中的分类可以按照农产品的种类进行分类。当客户点击某个分类链接时,导航到该分类的页面。在分类页面上,展示该类别下的所有商品。后端提供/Good/List接口,用于前端查询和筛选农产品信息。搜索框采用模糊查询,只要有正确的关键字,就可以搜索到与关键字相关的商品。农产品大全界面,如图所示。

通过协同过滤算法进行推荐商品,后端提供接口Good/Top10,进行请求推荐的商品。这个方法创建一个协同过滤对象,获取所有用户和订单。遍历所有用户。对于每个用户,找到他们购买的所有商品ID和购买次数。方法将用户ID、商品ID和评分添加到协同过滤对象中。初始化一个用于存储用户相似度的列表。再次遍历所有用户,但是不包含当前用户。对于每个其他用户,检查是否已经计算过与当前用户的相似度。如果还没有计算过相似度,调用方法来计算当前用户和其他用户的相似度。将用户ID对和相似度添加到列表中。查找与当前用户相似度最高的10个用户。遍历这些相似用户的购买记录,找到他们购买但当前用户没有购买的商品。根据相似度和购买次数等因素对这些商品进行排序,生成一个推荐列表,随后将推荐列表返回给当前用户。协同过滤推荐商品如图所示。

协同过滤算法推荐商品代码:

//随机推荐10个商品
LambdaQueryWrapper<Good> queryWrapper = Wrappers.<Good>lambdaQuery()
.eq(Good::getAuditStatus, Enums.AuditStatus.审核通过.index())
.orderByDesc(Good::getCreationTime)
.last("limit 10");
List<Good> goods = _GoodMpper.selectList(queryWrapper);
List<GoodDto> items = Extension.copyBeanList(goods, GoodDto.class);
return PagedResult.GetInstance(items, (long) 10);

1.3 话题模块

话题模块,前端发送请求后端/Topic/List接口,展示话题列表给前端,前端渲染到导航栏。当用户点击某个话题,前端将话题id作为参数传递给发送请求到后端,请求Topic/Get接口话题详细信息。后端返回话题详细信息给前端。用户发表新的评论,前端收集用户输入的内容,发送请求Comment/CreateOrEdit到后端。话题界面,如图所示。

关键代码:

//查询出关联的TopicType表信息
TopicTypeDto TopicTypeDTO = new TopicTypeDto();
TopicType TopicTypeEntity = _TopicTypeMapper.selectOne(Wrappers.<TopicType>lambdaQuery().eq(TopicType::getId, item.getTopicTypeId()));
if (TopicTypeEntity != null) {
BeanUtils.copyProperties(TopicTypeDTO, TopicTypeEntity);
item.setTopicTypeDto(TopicTypeDTO);
}

1.4 我的聊天模块

前端通过轮询的方式调用后端的聊天接口ChatTabulation/MyChatTabulationList。前端会每隔一段时间就向服务器发送一次请求,询问是否有新的聊天记录或者聊天消息。后端负责处理前端的请求,查询数据库中是否有新的聊天信息,然后将这些信息返回给前端。我的聊天界面,如图所示。

关键代码:

//查询我们2个之间的所有聊天记录
LambdaQueryWrapper<ChatRecord> queryWrapper = Wrappers.<ChatRecord>lambdaQuery()
        .eq(input.getSendUserId()!=null,ChatRecord::getSendUserId,input.getSendUserId())
        .eq(input.getReceiveUserId()!=null,ChatRecord::getReceiveUserId,input.getReceiveUserId())
        .or()
        .eq(input.getSendUserId()!=null,ChatRecord::getSendUserId,input.getReceiveUserId())
        .eq(input.getReceiveUserId()!=null,ChatRecord::getReceiveUserId,input.getSendUserId());

1.5 我的购物车模块

我的购物车使用一个Vue.js组件,用于显示一个数据表格。它使用了Element UI库中的<data-table>组件来展示数据,通过调用url地址/BuyCard/List获取数据。客户可以查看自己的所有加入购物车的商品,支持移除商品或者选择商品去进行结算。我的购物车界面,如图所示。

客户选择商品进行结算,选择支付宝,首先会把填写的订单表单信息提交给后端,后端创建表单成功后会返回一个订单编号,然后根据订单编号去调用payOrder方法,该方法会通过http工具类去调用支付宝沙箱,然后返回一个支付宝表单form,然后把表单form字符串返回给前端,前端把返回的表单加载到当前页面的body,再触发submit事件提交,则会跳转到具体的支付宝支付页面,当客户完成支付的时候,支付宝会根据你设置的回调页面,最终跳转到系统的我的订单页面,从而完成业务的闭环。支付宝支付界面,如图所示。

关键代码:
 

OrderInfo orderInfo = _OrderInfoMpper.selectById(input.getId());
List<OrderDetail> orderDetails = _OrderDetailMapper.selectList(Wrappers.<OrderDetail>lambdaQuery().eq(OrderDetail::getOrderInfoId, orderInfo.getId()));
String body = "";
for (OrderDetail orderDetail : orderDetails) {
    body += orderDetail.getGoodName() + ",";
}
String result = AliPayHelper.CreatePay("测试系统购买", body, orderInfo.getCode().toString(), orderInfo.getTotalMoney().toString(), input.getCallBack());
return result;

5.1.6 我的订单模块

客户可以在个人中心查找我的订单,将客户Id传递/OrderInfo/List接口,进行调用,返回该客户的所有的订单。客户可以查看自己订单的明细,前端将订单Id传递给/OrderDetail/List接口,进行调用返回数据进行展示。当订单状态为已发货的时候,客户可以进行确认收货。在订单还没有发货的时候,可以选择取消订单。我的订单界面如图所示。

关键代码:

queryWrapper = queryWrapper.orderByDesc(OrderInfo::getCreationTime);
Page<OrderInfo> page = new Page<>(input.getPage(), input.getLimit());
IPage<OrderInfo> pageRecords = _OrderInfoMpper.selectPage(page, queryWrapper);
Long totalCount = _OrderInfoMpper.selectCount(queryWrapper);
List<OrderInfoDto> items = Extension.copyBeanList(pageRecords.getRecords(), OrderInfoDto.class);
List<Good> goodList = _GoodMpper.selectList(null).stream().toList();
List<GoodDto> goodDtoList = Extension.copyBeanList(goodList, GoodDto.class);

2 农户模块

2.1 商铺开店

农户想要进行开店,需要进行注册自己的店铺,输入店铺名称,将自己设计的logo进行上传,写入开店铺的地理位置,执业编号,注册的邮箱(邮箱使用正则表达式进行验证,不符合要求不允通过),身份证号(也是用正则表达式,18位,最后一位可以为数字或大写字母X),上传经营许可证,前端根据农户输入的数据进行请求/Shop/ShopRegister接口,查询数据库中是否存在与输入店铺名相同且审核状态为待审核的店铺。如果存在,则抛出一个自定义异常,说明“店铺名已经在审核中”。随后查询数据库,查看是否存在与输入店铺名相同且审核状态为审核通过的店铺。如果存在,则抛出异常,说明“店铺名已经在审核通过”。继续查询数据库中是否存在与输入店铺名相同、审核状态为待审核但id与当前店铺不同的店铺。如果存在,则抛出异常,说明“店铺名已经被其他人占用”。发送申请通过后,就等待管理员审核。商铺开店界面如图所示。

关键代码:

List<Shop> shopList = _ShopMpper.selectList(
        Wrappers.<Shop>lambdaQuery().eq(Shop::getName, input.getName())
                .in(Shop::getAuditStatus, Enums.AuditStatus.待审核.index())
                .ne(Shop::getId, input.getId()));
if (shopList.size() > 0) {
    throw new CustomException("店铺名已经被其他人占用");
}
shopList = _ShopMpper.selectList(
        Wrappers.<Shop>lambdaQuery().eq(Shop::getName, input.getName())
                .in(Shop::getAuditStatus, Enums.AuditStatus.审核通过.index())
                .ne(Shop::getId, input.getId()));
if (shopList.size() > 0) {
    throw new CustomException("店铺名已经被其他人占用");
}

2.2 我的店铺

农户可以装修自己的线上的店铺,修改店铺名称、店铺地址、封面、介绍等信息,前端会使用post方式请求Shop/CreateOrEdit接口,将农户修改的内容作为方法的入参进行传递,后端接收到前端的请求,进行操作店铺表,进行修改数据,并保存。执行完毕后,前端会将页面重新渲染,展示更新后的数据。我的店铺界面如图所示。

关键代码:

Shop Shop = new Shop();
BeanUtils.copyProperties(Shop, input);
//调用数据库的增加或者修改方法
saveOrUpdate(Shop);
//定义一个返回给前端的店铺传输模型
ShopDto ShopDto = new ShopDto();
//同理把操作的店铺实体拷贝给店铺传输模型
BeanUtils.copyProperties(ShopDto, Shop);
//把传输模型返回给前端
return ShopDto;

2.3 农户店铺的订单

农户店铺的订单,农户可以进行发货处理,输入物流单号,进行发货,前端将订单信息以物流单号发送给后端/OrderInfo/CreateOrEdit接口,将物流单号填写到订单表,订单的状态从待发货改为已发货。农户店铺的订单界面,如图所示。

关键代码:

OrderInfo OrderInfo = new OrderInfo();
//把前端传入的input参数拷贝到订单实体
BeanUtils.copyProperties(OrderInfo, input);
//调用数据库的增加或者修改方法
saveOrUpdate(OrderInfo);
//定义一个返回给前端的订单传输模型
OrderInfoDto OrderInfoDto = new OrderInfoDto();
//同理把操作的订单实体拷贝给订单传输模型
BeanUtils.copyProperties(OrderInfoDto, OrderInfo);
//把传输模型返回给前端
return OrderInfoDto;

2.4 我的商品

农户进行管理自己店铺的商品,可以进行添加商品,以及添加商品的属性。前端会展示一个新增商品的模态框供农户输入商品的基本信息,然后将输入的商品信息传递给Good/CreateOrEdit接口,增加商品数据。请求成功后,前端关闭模态框。并执行商品列表查询接口,查询出最新的商品列表。我的商品界面,如图所示。

关键代码:

public GoodPropsDto Get(GoodPropsPagedInput input) {
   if(input.getId()==null)
    {
        return new GoodPropsDto();
    }
    PagedResult<GoodPropsDto>  pagedResult =List(input);
    return pagedResult.getTotalCount()>0?pagedResult.getItems().stream().findFirst().get():new GoodPropsDto();
}

2.5 库存分析

农户的库存分析,使用ECharts 图表展示农产品库存Top10的数据。前端通过调用/OrderInfo/InventoryAnalyse,从后端获取数据。option 对象定义了 ECharts 图表的配置项。title: 设置了图表的标题为“农场品库存Top10”。legend: 设置了图例的位置和属性。xAxis: 定义了 x 轴的类型为“category”(分类),并使用 Data 数组中的 name 属性来设置轴标签。toolbox: 提供了图表工具箱,用户可以保存图表为图片、恢复原始配置或查看数据视图。yAxis: 定义了 y 轴的类型为“value”(数值)。series: 定义了数据系列,这里只有一个类型为“line”(折线)的系列。数据来自 Data 数组中的 value 属性,线条颜色被设置为黄色。库存分析界面,如图所示。

关键代码:

List<Good> list = _GoodMapper.selectList(null).stream().toList();
if (input.getShopIds() != null) {
    list = list.stream().filter(x -> input.getShopIds().contains(x.getShopId())).toList();
}
for (Good good : list) {
    Map<String, Object> data = new HashMap<>();
    data.put("name", good.getName());
    data.put("value", good.getStock());
    dataList.add(data);
}

3 管理员模块

3.1 农产品资讯管理

农产品资讯管理,前端使用 Vue.js 表单包含三个 el-form-item 组件,分别用于输入标题、选择浏览数范围和选择资讯类型。当管理员点击搜索按钮时,前端会触发TableSearch()方法,处理表格搜索的逻辑。将this.searchForm的数据被传递给 reload 方法。前端再调用/Topic/List方法。获取资讯列表。农产品资讯界面如图所示。

关键代码:

//查询出关联的TopicType表信息
TopicTypeDto TopicTypeDTO = new TopicTypeDto();
TopicType TopicTypeEntity = _TopicTypeMapper.selectOne(Wrappers.<TopicType>lambdaQuery().eq(TopicType::getId, item.getTopicTypeId()));
if (TopicTypeEntity != null) {
    BeanUtils.copyProperties(TopicTypeDTO, TopicTypeEntity);
    item.setTopicTypeDto(TopicTypeDTO);
}

3.2 农产品管理

管理员主要对农户添加的商品进行审核或者下架不符合要求的商品。前端展示出一个修改的模态框,管理员可以对商品进行审核,调用后端方法Good/CreateOrEdit,更改商品的审核状态。随后前端会自动关闭模态框。农产品资讯界面如图所示。

关键代码:

//按创建时间从大到小排序最新的显示在最前面
queryWrapper = queryWrapper.orderByDesc(Good::getCreationTime);
//构建一个分页查询的model
Page<Good> page = new Page<>(input.getPage(), input.getLimit());
//从数据库进行分页查询获取商品数据
IPage<Good> pageRecords = _GoodMpper.selectPage(page, queryWrapper);
//获取所有满足条件的数据行数
Long totalCount = _GoodMpper.selectCount(queryWrapper);
//把Good实体转换成Good传输模型
List<GoodDto> items = Extension.copyBeanList(pageRecords.getRecords(), GoodDto.class);
for (GoodDto item : items) {
    GoodInfoProcess(item);
}

3.3 优惠券管理

前端展示优惠券的名称、封面、金额、支付限制等基本信息。进入页面,直接通过post请求调用/Volumes/List接口,请求数据,将后端返回的数据通过前端vue进行渲染到界面。优惠券界面如图所示。

关键代码:

IPage<Volumes> pageRecords = _VolumesMpper.selectPage(page, queryWrapper);
Long totalCount = _VolumesMpper.selectCount(queryWrapper);
List<VolumesDto> items = Extension.copyBeanList(pageRecords.getRecords(), VolumesDto.class);
for (VolumesDto item : items) {
    AppUserDto CreatorAppUserDTO = new AppUserDto();
    AppUser CreatorAppUserEntity = _AppUserMapper.selectOne(Wrappers.<AppUser>lambdaQuery().eq(AppUser::getId, item.getCreatorId()));

3.4 店铺管理

管理员通过店铺管理,对农户的开店申请进行审核。显示所有开店申请的店铺名称、封面、账号、执业执照等信息列表。允许管理员通过关键词方式搜索和筛选开店申请。审核操作后,前端需要实时显示审核状态的变化,并给出相应的提示信息。店铺管理界面如图所示。

关键代码:

String Content = "";
if (input.getAuditStatus() == Enums.AuditStatus.审核通过.index()) {
    Content = "恭喜您,你的店铺" + input.getName() + "通过了系统的审核!,您可以正常完成账号信息的填写了,具体的链接为[http://localhost:9528/#/Register?ShopId=" + input.getId()+"]";
}
if (input.getAuditStatus() == Enums.AuditStatus.审核失败.index()) {
    Content = "很抱歉,您的店铺" + input.getName() + "未通过系统的审核,具体原因为:" + input.getAuditRemark();
}
EmailUtil.sendTextMail(input.getRegisterEmail(), "开店审核结果通知", Content);
三.需求分析

三.需求分析

1.客户

(1) 查询农产品:客户可以根据农产品的名称等关键字进行搜索,快速找到所需的农产品。搜索结果展示农产品的详细信息,包括图片、库存、售价等,方便客户查看。

(2) 加入购物车:客户可以将感兴趣的农产品加入购物车。购物车支持修改数量、删除商品等操作,客户可以调整购买商品的清单。点击购物车结算功能,客户可以选择支付方式完成购买。

(3) 农产圈交流:农产圈交流区,客户可以在这里发布自己的农产品信息、经验分享、问题求助等。其他客户可以浏览、评论和互动,形成一个活跃的农产品交流社区。

(4) 浏览通知公告:平台发布的重要通知、活动信息、政策变动等都会在通知公告区展示。客户可以随时浏览这些公告,了解平台的最新动态。

(5) 我的聊天:为了方便客户与客服人员进行即时沟通,解决他们在使用产品或服务过程中遇到的任何问题。通过“我的聊天”,客户可以轻松地与客服建立起联系,无论是询问产品详情、寻求技术支持,都能得到快速而有效的响应。

(6) 个人中心功能:

① 收藏话题资讯:客户可以收藏感兴趣的农产品话题资讯。

② 收货地址维护:客户可以添加、修改和删除收货地址。

③ 我的优惠券:展示客户领取的优惠券信息,包含是否使用,使用的订单等信息。

④ 我的订单:客户可以查看自己的订单状态以及订单明细,支持客户取消订单。

⑤ 我的购物车:展示客户购物车中的商品信息,方便客户随时查看和修改。

⑥ 我的资讯:客户可以查看自己发布的资讯,进行修改和删除。

2.管理员

(1) 客户管理:管理员管理客户的账户信息。查看客户资料、编辑客户权限以及在必要时删除不符合标准的账户。

(2) 通知公告管理:管理员可以发布、编辑和删除系统的通知和公告,确保客户可以及时获取到最新的系统更新、政策变更或其他重要信息。

(3) 农产品资讯管理:管理员可以添加、更新或删除农产品相关的资讯和文章,为客户提供丰富的农产品知识,帮助他们了解市场动态、种植技巧等有用信息。

(4) 店铺管理:管理员可以利用这个功能对接入平台的店铺进行管理,包括审核新店铺的入驻申请、更新店铺信息、监控店铺的销售行为等。

(5) 农产品管理:允许管理员对平台上销售的农产品进行审核,确保农产品信息的合规性。

(6) 订单管理:支持管理员查看订单详情、删除订单。

(7) 广告封面管理:管理员可以设计和更换平台的广告封面,以吸引客户注意力,提升平台的专业形象和客户体验。

(8) 优惠券管理:管理员可以创建、分发和管理优惠券,包括设置优惠券的使用条件、有效期和折扣额度,以此来激励客户消费,增加销售量。

3.农户

(1) 实时聊天:允许农户与顾客进行实时沟通。无论是解答顾客的疑问,还是提供售后服务,实时聊天都能确保农户与顾客之间的沟通无障碍,从而提高顾客满意度和购物体验。

(2) 我的店铺管理:允许农户对自己的在线店铺进行全面管理。包括店铺的店铺名称、地址、封面、店铺介绍等基本信息设置。

(3) 我的商品管理:农户可以为店铺添加新商品、编辑现有商品信息,或者删除不再销售的商品。农户还可以设置商品的库存量、价格、商品介绍等信息,确保商品信息的准确与及时更新。

(4) 我的订单管理:农户可以在这里查看所有订单的状态,包括待发货、已发货和已完成、评价完成以及取消的订单。农户可以对订单进行发货操作,确保订单处理的高效与准确。

(5) 库存分析:可帮助农户跟踪商品的库存情况,预测库存需求,避免库存积压或缺货的情况发生。通过数据分析,农户可以更好地掌握库存动态,优化库存管理。

(6) 最近7天销量分析:帮助农户了解过去一周的销售情况。通过分析哪些商品销售良好,哪些不畅销,农户可以调整销售策略,提高销售效率。

(7) 最近30天销量分析:提供了一个月的销售数据,帮助农户从宏观角度把握销售趋势。农户可以根据这些数据来制定长期的销售计划和市场策略。

(8) 助农话题交流:农户可以通过参与这个话题,与客户进行交流互动,发布农产品的专业知识,为客户提供专业的知识见解。可以扩大自己的销售额,促进农村经济的发展。

;