ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Spring Boot + Vue养老社区管理系统开发实战解析

Spring Boot + Vue养老社区管理系统开发实战解析 先说说这个项目的来龙去脉。东北大学软件学院2020级2021年夏季实训我选的是“东软颐养社区系统”这个方向本质上是一个典型的养老社区综合管理平台核心服务对象是社区运营人员、护工、入住老人及其家属。整个项目从需求梳理到前后端联调我在实训周期内独立完成技术栈以Spring Boot Vue为主数据库用的MySQL。如果你正好也在做类似的实训项目或者对“社区管理类系统的开发流程”感兴趣这篇文章能帮你把整体框架和很多容易踩坑的细节理顺。考虑到实训项目的特性我把它定位成“可跑通、可演示、有业务深度”的全栈小系统。它解决的核心痛点是社区内老人信息分散、活动报名靠人工登记、健康档案纸质化严重、护工排班与任务指派不透明。系统把这些线下流程搬到线上让管理员能管、护工能用、家属能看算是给传统养老社区做了一次轻量数字化改造。适合的人群包括正在准备实训/毕设的软件工程学生、刚接触Spring Boot Vue的初级开发者以及想了解社区管理类系统如何建模和落地的产品小白。需要说明的是受实训周期限制我在很多设计上选择的是“够用且稳定”的方案而不是“大而全”的企业级架构。比如权限模块没有引入Spring Security而是用拦截器Session的方式自己做了一套简单的角色控制权限角色也只区分了管理员和护工两种没有做家属端登录家属只能通过管理员代查。这些取舍在后续章节我会逐个解释原因方便你结合自己的需求去调整。1. 实训项目整体设计与业务拆解我一直觉得实训项目最忌讳的就是拿到题目直接开写代码。先花一两天把业务想明白后面能省出两三天的返工时间。所以拿到“东软颐养社区系统”这个题目后我第一件事不是在IDEA里新建项目而是用一页纸把整个业务画了一遍。1.1 项目背景与需求分析的思路养老社区这类系统业务上其实非常有规律。它不像电商系统那样有复杂的商品、订单、支付链路也不像社交平台那样有海量的内容流和关系链。它的核心是“以老人为中心把人和服务管理起来”。围绕这个核心我把业务拆成几个关键环节老人从哪来——入住登记录入基本信息、家属联系方式、紧急联系人。老人住在哪——房间分配需要维护楼栋、楼层、床位这些物理信息。老人身体怎么样——健康档案包括既往病史、过敏药物、近期体检结果、每日查房记录。老人每天干什么——社区活动活动发布、报名、签到以及活动照片归档。谁在服务老人——护工管理包括护工信息维护、任务指派、日常查房上报。谁来买单——费用管理这里实训期间我只做了简单的月度费用记录和欠费标识没有对接支付。这六个环节理清楚之后系统的功能模块就自然浮出水面了。为了控制工作量我把这六个业务环节映射成了五个大模块老人管理、健康管理、活动管理、护工管理、系统管理。其中费用管理因为业务相对独立我把它并入了老人管理模块中没有单独拆页面。这个拆法在答辩的时候也经得起问因为每一个模块都能对应到实际业务场景而不是为了凑功能硬造的。1.2 功能模块划分与业务流程我把系统的功能模块整理成了一张思维导图来指导后续开发这里我用文字给你还原一下方便你参考。第一个模块是老人管理包含老人档案的增删改查入住登记时填写身份证号、家属联系方式、既往病史等信息房间分配负责把老人关联到一个具体的床位同时校验床位是否已被占用费用记录则提供月度费用查询和欠费标记功能。第二个模块是健康管理包含健康档案的查看与维护护工每次查房后可以提交体征数据比如体温、血压、心率以及备注系统提供近7天的体征趋势查询方便快速了解老人身体状态是否有异常波动。第三个模块是活动管理包含活动发布管理员录入活动标题、时间、地点、人数上限和简介老人报名由护工代操作或管理员后台直接添加活动结束后可以上传现场照片并标记签到情况。第四个模块是护工管理包含护工信息维护和任务分配。任务分配是本模块的核心管理员可以给指定护工指派一个或多个老人护工登录后只能查到自己名下的老人列表和对应的待办任务。第五个模块是系统管理包含管理员账号维护、密码修改和登录日志记录。之所以没有把菜单管理和角色权限做成动态配置是因为实训项目只需要满足“管理员能管所有护工只能管自己名下的老人”这个核心规则做得太重反而容易出bug。这里给出一个建议在做需求分析的时候一定要把三个关键问题想清楚——“谁在用这个系统”“他登录进来能看到什么”“他能对数据做什么操作”。这三个问题一旦想清楚数据库的表结构就基本定了一半后面编码会非常顺畅。2. 技术选型与架构落地方案技术选型这件事我在实训开始前就做了功课。考虑到项目规模、开发效率、以及答辩时技术点能不能讲清楚最终没有选择过于复杂的微服务架构而是采用了一整套成熟稳定的前后端分离方案。2.1 后端技术栈选择的几个关键考虑后端我用的是Spring Boot 2.4.5 MyBatis-Plus 3.4.2JDK用的1.8。这三个组合在当时属于非常成熟的搭配网上资料多遇到问题也很好搜解决方案。Spring Boot 2.4.5用来搭建基础框架内嵌Tomcat打jar包直接跑省去了一堆XML配置。MyBatis-Plus则是MyBatis的增强工具不用自己写基本的CRUD SQL只需要继承BaseMapper接口就能获得单表增删改查的方法这对实训项目这种以单表操作为主的场景非常友好。ORM选型上我没有用Spring Data JPA原因是JPA在关联查询和复杂SQL上比较绕而且在表结构设计不够精细的时候容易生成一堆低效SQL。MyBatis-Plus配合XML写多表联查SQL会更直观也更容易控制查询性能。权限这一块我用的是最简单的方案拦截器 Session 自定义注解。没有引入Spring Security因为实训项目只需要区分管理员和护工两种角色用Spring Security的话配置量大对项目价值提升有限。我是这样设计的登录成功后把用户信息存到Session里自定义一个RequirePermission注解标记在Controller方法上然后在拦截器中校验当前用户角色。判断逻辑就两三行代码简单直接。数据库连接池用的阿里的Druid配置了基本的监控页面。数据库连接串里加上了useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai这几个参数解决中文乱码和时区问题。2.2 前端技术栈与开发环境准备前端我选择了Vue 2.6.11 Element UI 2.15.1。Vue 2在当时非常主流Element UI提供现成的表格、表单、弹窗、菜单等组件不需要自己写太多CSS样式。项目构建工具用的是Vue CLI 4.x用npm run serve启动开发调试npm run build产出静态资源包后端打成jar包时把dist目录拷贝到resources/static下这样通过后端端口就能直接访问页面部署时不需要额外配置Nginx。这里有个小细节值得说一下。前后端分离开发时本地联调会面临跨域问题。我用了两种方式解决开发环境在vue.config.js里配置了devServer的proxy代理把/api前缀的请求都转给本机的8080端口这样浏览器里看不到跨域请求生产环境则直接把前端编译结果交给Spring Boot托管前端请求改成/api相对路径后端Controller统一用server.servlet.context-path/api作为前缀。这两种方式结合起来整个开发流程就没遇到过跨域拦截的报错。开发环境用的是IDEA 2020.2 Navicat 15搭配使用。Navicat不仅用来建表、导数据还用来生成ER图和测试SQL脚本在写复杂关联查询的时候非常方便。我还配置了阿里云Maven镜像仓库来加速依赖下载。整套环境搭下来大概花了半天时间但对后续开发效率的提升非常明显。3. 数据库设计与核心表结构实现数据库设计是我在这个项目中花时间最多的部分也是收获最多的地方。一个系统的数据库设计做得好不好直接决定了后面的编码难度和系统的扩展空间。我在设计时反复改了三个版本才最终定稿。3.1 核心业务实体梳理与ER设计思路在设计表结构之前我先把上一轮分析出来的实体列了一遍。核心实体包括管理员、护工、老人、家属、房间、健康档案、查房记录、活动、活动报名、费用记录、公告消息。实体之间的关系是这样的一个护工可以负责多个老人一个老人也可以被多个护工共同照看所以“护工-老人”是多对多关系用中间表t_staff_elder来维护一个老人有多个健康档案是一对多一个活动对应多个报名记录是一对多老人和房间的关系在演示系统里简化成一对一一个房间对应一个床位号绑定后被标记为占用。画ER图的时候我有一个心得把“核心实体”和“流程记录实体”分开。核心实体是那些长期存在的主数据比如老人、护工、房间流程记录实体是每次操作产生的流水比如查房记录、活动报名、登录日志。这样分类之后你会发现有些表天生就是要经常插入数据的有些表则很少改动那么在写SQL和建索引的时候就能做到心里有数。3.2 重点表结构与字段设计解析下面我挑几张最核心的表来说明字段设计思路。老人信息表t_elder是系统的核心主表字段包括id主键elder_name老人姓名gender性别birth_date出生日期id_card身份证号phone联系电话address家庭住址emergency_contact紧急联系人emergency_phone紧急联系电话medical_history既往病史allergy_history过敏史room_id房间IDbed_no床位号status状态1在住0已退住create_time创建时间update_time更新时间。这里有几个设计上的细节值得说道一下。身份证号虽然是唯一标识但在这种系统里老人的住址和电话变动频率较高所以我保留了主键id作为逻辑主键而没有让身份证号承担主键职责。另外紧急联系人单独建了字段而不是设计成家属表是因为演示系统里一个老人对应一个紧急联系人就够了用一张独立的家属表会提高复杂度对业务演示没有实质帮助。健康档案表t_health_record字段包括id主键elder_id老人IDblood_pressure_high收缩压blood_pressure_low舒张压heart_rate心率temperature体温weight体重check_time检查时间checker检查人remark备注。这张表是典型的流水表每次护工查房或录入体征就新增一条记录。为了后续查询方便我特意建了(elder_id, check_time)的联合索引这样按老人查趋势图时走索引会非常快。任务分配表t_staff_elder是中间表字段包括id主键staff_id护工IDelder_id老人IDassign_time分配时间assign_by分配人。为什么单独建这张表而不是在护工表里直接存老人ID列表因为数据库第一范式不允许一个字段存多个值而且中间表天然就支持多对多查询后续要做“某个老人由哪些护工负责”或者“某个护工负责哪些老人”都只要一条join就能搞定。房间表t_room字段比较简单id主键building_no楼栋号floor_no楼层号room_no房间号bed_no床位号status房间状态0空闲1已入住elder_id当前入住老人ID。需要给(status, room_no)建联合索引因为分配房间时要快速筛出空闲房间。3.3 初始化数据与测试数据准备数据库设计完成后我先写了一份sql初始化脚本把表的DDL和基础数据一次性跑完后面每次清库重来都比较方便。基础数据我准备了两个管理员账号、六个护工账号、八个老人档案、十几条健康记录、三个活动记录。这些数据不是随便编的我会确保护工和老人之间存在真实的业务关联比如护工“李护士”名下有两位老人活动“上午太极课”的报名人数恰好是三人。演示系统最怕的就是页面能打开但数据逻辑对不上答辩时被问住就尴尬了。测试数据这块我踩过一个坑就是一开始用SQL插入中文时没有注意字符集结果页面上显示乱码。最后在每个建表语句末尾都加上了DEFAULT CHARSETutf8mb4同时在Druid连接串里配置了characterEncodingutf8才彻底解决。utf8mb4比utf8多支持了emoji能预防未来插入特殊字符时报错属于有备无患。4. 核心功能模块实操与编码细节数据库设计好之后就进入了最耗时的编码阶段。这个阶段我采用了一种比较高效的开发顺序先搭好后端工程结构和工具类然后按照“登录模块 → 老人管理 → 健康管理 → 活动管理 → 护工管理”的顺序逐个实现最后统一联调。4.1 登录认证与权限控制的具体实现登录模块是所有页面的入口我先从它开始。后端我创建了LoginController接收账号密码后调用service层校验。密码存储上我用的不是明文而是MD5加盐的方式。具体做法是注册时用MD5(密码 盐值)生成密文盐值取用户的创建时间戳。登录时把用户输入的密码按同样的规则加密后与数据库中的密文比对。这个方案虽然不是最安全的MD5已经不够强但在演示系统里足够用而且实现简单答辩时能讲清楚加盐的原理。权限控制这块我创建了一个自定义注解RequirePermission注解上定义了一个value属性表示需要的角色。在方法上加RequirePermission(admin)拦截器就会判断当前Session中的用户角色是否是admin不是就直接返回401。护工端的接口则全部在方法上标注RequirePermission(staff)。这里有个细节只在Controller方法上加注解还不行拦截器还必须对“获取当前用户未登录”的情况做统一处理。我封装了一个BaseController在里面定义了一个获取当前登录用户的方法子类继承即可。这样每个Controller就不需要重复写获取Session里用户信息的代码了日志打印也会方便很多。4.2 老人档案与健康管理模块实现详解老人管理模块是典型的CRUD场景但里面有几个值得注意的业务点。第一个是“入住登记”时要做房间占用校验。流程是前端表单提交老人信息和选择的房间ID后后端先根据room_id查询t_room的status如果status等于1就直接返回“该房间已入住”的错误提示否则将房间status更新为1并把elder_id写入t_room表。为了保证这两步操作的原子性我用Transactional注解把这个方法包了起来任何一步失败都会回滚。第二个业务点是“退住处理”。退住时需要把老人的status改为0同时释放房间也就是把t_room的status改回0elder_id清空。这里同样用到了事务。虽然这个操作看起来简单但如果不加事务一旦第一步成功而第二步失败就会出现老人已经退住但房间还显示占用的情况非常影响演示效果。健康管理模块我做了体征录入和趋势查询两个功能。体征录入是护工端的高频操作表单包含血压、心率、体温、体重和备注。后端会校验数据的合法性比如心率范围在0到300之间体温范围在35到45之间不合法的数据直接抛业务异常。趋势查询是用ECharts实现了一个折线图展示老人最近7天的体温和心率变化。这个功能虽然不复杂但在答辩演示时效果很加分因为能直观展示“系统不是简单的增删改查”。后端的接口是GET /api/health/trend?elderIdxxdays7返回最近7天的体征数据列表前端拿到后直接渲染到图表里。4.3 活动发布报名与护工任务分配功能实现活动管理模块我实现了发布、查看、报名和签到的完整闭环。发布活动时需要一个字段“报名截止时间”前端用日期时间选择器选择后端判断截止时间不能早于当前时间。报名功能我用了一个标志字段is_signup来控制默认报名状态为已报名管理员可以手动取消。活动开始后管理员就能标记签到状态前端页面上未签到的人员会以红色标签展示。护工任务分配是本系统里逻辑最复杂的一个点也是核心亮点。我在护工端的首页设计了一个“我的老人”列表展示当前登录护工负责的所有老人信息。这个列表的数据来源是一条联合查询SQL关联了t_staff_elder、t_elder和t_room三张表查出老人基本信息并带上房间号。护工点击某个老人卡片后会进入老人的详情页展示老人的健康档案和最近查房记录同时有“新增查房记录”的按钮。管理员在后台的护工管理页面可以通过一个多选下拉框为护工分配老人分配的接口接收staffId和一个elderIds数组批量插入t_staff_elder表。批量插入用MyBatis-Plus的saveBatch方法一条SQL搞定效率很高。5. Vue前端页面集成与接口联调前端这部分虽然前端技术本身不是实训的重点但页面做得好不好直接决定了整体演示效果。我在前端用了Element UI把管理员端和护工端做了侧边导航区分左侧菜单根据登录角色动态渲染。5.1 Vue项目脚手架搭建与路由拆分配置Vue项目的脚手架我用Vue CLI 4.x创建创建完成之后第一件事就是安装Element UI和axios然后配置路由。路由设计上我采用了懒加载方式每一个页面组件对应一个路由。管理员端有老人列表、添加老人、房间管理、活动管理、护工管理等路由护工端有首页我的老人、健康记录、任务列表等路由。这里有一个关键的细节路由守卫。我用Vue Router的beforeEach钩子做了登录判断如果访问的页面需要登录但没有登录态就跳转到登录页如果已经登录但角色不对就跳转到403页面。这个功能在前后端分离项目里是标配没有它的话用户直接输入URL就能绕过登录进入页面演示时就会很尴尬。axios的配置也很重要。我在src/utils/request.js里封装了一个axios实例设置baseURL为/api请求拦截器里从localStorage读取token并添加到请求头响应拦截器里统一处理错误码。比如后端返回401时自动跳转登录页返回500时用Element UI的Message组件弹出错误提示。这样的统一封装让每个页面的业务代码变得非常干净。5.2 列表页、表单弹窗与图表组件实操要点在管理员端的老人管理页面我用了el-table展示老人列表每一行的操作列里有“编辑”“健康档案”“退住”“安排护工”四个按钮。编辑按钮弹出一个el-dialog里面是一个el-form表单保存时调用后端的更新接口。“健康档案”按钮则跳转到健康记录的详情页该页面用el-descriptions展示档案信息配一个ECharts折线图展示体征趋势。表单校验方面el-form的rules属性可以实现基本的必填校验比如姓名必填、身份证格式校验、手机号格式校验。这些校验规则我在前端和后端各写了一遍。前端校验主要提升用户体验后端校验是防止绕过前端直接调接口时把脏数据写进数据库。实训项目虽然规模不大但校验习惯要养成答辩时能提出来是加分项。在护工端的老人详情页我添加了一个“新增查房记录”的按钮点击弹出表单表单里有一个日期选择器和一个文本域。日期选择器默认值设为当前时间文本域用来填写查房备注。提交后调用后端接口成功后刷新下方的健康记录列表同时更新图表数据。为了实现图表更新我把表格数据和图表数据都定义成了响应式数据用watch监听elderId的变化如果切换老人就重新拉取数据。5.3 前后端联调阶段的接口规范与调试技巧实训项目中联调阶段是最容易出问题的阶段。联调之前我做了两件事一是统一了接口返回格式定义了一个Result对象里面包含code、message和data三个字段。code为200表示成功500表示业务异常401表示未登录或权限不足。这样做的好处是前端响应拦截器可以统一处理错误不需要每个页面单独判断。二是把所有接口的路径都整理成了一份接口文档虽然是简单的表格形式但给联调和答辩都提供了很大的帮助。接口文档上列出了请求方式、请求路径、请求参数、返回值示例前端照着文档调接口效率很高。联调中最常遇到的问题就是后端返回的日期格式不符合前端预期。默认情况下Spring Boot返回的LocalDateTime格式是2021-07-01T10:30:00而前端Element UI的表格显示时会出现英文T非常难看。解决方式是在application.yml里配置了spring.jackson.date-format和time-zone或者在LocalDateTime字段上加上JsonFormat(pattern yyyy-MM-dd HH:mm:ss)注解。我采用了后者因为这样可以把日期格式控制在字段级别不影响其他类型的字段。还有一个问题是用axios提交表单数据时默认的Content-Type是application/json;charsetUTF-8而如果用默认的URL-encoded方式发送数据后端用RequestBody接收就会报错。我在封装axios时统一设置了Content-Type为application/json前端所有POST请求都通过JSON对象发送后端用RequestBody接收整个联调过程就顺畅了。6. 实训中的坑点与问题排查实录这个部分我总结一些在实训过程中真实遇到的、而且很典型的问题。问题清单的价值在于如果你正在做类似的系统碰到类似报错时可以直接对照排查不用再花半天时间翻搜索引擎。6.1 后端高频问题清单与解决方案第一个问题MyBatis-Plus的分页查询不生效。这个问题很经典也是最容易踩的坑。MyBatis-Plus从3.4版本开始使用分页功能需要手动配置PaginationInnerInterceptor分页插件否则调用selectPage方法时不会自动拼接LIMIT语句而是把全表数据都查出来了。解决方式是在MybatisPlusConfig类里添加这样一个配置Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }第二个问题LocalDateTime字段在JSON序列化时是数组格式。这个是典型的Jackson序列化坑默认情况下Jackson会把LocalDateTime序列化成类似[2021, 7, 1, 10, 30, 0]的数组页面没法直接用。解决办法是引入jsr310模块并配置日期格式或者直接在字段上添加JsonFormat注解。我用的是第二种直接在Bean里加了JsonFormat(pattern yyyy-MM-dd HH:mm:ss)然后重启服务就正常了。第三个问题Druid数据源启动时打印“discard long time none received connection”警告。这个警告不影响系统运行是Druid连接池回收空闲连接时的正常日志可以忽略。如果不喜欢这个日志可以在application.yml里配置spring.datasource.druid.test-while-idletrue和time-between-eviction-runs-millis60000能减轻一点日志噪音。第四个问题使用saveBatch批量插入时效率极低。后来排查是默认的批量插入没有开启rewriteBatchedStatements参数。MySQL驱动连接串里加上rewriteBatchedStatementstrue之后批量插入的效率提升了不止一个数量级这个参数强烈建议加上。6.2 前端与联调阶段的疑难杂症排查第一个问题axios请求后端时返回403。这个问题在开发环境通常是因为跨域配置没做好在Vue的vue.config.js里配置devServer的proxy即可解决。但要注意配置proxy后前端请求必须用相对路径比如/api/login而不是写死http://localhost:8080/api/login否则proxy不会生效。第二个问题el-dialog预览时出现了页面底部滚动条或者遮罩层关闭时页面无法滚动。这个往往是CSS样式没处理好尤其是给body设置了overflow:hidden之后忘了恢复。解决办法是在Dialog的open事件里记录原来的是否有滚动条在close事件里恢复。第三个问题接口正常返回数据但页面上表格是空的。这个问题我排查了很久最后发现是表格绑定的数据字段名和后端返回的字段名不一致。比如后端返回的是elderName前端表格写的是name那肯定展示不出来。建议前端在做表格列的时候严格对照后端的实体类字段命名统一使用驼峰命名法不要擅自改名。6.3 数据一致性与并发场景处理的实用经验实训项目虽然并发量不高但有一些数据一致性场景要在设计时提前考虑。最典型的就是“多人同时给同一个老人录体征数据”以及“两个管理员同时给同一个护工分配同一个老人”。前者在保存时如果不对心率、体温等字段做范围校验可能会录进明显不合理的数据后者如果不做唯一约束中间表t_staff_elder就容易出现重复记录导致护工端老人列表出现同一个老人多条记录。我在做这类操作时的解决思路是在数据库层面给t_staff_elder表加上唯一索引字段组合为(staff_id, elder_id)这样重复插入时会直接报错从根源上杜绝重复数据。在业务层面对关键写入操作增加校验比如编辑健康记录时先查询该老人是否存在再做插入。对这类问题做统一异常处理捕获DuplicateKeyException并返回“该护工已经负责了这位老人”的友好提示而不是直接抛500。其实实训项目的数据一致性更多取决于表设计和校验逻辑好的表设计能避免大部分问题。7. 实训项目复盘与一些经验之谈项目整体做完之后我花了一个下午做了一次复盘。复盘的内容不光是“做了什么功能”更重要的是“踩过哪些坑”和“哪些地方可以优化”。这部分的经验我觉得比项目本身更值得分享。第一点体会是实训项目一定要控制好技术边界。做之前先把需要用到的技术点列清楚能不用就不用的东西坚决不引入。我一开始想用Redis做缓存后来认真评估发现系统没有什么明显的热点查询加了反而让部署和演示多一层麻烦最后果断放弃。能用简单的方案解决问题就不要把问题变复杂。第二点体会是数据库表设计值得花更多时间反复推敲。我这次在设计阶段改了三次表结构每次改动都意味着前面的代码白写了。比如一开始我把房间和老人设计成了多对多关系后来发现演示场景里完全不需要改成了现在的一对一。如果一开始就画好ER图并审查几遍后面会少做很多无用功。第三点体会是坚持写接口文档非常有用。哪怕只是一个简单的接口清单也能让前后端开发分工清晰、联调顺畅。我的具体做法是写一个接口文档每个接口写明路径、参数、返回字段和示例。这个文档在后面写答辩PPT、写项目报告时也成了最好的素材相当于一稿多用。这个项目如果想继续扩展还可以做几个方向增加家属端小程序让家属可以在手机上查看老人的健康报告给护工端增加排班日历把每周的照护任务可视化引入消息通知机制比如老人体征异常时推送告警给管理员和家属。这些扩展方向我在答辩时简单提了一下面试官反馈还不错。如果你也在做类似的养老社区管理系统可以在设计阶段提前预留这些扩展的余地。
返回列表