尧图精选

SpringBoot+Vue助农电商平台毕设实战:从数据库设计到部署避坑

🕒 发布时间:2026/10/2 14:36:33 📁 来源:尧图网络
简介一份基于SpringBootVue的助农电商平台完整项目源码主要面向高校毕业设计、课程作业以及Java全栈学习者同时适用于希望了解电商系统前后端分离实现路径的开发者。平台以农产品销售场景为核心涵盖商品展示、用户管理、订单处理等业务模块后端基于SpringBoot的“约定优于配置”特性降低搭建门槛并通过数据库表结构设计保障产品分类、用户信息、订单数据等的一致性前端使用Vue构建动态交互界面代码划分为client_code、manage_code、server_code等目录清晰体现客户端、管理端与服务端逻辑分离便于独立部署和二次开发。压缩包共375个文件约10.89MB以java、vue、js、css等源码为主并包含png/jpg界面截图、xml配置文件、sql数据库脚本以及“有问题请先读我.txt”常见问题文档可帮助读者快速完成从环境搭建到功能调试的全流程学习。已有162人学习下载适合作为毕业设计参考、课程项目模板或农贸电商入门实战案例在现有基础上扩展营销、支付等模块也较为方便。1. 助农电商平台先想清楚它在毕设里到底考什么“助农电商平台”这类题目在 Java 课程设计和毕业设计里出现频率很稳定。表面看它是从 0 到 1 写一套电商系统实际上核心工作量集中在三块角色权限拆分、商品-订单数据模型、前后端联调。如果你现在打开源码包一头扎进代码大概率三天后还在改接口路径。建议先看数据表再看后端接口最后打开前端这套顺序能帮你把“助农”这个大命题收敛成七个表加二十个接口的工程题。适合正在做毕设的 Java 学生也适合想用 SpringBoot Vue 刷一遍电商流程的开发者。2. 需求拆解与数据库设计把“助农”收敛成七个核心表拿到基于 SpringBoot Vue 的助农电商项目我最建议先看数据库脚本。因为电商项目里接口可以临时补字段关系错了回改成本极高。如果资源包里没带sql文件按下面这套结构建出来后面写接口会非常顺。2.1 角色划分与权限边界助农平台天然有三类人管理员、农户、普通用户。角色设计直接决定后端接口要不要做权限校验也决定前端路由怎么拦截。下表是我在毕设里常用的权限边界角色主要操作接口前缀管理员用户管理、商品审核、订单查看、数据统计/api/admin/**农户商品发布、商品编辑、订单发货/api/farmer/**普通用户浏览商品、加购、下单、查看订单/api/user/**用户表里的角色字段建议直接存整型而不是字符串role tinyint(1) NOT NULL DEFAULT 2 COMMENT 0-管理员 1-农户 2-普通用户为什么不用字符串前端if (user.role 1)判断最直接数据库体积也小。如果答辩时导师问角色扩展注释里已经写了“预留扩展位”一句就能解释清楚。这体量的项目没必要建角色表和用户-角色关联表那套东西留到 SSM 老项目里反而显得臃肿。2.2 核心表结构与建表脚本我梳理出的七个核心表是用户表sys_user、分类表category、商品表product、购物车表cart、订单表order_info、订单明细表order_item、收货地址表address。下面给出最关键的四个表建表脚本分类和地址表结构简单按外键思路补就行CREATE TABLE sys_user ( id bigint(20) NOT NULL AUTO_INCREMENT, username varchar(32) NOT NULL COMMENT 登录名, password varchar(128) NOT NULL COMMENT 加密后的密码, nickname varchar(32) DEFAULT NULL COMMENT 昵称/农户名称, role tinyint(1) NOT NULL DEFAULT 2 COMMENT 0-管理员 1-农户 2-普通用户, phone varchar(11) DEFAULT NULL, avatar varchar(255) DEFAULT NULL COMMENT 头像地址, status tinyint(1) NOT NULL DEFAULT 1 COMMENT 0-禁用 1-正常, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE product ( id bigint(20) NOT NULL AUTO_INCREMENT, name varchar(64) NOT NULL COMMENT 农产品名称, category_id bigint(20) NOT NULL COMMENT 分类ID, price decimal(10,2) NOT NULL COMMENT 单价(元), stock int(11) NOT NULL DEFAULT 0 COMMENT 库存, unit varchar(8) DEFAULT 斤 COMMENT 售卖单位, cover varchar(255) NOT NULL COMMENT 主图地址, images text COMMENT 轮播图JSON数组, detail text COMMENT 富文本详情, status tinyint(1) NOT NULL DEFAULT 0 COMMENT 0-下架 1-上架 2-待审核, farmer_id bigint(20) NOT NULL COMMENT 所属农户ID, deleted tinyint(1) NOT NULL DEFAULT 0 COMMENT 逻辑删除, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE order_info ( id bigint(20) NOT NULL AUTO_INCREMENT, order_no varchar(32) NOT NULL COMMENT 订单号, user_id bigint(20) NOT NULL, total_amount decimal(10,2) NOT NULL COMMENT 订单总金额, status tinyint(1) NOT NULL DEFAULT 0 COMMENT 0待付款 1待发货 2待收货 3已完成 4已取消, receiver_name varchar(32) NOT NULL, receiver_phone varchar(11) NOT NULL, receiver_address varchar(255) NOT NULL, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE order_item ( id bigint(20) NOT NULL AUTO_INCREMENT, order_id bigint(20) NOT NULL, product_id bigint(20) NOT NULL, product_name varchar(64) NOT NULL COMMENT 下单快照, product_cover varchar(255) DEFAULT NULL, price decimal(10,2) NOT NULL COMMENT 下单单价, count int(11) NOT NULL DEFAULT 1, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里有两个设计容易忽略order_info里冗余了收件人姓名、电话、地址而收货地址表只保留常用地址这样订单独立成档不受地址表修改影响order_item里存了product_name和product_cover快照商品后来改名或换图历史订单里的显示不会被带偏这是答辩时能拿出来讲的数据冗余理由。2.3 字段类型与通用坑这类毕设项目最常见的三类坑在建表时就能避开。金额一律用decimal(10,2)绝对不要用double浮点运算在累计金额时会差出分单位时间字段统一用datetimeJava 端对应LocalDateTime配合 MyBatis-Plus 的自动填充省掉手写setCreateTime商品删除用deleted字段做逻辑删除而不是DELETE FROM product电商项目最忌讳物理删数据留下痕迹在答辩里就是加分项。状态字段也要养成写注释的习惯。比如商品状态0下架 1上架 2待审核、订单状态0待付款等数据库里注释写清楚后面写switch或者前端枚举判断时不用再猜。分类表可以预置几条数据水果、蔬菜、粮油、禽蛋农户上架商品时从下拉里选不让农户手输分类数据质量会干净很多。3. 后端实现SpringBoot 接口设计与关键代码后端部分我按一个标准的前后端分离项目来拆。这套接口设计不只是“能跑”还要让答辩老师觉得你是理解工程化的而不是把代码堆在一个 Controller 里。3.1 服务端分层与包结构我常用的分层是controller / service / mapper / entity / common / config。Controller 只做参数接收和结果包装Service 写业务逻辑Mapper 只碰 SQL。商品、订单、用户这些模块各自建一个包避免出现十来个 Controller 塞在同一个目录下的情况。src/main/java/com/qianhong/platform/ ├── controller/ # 接口入口只做参数解析与结果返回 ├── service/ # 业务逻辑层 ├── mapper/ # MyBatis-Plus 的 Mapper 接口 ├── entity/ # 数据库表对应的实体类 ├── common/ # Result、异常、常量、工具类 └── config/ # JWT、MyBatis-Plus、静态资源等配置分层清晰的项目后端翻代码时只要看 Service 就能定位业务逻辑。比如下单接口Controller 只有两行真正的库存扣减、订单生成都在OrderServiceImpl里导师问起“异常了怎么办”你直接说Transactional加在实现类方法上就行。3.2 登录鉴权JWT 拦截器这个体量的项目上 Spring Security 太重配置类一大堆调试成本高。我一般用 Hutool 的 JWT 工具做登录鉴权配合一个 HandlerInterceptor代码量小讲解也直观。先写一个 JWT 工具类public class JwtHelper { private static final byte[] KEY qi-nong-platform.getBytes(); public static String createToken(Long userId, Integer role) { return JWT.create() .setKey(KEY) .setPayload(userId, userId) .setPayload(role, role) .setExpiresAt(new Date(System.currentTimeMillis() 7 * 24 * 3600 * 1000L)) .sign(); } public static JWT parseToken(String token) { JWT jwt JWTUtil.parseToken(token); if (!jwt.setKey(KEY).verify()) { throw new BusinessException(401, token 校验失败); } return jwt; } }createToken里设置了 7 天有效期毕设演示期间基本够用KEY是签名密钥源码包里如果给了配置项上线时一定要换成环境变量别把密钥硬编码提交到仓库。parseToken里先解析再校验签名校验失败统一抛 401由全局异常处理器转成 JSON 返回。登录接口和拦截器这样配合RestController RequestMapping(/api/auth) public class AuthController { Resource private UserService userService; PostMapping(/login) public Result login(RequestBody LoginDTO dto) { User user userService.login(dto.getUsername(), dto.getPassword()); if (user null) { return Result.error(用户名或密码错误); } String token JwtHelper.createToken(user.getId(), user.getRole()); return Result.ok().put(token, token).put(userInfo, user); } }Component public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String token request.getHeader(Authorization); if (StrUtil.isBlank(token)) { throw new BusinessException(401, 未登录); } JWT jwt JwtHelper.parseToken(token); request.setAttribute(userId, Long.valueOf(jwt.getPayload(userId).toString())); request.setAttribute(role, jwt.getPayload(role)); return true; } }Configuration public class WebConfig implements WebMvcConfigurer { Resource private LoginInterceptor loginInterceptor; Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns(/api/**) .excludePathPatterns(/api/auth/**, /api/product/list); } }拦截器里把userId和role放进了 request 属性Controller 里直接(Long) request.getAttribute(userId)拿当前用户不用每个接口再解析一次 token。excludePathPatterns放行登录接口和商品列表这两个是所有用户都能访问的其余接口默认都要 token。需要按角色控制的接口比如农户发布商品在 Controller 里加一行角色判断或者用自定义注解效果都比想象中好讲。3.3 商品列表分页与条件查询商品列表是前端首页的主力接口涉及分页和条件筛选。MyBatis-Plus 的分页插件在这个场景里几乎是标配先注册插件Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }注意如果你用的是 SpringBoot 3.x要引入mybatis-plus-spring-boot3-starter而不是老的mybatis-plus-boot-starter否则启动会直接报 Bean 找不到。这是版本升级后最经典的一个坑。分页查询代码Override public PageProduct pageProducts(int pageNum, int pageSize, String keyword, Long categoryId) { PageProduct page new Page(pageNum, pageSize); LambdaQueryWrapperProduct wrapper new LambdaQueryWrapperProduct() .eq(Product::getStatus, 1) .like(StrUtil.isNotBlank(keyword), Product::getName, keyword) .eq(categoryId ! null, Product::getCategoryId, categoryId) .orderByDesc(Product::getCreateTime); return productMapper.selectPage(page, wrapper); }pageNum从 1 开始pageSize默认 10前端传参时我用current和size做映射避免语义混乱。LambdaQueryWrapper的like条件带上了keyword是否为空判断为空时该条件不拼进 SQL比手动拼字符串安全得多也天然防住了模糊查询的注入风险。status 1写死为“上架”保证前台用户永远看不到审核中和下架商品。3.4 下单流程与库存扣减下单是电商项目的核心事务。我推荐一个既简单又能讲清楚的方案Transactional保证订单、明细、库存扣减要么全部成功要么全部回滚库存扣减用条件更新不先查询再更新从源头避开超卖问题。Transactional(rollbackFor Exception.class) public Long createOrder(OrderCreateDTO dto, Long userId) { String orderNo QH System.currentTimeMillis() RandomUtil.randomNumbers(4); OrderInfo order new OrderInfo(); order.setOrderNo(orderNo); order.setUserId(userId); order.setStatus(0); order.setTotalAmount(dto.getTotalAmount()); order.setReceiverName(dto.getReceiverName()); order.setReceiverPhone(dto.getReceiverPhone()); order.setReceiverAddress(dto.getReceiverAddress()); orderMapper.insert(order); for (OrderItemDTO item : dto.getItems()) { int rows productMapper.deductStock(item.getProductId(), item.getCount()); if (rows 0) { throw new BusinessException(商品库存不足或已下架); } OrderItem orderItem new OrderItem(); orderItem.setOrderId(order.getId()); orderItem.setProductId(item.getProductId()); orderItem.setProductName(item.getProductName()); orderItem.setProductCover(item.getProductCover()); orderItem.setPrice(item.getPrice()); orderItem.setCount(item.getCount()); orderItemMapper.insert(orderItem); } return order.getId(); }Update(UPDATE product SET stock stock - #{count} WHERE id #{productId} AND stock #{count} AND status 1) int deductStock(Param(productId) Long productId, Param(count) int count);这里重点说两个细节。订单号用“前缀 时间戳 4 位随机数”比 UUID 短得多用户和后台都愿意看扣库存的 SQL 带了stock #{count}数据库层面保证扣减后库存不为负如果这条 SQL 影响行数为 0说明库存不足或者商品已下架业务直接抛异常。Transactional(rollbackFor Exception.class)一定要写rollbackFor否则遇到非受检异常可能不回滚。这个知识点在面试里大概率被问到“数据库怎么保证数据一致性”这条 SQL 就是最直接的答案。4. 前端实现Vue 路由、状态与接口联调前端我用 Vue3 Vite 的写法来拆。源码包里可能是 Vue CLI 创建的老结构但页面组件和路由逻辑是相通的核心思路能直接平移。4.1 环境准备与项目初始化先把 Node 环境切到 18 LTS这是 Vue3 生态兼容性最好的版本。Node 20 以上跑老项目偶尔会报 OpenSSL 相关的 hash 错误降到 18 立刻正常。依赖安装顺序nvm use 18 cd 前端项目目录 npm install npm install element-plus axios pinia vue-router4 npm run develement-plus负责 UI 组件axios做 HTTP 请求pinia管理购物车、用户信息这类跨页面状态vue-router4是 Vue3 对应的路由版本。如果源码包里自带node_modules我一般直接删掉重装因为本机 Node 版本和依赖锁定版本对不上时报错很难定位。4.2 路由配置与页面结构页面和路由的对应关系如下路由页面组件说明/homeHome.vue首页商品列表/product/:idProductDetail.vue商品详情/cartCart.vue购物车/ordersOrderList.vue我的订单/loginLogin.vue登录/admin/goodsAdminGoods.vue管理员/农户商品管理路由配置加上登录守卫和角色判断const routes [ { path: /home, component: Home, meta: { title: 首页 } }, { path: /product/:id, component: ProductDetail }, { path: /cart, component: Cart, meta: { requiresAuth: true } }, { path: /admin/goods, component: AdminGoods, meta: { requiresAuth: true, role: 0 } }, { path: /login, component: Login } ] const router createRouter({ history: createWebHistory(), routes }) router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next(/login) } else if (to.meta.role ! undefined to.meta.role ! Number(localStorage.getItem(role))) { next(/home) } else { next() } })这里把本地存储的role和路由meta.role做比对管理员后台页面普通人进不去。调试路由跳转和守卫逻辑时Vue DevTools 插件要看一眼路由状态变化比在控制台瞎猜强得多。我用的是createWebHistory()历史模式URL 干净但部署后刷新会踩 404 的坑这个在第 5 章展开。4.3 Axios 封装与登录状态处理前端所有接口请求建议走一个统一封装的 Axios 实例。开发环境用 Vite 代理把/api转发到后端生产环境用 Nginx 转发前端代码里只写相对路径。const request axios.create({ baseURL: /api, timeout: 10000 }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization token } return config }) request.interceptors.response.use( res { if (res.data.code 200) return res.data if (res.data.code 401) { localStorage.removeItem(token) localStorage.removeItem(role) router.push(/login) return Promise.reject(new Error(登录已过期)) } return Promise.reject(new Error(res.data.msg)) }, err { return Promise.reject(err) } )token 我用localStorage存而不是sessionStorage因为用户刷新页面后登录状态不能丢。baseURL: /api是相对路径开发环境在vite.config.js里配 proxy 转发到http://localhost:8080这样前端写代码时不需要关心后端地址换环境只改代理配置。响应拦截器里对 401 统一处理清掉本地状态并跳到登录页这是第 5 章会说到的“接口报 401 页面却没反应”的正确解法。4.4 商品详情与购物车交互商品详情页要接收路由参数Vue Router 的用法是import { useRoute } from vue-router const route useRoute() const productId Number(route.params.id) onMounted(() { loadProductDetail(productId) })注意route.params.id拿到的类型是字符串请求后端前先转成 Number否则某些后端框架在类型转换上会出幺蛾子。广告位不想要可以用 div 做平替购物车状态我推荐用 Pinia 管理。前后端交互上购物车建议走后端接口而不是只存在前端内存里用户加购、改数量、删除都调用后端接口刷新页面后购物车数据还在。前端只保存一个cartCount用于角标展示import { defineStore } from pinia export const useCartStore defineStore(cart, { state: () ({ count: 0 }), actions: { async fetchCartCount() { const res await request.get(/api/cart/count) this.count res.data } } })这样一个 store 就能支撑整个项目的购物车角标。结算时把购物车里勾选的商品 ID 列表传给后端下单接口后端统一去查最新价格和库存避免前端传过来的价格被篡改。这个点答辩时主动说出来老师会认为你考虑到了“价格以服务端为准”的安全意识。5. 避坑与排查前后端分离项目最常见的五个翻车点这一章写的是我做这类项目反复踩过的坑每条都按“现象 → 原因 → 解决”说清楚能省下很多联调时间。5.1 跨域请求被拦截现象前端npm run dev启动在 5173 端口后端启动在 8080 端口浏览器控制台报 CORS error接口数据进不来。原因前后端分离项目两个端口不是同源浏览器默认拦截跨域 AJAX 请求。很多人一上来就在后端加CrossOrigin改来改去还是不稳定。解决开发环境用 Vite 代理解决这是最干净的方式。在vite.config.js里配置server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }前端请求/api/product/listVite 在开发服务器层面转发到http://localhost:8080/api/product/list浏览器看到的是同源请求根本不会触发跨域。生产环境用 Nginx 反向代理做同样的事。后端可以保留 CORS 配置兜底但不要依赖它。5.2 商品图片上传成功却访问不到现象农户在后台传图片接口返回成功数据库里也存了路径但前端img.src拼上路径后图片 404。原因图片保存到了本地磁盘目录比如D:/upload/xx.jpg但 SpringBoot 默认只映射classpath:/static/访问/upload/xx.jpg时根本没有对应处理器。解决在 WebConfig 里加静态资源映射Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceLocations(file: uploadDir); }uploadDir是图片存放的磁盘绝对路径。注意拼接时file:后要跟带斜杠的绝对路径Windows 和 Linux 写法不同这是最容易漏的地方。更进一步的做法是上 MinIO 做对象存储接口逻辑不变只是把落盘换成客户端毕设阶段先把本地映射配好就能跑通展示。5.3 登录状态失效后页面没跳转现象用户在那放着不动过了 7 天 token 过期再点“提交订单”接口报 401但页面没反应也没有跳回登录页。原因后端返回了 401但前端 Axios 响应拦截器里没有统一处理 401每个页面都要自己写判断漏一个接口就漏一个跳转。解决按 4.3 的做法在 Axios 响应拦截器里统一判断code 401清掉 token 和用户信息然后router.push(/login)。这样全项目只需要写一次。还有一个细节跳转登录页时要携带当前路由地址登录成功后回跳体验会好很多。5.4 数据库时间比本地早 8 小时现象后台添加商品列表里显示的create_time比北京时间早了 8 个小时。原因MySQL 连接串里的serverTimezone没设置驱动用了默认 UTC 时区。数据库本地时间没问题但 Java 拿到的值在序列化输出时被当成了 UTC。解决数据源连接串加上时区参数jdbc:mysql://localhost:3306/qi_nong?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai同时配置 Jackson 统一时间格式spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8这两处同时改完前后端显示的时间就一致了。这类时区问题最玄学因为数据库和 Java 进程本地看着都没问题只有拼在一起才暴露。5.5 打包部署后刷新子页面 404现象前端npm run build后部署到 Nginx访问首页正常但刷新/product/1或/orders页面时变成 404。原因前端是 Vue Router 的createWebHistory()模式路由是前端 JS 控制的Nginx 里没有对应/product/1这个真实文件请求落到了静态目录上找不到文件。解决Nginx 配置里加一条 try_fileslocation / { try_files $uri $uri/ /index.html; }try_files的含义是先找$uri对应的文件找不到就找目录再找不到直接回退到index.html由 Vue Router 接管路由。加了这一行刷新任何子路由都能正常回到前端路由逻辑。如果你不想碰 Nginx 配置也可以把路由模式改成createWebHashHistory()URL 里带#号部署就永远不会 404但 URL 不美观看你的取舍。6. 部署验证与排查技巧从本机跑通到上线可演示项目开发完到能演示中间还差一次正经的部署。这一章的步骤走完答辩演示时才能稳定不翻车。6.1 打包顺序与上线检查清单后端和前端各自打包mvn clean package -DskipTestsnpm run build后端产出一个jar包前端产出一个dist目录。部署到服务器时两个产物分别放好前端dist交给 Nginx后端jar用java -jar启动。我每次上线前强制走一遍这个清单确认后端application-prod.yml里的数据库地址、账号密码是生产环境的不是本机的 localhost。确认服务器 MySQL 里执行过初始化 SQL表结构和预置数据都在。确认 Nginx 的/api代理指向后端实际端口。启动后先curl http://localhost:8080/api/auth/login验证后端口活通再去浏览器刷新前端页面。顺手把 SpringBoot 默认的 Banner 换成项目 LOGO终端启动时一眼能看到服务起来了这个细节答辩现场印象很深。6.2 一个排查技巧把 jar 当目录看上线后如果接口行为不对劲不要急着反编译整个项目。jar 就是个 zip先把它当目录列出来看jar tf qianhong-platform.jar | grep applicationunzip -p qianhong-platform.jar BOOT-INF/classes/application-prod.ymljar tf列出 jar 里所有文件grep application过滤出配置文件unzip -p直接把文件内容打印到终端不用解压、不用反编译。我遇到过好几次本地跑得好好的一上线就报数据库连不上查了一圈发现是application-prod.yml里数据源配的还是内网地址。用这两条命令10 秒就能定位是不是打进了错误的配置。这个技巧比用 IDE 反编译整个 jar 快得多排查线上问题时非常实用。从那以后我每次部署完都强制自己走一遍这个顺序先看 jar 里的配置再 curl 后端接口最后刷新前端页面。做完这个项目JWT 鉴权、分页插件、事务回滚、跨域处理这几个点正好就是 Java 面试题里最常被翻牌子的内容把代码里每一处都吃透比背八股文踏实得多。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →