最近把java升级到了java21的版本,就使用springboot框架写了一个二手交易平台来熟悉一下springBoot框架的语法。功能方面主要做了管理后台和手机用户端的功能。
管理系统后台的样式:
手机端:
主要做的功能有:
(1)平台管理端
· 用户管理
· 商品分类管理
· 二手商品管理
· 订单管理
· 管理员管理
· 资讯管理
(2)用户端
· 二手商品展示
· 分类筛选
· 商品详情
· 订单凭证与我的订单
· 联系卖家
· 资讯查看
· 我的发布
主要练习的后端技术是:springboot2.7 + mybatisplus
使用的数据库:mysql8.0
前端使用的 vue2 + Element UI
手机端使用的是 uni-app框架 也是可以打包小程序的!
node版本:16.20
后端管理员管理部分代码:
bash
package com.jsonll.base.controller;
import com.jsonll.base.core.NoLogin;
import com.jsonll.base.core.R;
import com.jsonll.base.entity.Admin;
import com.jsonll.base.request.AdminRequest;
import com.jsonll.base.service.IAdminService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* 管理员控制器
*/
@RestController
@RequestMapping("/admin")
public class AdminController extends BaseController {
@Autowired
private IAdminService iAdminService;
/**
* 管理员登录
*/
@PostMapping("/login")
@NoLogin
public R login(@RequestBody AdminRequest request) {
return iAdminService.login(request);
}
/**
* RESTful: 获取管理员列表(不分页)
*/
@GetMapping("")
public R list() {
// 直接使用 MyBatis-Plus 提供的 list 方法
java.util.List<Admin> list = iAdminService.list();
// 隐藏密码
list.forEach(item -> item.setPassword(null));
return R.successData(list);
}
/**
* RESTful: 获取单个管理员详情
*/
@GetMapping("/{id}")
public R get(@PathVariable Integer id) {
Admin admin = iAdminService.getById(id);
if (admin == null) {
return R.error("管理员不存在");
}
admin.setPassword(null);
return R.successData(admin);
}
/**
* 获取管理员列表
*/
@GetMapping("/pageList")
public R pageList(AdminRequest adminRequest) {
return iAdminService.pageList(adminRequest);
}
/**
* 添加管理员
*/
@PostMapping("")
public R add(@RequestBody Admin admin) {
return iAdminService.add(admin);
}
/**
* 更新管理员信息
*/
@PutMapping("/{id}")
public R updateAdmin(@PathVariable Integer id, @RequestBody Admin admin) {
// 路径参数为准
admin.setId(id);
return iAdminService.updateAdmin(admin);
}
/**
* 删除管理员
*/
@DeleteMapping("/{id}")
public R deleteAdmin(@PathVariable Integer id) {
return iAdminService.deleteAdmin(id);
}
/**
* 获取当前登录用户信息
*/
@GetMapping("/userInfo")
public R userInfo() {
Integer userId = getLoginUserId();
return iAdminService.userInfo(userId);
}
}
bash
package com.jsonll.base.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jsonll.base.core.R;
import com.jsonll.base.entity.Admin;
import com.jsonll.base.mapper.AdminMapper;
import com.jsonll.base.request.AdminRequest;
import com.jsonll.base.utils.JwtHelper;
import com.jsonll.base.utils.MD5;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
/**
* 管理员表 接口实现类
*/
@Service
public class AdminServiceImpl extends ServiceImpl<AdminMapper, Admin> implements IAdminService {
@Override
public R login(AdminRequest request) {
// 参数校验
if (StringUtils.isEmpty(request.getUsername()) || StringUtils.isEmpty(request.getPassword())) {
return R.error("用户名或密码不能为空");
}
// 查询条件 示例
LambdaQueryWrapper<Admin> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Admin::getUsername, request.getUsername());
Admin admin = getOne(queryWrapper);
// 用户不存在
if (admin == null) {
return R.error( "用户名或密码错误");
}
// 密码校验
String encryptedPassword = MD5.encoder(request.getPassword());
if (!admin.getPassword().equals(encryptedPassword)) {
return R.error( "用户名或密码错误");
}
// 生成token
String token = JwtHelper.sign(admin.getId());
// 给实体类字段赋值
admin.setToken(token);
return R.successData(admin);
}
@Override
public R userInfo(Integer userId) {
//根据id查询示例
Admin admin = getById(userId);
if (admin == null) {
return R.error("未查询到数据");
}
return R.successData(admin);
}
@Override
public R pageList(AdminRequest adminRequest) {
//分页示例
Page<Admin> page = new Page<>(adminRequest.getPageNum() == null ? 1 : adminRequest.getPageNum(), adminRequest.getPageSize() == null ? 10 : adminRequest.getPageSize());
QueryWrapper<Admin> queryWrapper = new QueryWrapper<>();
//搜索条件 示例
if(!StringUtils.isEmpty(adminRequest.getSearch())){
queryWrapper.lambda().like(Admin::getUsername, adminRequest.getSearch());
}
page(page, queryWrapper.lambda().orderByDesc(Admin::getId));
// 处理返回结果,隐藏密码
List<Admin> records = page.getRecords();
//列表回显 示例
records.forEach(admin->{
admin.setPassword(null);
//列表实体类 回显示例
admin.setEchoExample("回显示例");
});
return R.successData(page);
}
@Override
public R add(Admin admin) {
// 参数校验
if (StringUtils.isEmpty(admin.getUsername()) || StringUtils.isEmpty(admin.getPassword())) {
return R.error("用户名或密码不能为空");
}
// where条件示例
LambdaQueryWrapper<Admin> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Admin::getUsername, admin.getUsername());
if (count(queryWrapper) > 0) {
return R.error( "用户名已存在");
}
// 设置默认值
admin.setIsSuper(0); // 默认为普通管理员
// 密码加密
admin.setPassword(MD5.encoder(admin.getPassword()));
// 保存管理员信息 示例
save(admin);
//无返回数据 示例
return R.success();
}
@Override
public R updateAdmin(Admin admin) {
// 参数校验
if (admin.getId() == null) {
return R.error( "管理员ID不能为空");
}
// 获取原管理员信息
Admin existAdmin = getById(admin.getId());
if (existAdmin == null) {
return R.error("管理员不存在");
}
// 不允许修改超级管理员状态
if (existAdmin.getIsSuper() == 1) {
admin.setIsSuper(1);
}
// 如果修改了用户名,检查是否已存在
if (!StringUtils.isEmpty(admin.getUsername()) && !admin.getUsername().equals(existAdmin.getUsername())) {
LambdaQueryWrapper<Admin> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Admin::getUsername, admin.getUsername());
if (count(queryWrapper) > 0) {
return R.error( "用户名已存在");
}
}
// 如果密码为空,使用原密码
if (StringUtils.isEmpty(admin.getPassword())) {
admin.setPassword(existAdmin.getPassword());
} else {
// 密码加密
admin.setPassword(MD5.encoder(admin.getPassword()));
}
// 根据id更新管理员信息 示例
updateById(admin);
return R.success();
}
@Override
public R deleteAdmin(Integer id) {
// 参数校验
if (id == null) {
return R.error( "管理员ID不能为空");
}
// 获取管理员信息
Admin admin = getById(id);
if (admin == null) {
return R.error("管理员不存在");
}
// 不允许删除超级管理员
if (admin.getIsSuper() == 1) {
return R.error( "不能删除超级管理员");
}
// 删除管理员 示例
removeById(id);
return R.success();
}
}
手机端代码:
bash
<template>
<view class="my-container">
<!-- 用户信息区域 -->
<view class="user-info" v-if="isLogin">
<view class="avatar-box">
<image class="avatar" :src="userInfo.avatar || '/static/default-avatar.png'" mode="aspectFill"></image>
</view>
<view class="user-detail">
<view class="nickname">{{userInfo.nickname || '未设置昵称'}}</view>
<view class="auth-status">
<text :class="['auth-tag', userInfo.auth_status === 1 ? 'auth-yes' : 'auth-no']">
{{userInfo.auth_status === 1 ? '已认证' : '未认证'}}
</text>
</view>
</view>
</view>
<!-- 未登录状态 -->
<view class="user-info not-login" v-else @click="goToLogin">
<view class="avatar-box">
<image class="avatar" src="/static/default-avatar.png" mode="aspectFill"></image>
</view>
<view class="user-detail">
<view class="nickname">点击登录</view>
</view>
</view>
<!-- 功能菜单 -->
<view class="menu-list">
<view class="menu-group">
<view class="menu-item" @click="handleMenuClick('myPublish')">
<text class="iconfont icon-publish"></text>
<text class="menu-text">我的发布</text>
<text class="iconfont icon-right"></text>
</view>
<view class="menu-item" @click="handleMenuClick('myOrders')">
<text class="iconfont icon-order"></text>
<text class="menu-text">我的订单</text>
<text class="iconfont icon-right"></text>
</view>
</view>
<view class="menu-group">
<view class="menu-item" @click="handleMenuClick('news')">
<text class="iconfont icon-news"></text>
<text class="menu-text">公告资讯</text>
<text class="iconfont icon-right"></text>
</view>
<view class="menu-item" @click="handleMenuClick('authenticate')" v-if="isLogin">
<text class="iconfont icon-auth"></text>
<text class="menu-text">我的认证</text>
<text class="iconfont icon-right"></text>
</view>
</view>
<view class="menu-group" v-if="isLogin">
<view class="menu-item" @click="handleMenuClick('logout')">
<text class="iconfont icon-logout"></text>
<text class="menu-text">退出登录</text>
<text class="iconfont icon-right"></text>
</view>
</view>
<view class="menu-group" v-else>
<!-- <view class="menu-item" @click="handleMenuClick('register')">
<text class="iconfont icon-register"></text>
<text class="menu-text">注册</text>
<text class="iconfont icon-right"></text>
</view> -->
<view class="menu-item" @click="handleMenuClick('login')">
<text class="iconfont icon-login"></text>
<text class="menu-text">登录</text>
<text class="iconfont icon-right"></text>
</view>
</view>
</view>
</view>
</template>
<script>
import { getCurrentUser } from '@/api/user.js'
export default {
data() {
return {
isLogin: false,
userInfo: {}
}
},
onShow() {
// 每次显示页面时检查登录状态
this.checkLoginStatus()
},
onLoad() {
// 页面加载时立即检查登录状态,如果未登录则跳转到登录页
const token = uni.getStorageSync('token')
if (!token) {
uni.navigateTo({
url: '/pages/login/login'
})
}
},
methods: {
// 检查登录状态
checkLoginStatus() {
const token = uni.getStorageSync('token')
if (token) {
this.isLogin = true
this.getUserInfo()
} else {
this.isLogin = false
this.userInfo = {}
}
},
// 获取用户信息
getUserInfo() {
getCurrentUser().then(res => {
if (res.code === 1000) {
this.userInfo = res.data
}
}).catch(() => {
this.isLogin = false
this.userInfo = {}
})
},
// 跳转到登录页
goToLogin() {
uni.navigateTo({
url: '/pages/login/login'
})
},
// 菜单点击处理
handleMenuClick(type) {
switch(type) {
case 'myPublish':
// 检查登录状态
if (!this.isLogin) {
uni.showToast({
title: '请先登录',
icon: 'none'
})
this.goToLogin()
return
}
// 跳转到我的发布页面
uni.navigateTo({
url: '/pages/my/published'
})
break
case 'myOrders':
// 检查登录状态
if (!this.isLogin) {
uni.showToast({
title: '请先登录',
icon: 'none'
})
this.goToLogin()
return
}
// 跳转到我的订单页面
uni.navigateTo({
url: '/pages/my/orders'
})
break
case 'news':
uni.navigateTo({
url: '/pages/news/list'
})
break
case 'authenticate':
// 跳转到认证页面
uni.navigateTo({
url: '/pages/my/authenticate'
})
break
case 'register':
uni.showToast({
title: '注册功能开发中',
icon: 'none'
})
break
case 'login':
this.goToLogin()
break
case 'logout':
this.handleLogout()
break
}
},
// 退出登录
handleLogout() {
uni.showModal({
title: '提示',
content: '确定要退出登录吗?',
success: (res) => {
if (res.confirm) {
uni.removeStorageSync('token')
this.isLogin = false
this.userInfo = {}
uni.showToast({
title: '已退出登录',
icon: 'success'
})
}
}
})
}
}
}
</script>
<style lang="scss">
.my-container {
min-height: 100vh;
background-color: #f5f5f5;
.user-info {
display: flex;
align-items: center;
padding: 30rpx;
background-color: #ffffff;
&.not-login {
opacity: 0.8;
}
.avatar-box {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
overflow: hidden;
margin-right: 30rpx;
.avatar {
width: 100%;
height: 100%;
background-color: #eeeeee;
}
}
.user-detail {
flex: 1;
.nickname {
font-size: 32rpx;
font-weight: bold;
margin-bottom: 10rpx;
}
.auth-status {
.auth-tag {
display: inline-block;
font-size: 24rpx;
padding: 4rpx 12rpx;
border-radius: 20rpx;
&.auth-yes {
background-color: #e6f7ff;
color: #1890ff;
}
&.auth-no {
background-color: #fff7e6;
color: #fa8c16;
}
}
}
}
}
.menu-list {
margin-top: 20rpx;
.menu-group {
background-color: #ffffff;
margin-bottom: 20rpx;
.menu-item {
display: flex;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
.iconfont {
font-size: 40rpx;
color: #666;
margin-right: 20rpx;
&.icon-right {
margin-right: 0;
margin-left: auto;
font-size: 32rpx;
color: #999;
}
}
.menu-text {
font-size: 28rpx;
color: #333;
}
}
}
}
}
</style>

如果你刚开始学习Java,并且想通过实践提升自己,可以参考这篇文章,尝试模仿并独立完成一个类似的项目。
把项目部署了一个预览地址,方便大家参考。
https://test.wwwoop.com/?s=/er-shou-ping-tai-web&no=Second-hand-TP003&rand=0.6679227765871157