
去年九月份开学那阵我手机里加了五个二手交易群、三个失物招领群、两个表白墙账号每天消息上千条真到用的时候什么都搜不到。东区捡到一张校园卡在群里刷屏了三天都没找到失主有人要转二手教材发出去十秒就沉底了。做一个大学校园生活信息平台的想法就是那时候冒出来的。技术栈定得很干脆SpringBoot2 Vue3 MyBatis-Plus MySQL8.0前后端分离用户端和管理后台一整套最后连环境搭建文档、数据库初始化SQL、接口文档一起打包交付。这篇文章把项目从需求、建表、开发、踩坑到部署的全过程复盘一遍供正在做同类课设、毕设或者想接外包项目的朋友参考。1. 为什么做这个项目校园信息流通的真实痛点与功能切片1.1 校园信息孤岛群聊、朋友圈和公告栏解决不了的三件事在做这个平台之前我先花了一周时间观察身边同学的实际使用习惯。结果很直观信息发布渠道极度分散。有人习惯发QQ群有人只发朋友圈还有人往表白墙投稿公告栏则承担着官方通知的作用。这些渠道全是流式消息来了就往上刷没有任何沉淀。你想找一条三天前的二手交易信息要么翻几千条聊天记录要么干脆放弃。这里面有三个核心痛点不可检索流式信息无法按关键词、分类、标签筛选找东西基本靠运气。没有闭环失物招领发布之后有没有人领取、物品有没有归还没有任何状态跟踪发完就消失了。无法管理群里卖东西出现纠纷表白墙出现引战内容既没有举报入口也没有审核机制。这三个痛点决定了系统的核心定位做一套围绕发布—检索—联系—完结闭环的校园信息平台而不是又做一个即时通讯工具。1.2 功能范围确认用户端和管理后台一分为二基于上面的痛点最终功能切片如下功能模块用户角色核心动作用户体系学生、教师、管理员注册、登录、实名认证、个人主页信息广场所有用户分类发帖、关键词搜索、标签筛选失物招领所有用户拾取发布、寻物启事、认领申请、状态跟踪二手交易所有用户商品发布、上下架、预约锁定、售出标记活动报名发布者、参与者活动创建、名额统计、报名审核管理后台管理员内容审核、用户管理、举报处理、数据看板这个范围能覆盖校园里接近80%的日常信息需求。用户端面向普通学生界面尽量轻量操作路径不超过三步管理后台面向学生会和老师侧重审核效率和状态可视。1.3 MVP版本取舍哪些功能砍掉了很多人做这类系统容易过度设计我的建议是分两期走。第一期MVP砍掉了三样东西砍掉站内实时聊天开发周期太长用站内留言加联系方式展示可以完全替代。砍掉在线支付二手交易核心是线下当面交易引入支付会涉及资金安全和合规问题完全没有必要。砍掉LBS定位校园范围就那么大用东区食堂二楼3号宿舍楼下这样的文本描述比地图定位更实用。第一期把发帖—浏览—联系—状态变更这条主线跑通比堆砌功能重要得多。事实证明这个取舍是对的后续所有新增需求都可以在稳定的信息流框架上扩展。2. 后端工程实践SpringBoot2 MyBatis-Plus 的落地细节2.1 版本选型为什么锁死 SpringBoot 2.7.18这个项目用的是SpringBoot 2.7.18 JDK 8 MyBatis-Plus 3.5.3.1。很多新人上来就想用SpringBoot 3.x我建议校园类项目慎重。SpringBoot 3.0之后有两个变化影响很大一是javax命名空间整体迁移到jakarta二是最低要求JDK 17。如果你用的是JDK 8环境或者后续要接一些基于JDK 8的旧组件SpringBoot 2.7就是最稳的选择。另外2.7是SpringBoot 2.x的最后一个维护分支社区补丁和文档都非常成熟。这套项目将来交给学弟学妹维护环境门槛越低越省事。Maven依赖核心清单大致如下spring-boot-starter-web、spring-boot-starter-validation、mybatis-plus-boot-starter、mysql-connector-j、lombok、jjwt。Redis不是必须的但我会用它做验证码和热门帖缓存后面会说。2.2 工程分层经典三层架构依然是最优解后端工程结构如下src/main/java/com/campus ├── common // 统一响应、异常枚举、常量 ├── config // 配置类拦截器、跨域、MyBatis-Plus插件 ├── security // JWT工具、登录拦截、权限注解 ├── controller // 接口层 ├── service // 业务层接口 实现 ├── mapper // MyBatis-Plus Mapper接口 ├── entity // 数据库实体 └── dto // 请求/响应对象为什么不用DDD或者其他高级架构因为校园信息平台的业务复杂度还没到需要领域建模的程度。经典三层架构的优点是责任边界清晰新人接手半小时就能看懂代码流向。controller只做参数接收和响应封装service专注业务规则mapper只做数据访问。遇到复杂查询就加一个CustomMapper写SQL不硬套MyBatis-Plus的API。2.3 MyBatis-Plus的正确打开方式MyBatis-Plus的核心价值是让单表CRUD零SQL。我实际使用中受益最大的是三个能力BaseMapper / IService / ServiceImpl继承之后基础方法全部自带不需要手写insert into...这类SQL。LambdaQueryWrapper用方法引用代替字符串列名既防手滑写错字段名也防SQL注入。例如wrapper.like(Post::getTitle, keyword).eq(Post::getType, type)。分页插件MyBatis-Plus的物理分页基于拦截器实现需要在配置类注册PaginationInnerInterceptor指定数据库类型为DbType.MYSQL。这一步极其关键漏掉的话分页查询返回的数据会异常后面踩坑章节细讲。实体类上统一使用TableName、TableId(type IdType.ASSIGN_ID)、TableField(fill FieldFill.INSERT)这些注解。create_time和update_time不用在业务代码里手动set通过MetaObjectHandler实现自动填充减少重复劳动。2.4 统一响应和全局异常接口层的第一道防线所有接口返回统一结构public class ResultT { private Integer code; // 200成功403无权限500异常 private String msg; private T data; }配合RestControllerAdvice做全局异常处理业务异常通过BizException抛出参数校验异常MethodArgumentNotValidException统一翻译成友好文案。这个设计很大程度上避免了前端拿到一串看不懂的堆栈信息。注意一个细节发布到生产环境时异常处理器不要把服务器内部堆栈原样返回日志里打印就行响应体只给用户能看懂的提示。3. MySQL8.0 配置与表结构设计从安装坑到实体类建表3.1 MySQL8.0 安装与JDBC连接串参数数据库版本选的是MySQL 8.0开发环境有两种装法。如果是本地开发我推荐直接用Docker干净、好卸载几分钟就能起一个实例docker run --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD你的密码 \ -e MYSQL_DATABASEcampus_life \ -d mysql:8.0 \ --character-set-serverutf8mb4 \ --collation-serverutf8mb4_unicode_ciJDBC连接串是这类项目里最容易出问题的点我最终使用的配置如下spring: datasource: url: jdbc:mysql://localhost:3306/campus_life?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver三个必填参数说明serverTimezoneAsia/Shanghai不设的话默认UTC时间前端看到的create_time会比北京时间少8小时。useSSLfalse本地开发环境没必要走SSL握手速度快很多。allowPublicKeyRetrievaltrueMySQL8默认认证插件是caching_sha2_password不开这个参数客户端首次连接会报Public Key Retrieval is not allowed。3.2 核心表结构七张表撑起整个平台数据库设计遵循信息流统一的思路没有为每个业务模块单独建表最终核心表如下表名作用关键字段user用户表id, username, password, nickname, avatar, role, statuspost统一信息表id, user_id, type, title, content, images, status, view_countpost_comment评论/留言表id, post_id, user_id, content, parent_id, statuslike_record点赞记录表id, user_id, target_type, target_id, create_timeclaim失物认领申请表id, post_id, user_id, description, contact, statusactivity活动表id, user_id, title, content, start_time, end_time, max_peopleactivity_registration活动报名表id, activity_id, user_id, status, create_timepost表用type字段区分失物招领、寻物启事、二手交易、校园动态等业务类型。表数量少逻辑统一列表页可以用一套分页查询搞定。业务特有字段比如二手价格、活动时间通过扩展表或者独立字段承载不会造成巨大的表结构冗余。3.3 根据实体类生成建表SQL的正确理解很多新手搜mybatisplus根据java实体类生成创建表的sql语句其实MyBatis-Plus本身不具备实体类自动建表能力它不是JPA那种ddl-autoupdate的自动建表工具。我在项目中采用了两级方案开发期快速建表写了一个简单工具类扫描TableName、TableId、TableField注解自动拼出CREATE TABLE IF NOT EXISTS语句只用于本地开发环境。字段类型从Java类型推断String映射VARCHAR(255)LocalDateTime映射DATETIME等够用但不够精细。正式环境版本化DDL把最终的campus_life.sql手工整理成带注释的建表脚本后续所有表结构变更都走增量SQL不允许直接改原脚本。这样部署到服务器时执行一遍SQL就能得到与开发环境一致的结构不会出现代码到服务器上跑不通的问题。代码和表结构之间的一致性靠的是实体类字段注释和DDL注释都写得足够清晰而不是依赖自动生成工具。3.4 索引设计单表数据量不大也要走对索引虽然校园系统用户量撑死几千但索引仍然要建对。post表核心索引如下idx_user_id查询我发布的帖子时走。idx_type_status按类型和状态筛选列表页时走比如只查type2 AND status0的二手在售商品。idx_create_time默认信息流按时间倒序排列建索引避免大数据量下的filesort。标题搜索先用的LIKE %keyword%这个写法无法走普通索引但在几千条数据量下性能完全没问题。等数据真正到了几十万级再考虑引入MySQL全文索引或者ElasticSearch也不迟。不要为了追求所谓的高性能架构在项目初期就过度设计。4. Vue3 前端实战管理端、用户端与接口联调4.1 Vite4 Vue3.2 Element Plus前端工程初始化前端基于Vite4 Vue3.2 Pinia Vue Router4 Element Plus构建状态管理用Pinia替代Vuex原因很简单Pinia的API更简洁天然支持组合式APITypeScript类型推断也更好。工程目录这样划分src ├── api // 接口函数统一封装 ├── assets ├── components // 通用组件PostCard、UploadImage、CommentList ├── router // 路由配置用户端 管理后台分模块 ├── stores // Pinia状态userStore、appStore ├── utils // axios实例、权限指令、工具函数 └── views ├── portal // 用户端页面 └── admin // 管理后台页面用户端和管理后台没有拆成两个独立项目而是共用一套依赖和组件库通过路由模块区分。管理后台的菜单、布局、权限和用户端完全隔离但登录逻辑、上传组件这些可以复用开发效率高不少。4.2 组合式API的组织方式hooks是提升效率的关键Vue3最大的改变是组合式API。项目中我把可复用的业务逻辑都抽成了hooks比如useTable.ts封装分页表格的全套逻辑传入fetchApi函数返回list、loading、pagination、search、reload方法。useLogin.ts封装登录流程、Token存取、用户信息拉取。以useTable为例核心逻辑非常简单export function useTable(fetchApi: (params: any) PromiseResult, initParams {}) { const list ref([]); const loading ref(false); const pagination reactive({ current: 1, size: 10, total: 0, }); const queryParams reactive({ ...initParams }); async function loadData() { loading.value true; const params { current: pagination.current, size: pagination.size, ...queryParams, }; const res await fetchApi(params); list.value res.data.records; pagination.total res.data.total; loading.value false; } function search() { pagination.current 1; loadData(); } return { list, loading, pagination, queryParams, search, loadData }; }这样的好处是用户端信息流列表和管理后台用户列表可以共用同一套逻辑没有复制粘贴代码。4.3 axios 封装拦截器决定了接口调用的体感axios实例的封装是前端工程的基座。我这边做了三层拦截const request axios.create({ baseURL: /api, timeout: 10000 }); request.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers.Authorization Bearer ${token}; } return config; }); request.interceptors.response.use( response { const res response.data; if (res.code ! 200) { ElMessage.error(res.msg || 接口异常); return Promise.reject(new Error(res.msg)); } return res; }, error { if (error.response?.status 401) { localStorage.clear(); location.href /login; } else { ElMessage.error(网络异常请稍后重试); } return Promise.reject(error); } );三个关键点baseURL统一走/api前缀开发环境交给Vite代理转发到8080端口请求拦截统一加Token响应拦截统一处理业务错误码和401登录失效。401后清理LocalStorage并跳转登录页这个逻辑在多标签页场景下尤其重要避免用户A退出后用户B的页面还在正常操作。4.4 动态路由与按钮级权限管理后台的菜单不是写死的路由表而是用户登录后根据后端返回的角色权限动态生成。实现思路路由表分两部分基础路由登录页、首页是静态的管理页是动态的。用户登录后调/api/user/permissions获取菜单列表和按钮权限码数组。用router.addRoute动态注册管理页路由。按钮权限用自定义指令v-permission控制比如删除帖子的权限码是post:delete在按钮上写v-permissionpost:delete指令内部判断权限码不存在时移除该DOM节点。这种方案的优点是权限一变前端立即生效不用发版。4.5 前后端联调细节字段、日期与跨域联调阶段最容易暴露问题的是三处字段命名、日期格式、跨域。字段命名上后端JSON返回默认驼峰userId前端直接用同一套命名即可MyBatis-Plus的mapUnderscoreToCamelCase已经做好了数据库下划线到Java驼峰的自动映射前端不需要再做任何转换。日期格式上如果后端直接返回LocalDateTime默认序列化结果是数组格式可读性极差。我统一在配置里加了Jackson的日期格式spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai跨域方面开发环境用Vite的server.proxy配置server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, } } }生产环境由Nginx接管同源访问前后端分离部署时不会遇到跨域问题。这条思路一定要理清楚开发环境代理解决跨域生产环境同源规避跨域。5. 核心业务逻辑与权限控制帖子、交易、失物招领怎么串起来5.1 统一信息流设计一张post表承载多种业务我最开始也纠结过要不要为二手交易、失物招领、活动各建一套表最后选择了post统一信息表加type字段的方案。type枚举定义如下public enum PostTypeEnum { LOST(1, 寻物启事), FOUND(2, 失物拾取), SECOND_HAND(3, 二手交易), ACTIVITY(4, 校园活动), FREE_TALK(5, 自由交流); }这么设计的好处非常明显列表页统一首页信息流不管什么类型都走同一个分页接口前端根据type值渲染不同的卡片样式。搜索统一用户输入关键词只需在post表里做LIKE title/content不需要跨多张表联合搜索。审核统一管理后台一套审核流程处理所有类型内容不用各自开发。代价是post表无法直接承载业务专属字段比如二手价格、活动时间这些数据通过activity扩展表和post表中的通用字段比如extra_json补充。对于校园场景这个取舍完全划算。5.2 状态机设计业务状态不能散落在代码里信息流平台最怕的是状态变量到处都是。项目里我把每类业务的状态流转都做了显式定义业务状态值流转流程失物招领0待领取1认领中2已完成3已撤销发布 - 用户提交认领 - 发布者确认 - 认领完成二手交易0在售1已预约2已售出3已下架发布 - 买家留言预约 - 卖家标记 - 交易结束活动报名记录0待审核1已通过2已拒绝报名 - 管理员/发布者审核 - 通过/拒绝状态字段在数据库里存的是整型数字在Java层用枚举类定义接口层统一用fromCode方法转换。每个状态变更都收敛到对应的service方法里比如confirmClaim()、markSold()不允许在controller里直接改status字段。5.3 行级权限防止水平越权的关键写法行级权限指的是用户只能操作属于自己的数据这是这类系统里最容易出漏洞的地方。常见的安全漏洞就是前端传一个postId后端没校验当前登录用户是不是作者就直接允许修改或删除。MyBatis-Plus中我用一个标准化写法防住这类越权public void deletePost(Long id) { Long currentUserId SecurityUtil.getCurrentUserId(); LambdaQueryWrapperPost wrapper Wrappers.PostlambdaQuery() .eq(Post::getId, id) .eq(Post::getUserId, currentUserId); // 关键条件 Post post postMapper.selectOne(wrapper); if (post null) { throw new BizException(无权删除该帖子); } postMapper.deleteById(id); }核心思路就是任何涉及修改、删除的操作查询条件必须带上当前用户ID。管理员操作则通过角色校验后走独立的管理接口不经过这个越权判断。5.4 业务闭环发帖、联系、认领、完结的一整条链路拿失物招领场景举例完整的业务流程是同学在东区捡到校园卡发帖选择失物拾取填写物品描述、拾取地点、联系方式。失主在信息广场搜索校园卡看到帖子点击申请认领填写物品细节卡号后四位、姓名等并留下联系方式。发布者收到认领申请列表核对信息无误后点击确认认领状态变为认领中双方线下交接。交接完成后发布者再次确认状态变为已完成。这一系列操作涉及post状态变更、claim认领申请新增、post_comment交流记录三张表。设计时要仔细考虑的是状态变更和认领申请必须分开不能让评论承担状态流转的逻辑否则审核和状态跟踪都无法做到结构化。6. 开发期踩坑实录十个高频问题与完整排查链路6.1 分页查询返回total0或查全量分页插件未注册这是MyBatis-Plus项目里最经典的坑。排查链路如下第一步打开SQL日志观察分页查询是否打印了LIMIT语句。如果日志里只有查询没有LIMIT说明分页插件根本没生效。第二步检查配置类是否注册了PaginationInnerInterceptorConfiguration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }第三步检查项目里是否混用了PageHelper等其它分页插件会造成拦截器冲突。实际经验是MyBatis-Plus分页失效大概率就是忘了注册Bean99%的情况都能用这一步解决。6.2 Vue3修改Element Plus的Tabs标签页样式不生效项目中信息广场首页用了el-tabs做分类切换默认样式不够贴切需要自定义。第一次写发现直接覆盖类名无效原因在于Element Plus组件的样式默认是全局的子组件内部无法用非深层选择器覆盖。正确的做法是给el-tabs加一个自定义class然后用:deep().campus-tabs { :deep(.el-tabs__item) { font-size: 16px; font-weight: 500; .is-active { color: #409EFF; } } }写(deep()时要注意作用域在style scoped和style langscss scoped里语法不同我用的是SCSS。6.3 Long类型主键传到前端精度丢失线上用户反馈删除某条帖子时提示数据不存在。排查后发现是主键精度问题。MyBatis-Plus的ASSIGN_ID生成的是19位雪花IDJS的Number类型只能安全表示16位数字导致前端拿到的id最后几位变成000请求后端时查不到对应记录。全局面修复方案是重写Jackson的Long序列化Configuration public class JacksonConfig { Bean public Jackson2ObjectMapperBuilderCustomizer customizer() { return builder - builder.serializerByType(Long.class, ToStringSerializer.instance) .serializerByType(Long.TYPE, ToStringSerializer.instance); } }这样后端返回给前端的所有Long主键都变成字符串前端不需要改任何代码。6.4 图片上传开发环境正常、部署后404或413开发时图片上传走的是后端本机路径一切正常部署到服务器后通过Nginx访问图片却404。排查发现两个问题第一后端配置文件上传路径是/home/campus/upload/代码里返回的图片URL是/upload/xxx.jpg但Nginx没有配置/upload/的映射。需要在Nginx的server块里加location /upload/ { alias /home/campus/upload/; }第二Nginx默认client_max_body_size只有1m手机拍的照片动辄3-5M大文件上传直接返回413。需要调大client_max_body_size 10m;这条经验告诉大家前端的错误提示不一定指向真实原因先看Network面板返回的状态码404查路径映射413查Nginx请求体限制。6.5 Vue3动态添加/删除表单行reactive数组的操作细节活动报名需要支持动态添加参与人联系方式用户后台需要动态管理标签列表。用Vue3实现时最稳定的做法是用reactive数组承载每一行的数据const items reactiveItemForm[]([{ name: , phone: }]); function addItem() { items.push({ name: , phone: }); } function removeItem(index: number) { items.splice(index, 1); }模板里el-form-item的prop要动态绑定为items[${index}].name的形式因为el-form的校验规则需要通过这个prop找到对应的数据字段。实测容易踩的坑是删除中间一行后校验状态可能会混乱解决办法是给每行渲染时绑定一个稳定的:keyindex或者每行用自增ID。6.6 排查问题的通用顺序不凭感觉猜按链路走踩过几个坑之后我养成了一个固定的排查流程浏览器Network面板看请求是否发出、URL是否正确、状态码是多少、响应体是什么。前端控制台看有没有报错堆栈是JS运行错误还是接口数据异常。后端日志看接口是否收到请求、业务异常是否在预期位置抛出。SQL日志打开MyBatis-Plus的log-impl配置看实际执行的SQL语句是否符合预期。数据库客户端手动执行一遍同样SQL验证数据状态和结果。这条链路由上到下每一步都只解决一层问题绝不跳过。慌的时候最容易犯的错是直接改代码去试试看结果越改越乱。6.7 其它高频小坑速查问题原因解决方案登录接口报401但密码正确Token有效期设置过短或Redis未保存检查JWT生成与拦截器逻辑Vue页面空白无报错路由或动态插槽写错打开浏览器控制台看Vue警告信息列表接口慢未走索引或用SELECT *检查explain执行计划精简查询列本地正常、服务器时间差8小时容器时区未同步启动容器加-e TZAsia/Shanghai这些坑单独看都不难但每一条都能耗掉半天时间。把它们沉淀成文档才是含文档项目真正的护城河。7. 部署上线与项目沉淀从源码到可交付的完整闭环7.1 前后端分离部署这套系统最终采用后端Jar包 前端静态文件 Nginx反向代理的方式部署是最经典的部署模型。后端打包后直接运行mvn clean package -DskipTests nohup java -jar campus-life.jar --spring.profiles.activeprod server.log 21 前端执行npm run build产物dist目录上传到服务器由Nginx托管server { listen 80; server_name your-domain.com; location / { root /var/www/campus-life/dist; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }注意try_files到index.html的写法Vue Router的history模式必须依赖这个配置否则刷新子路由页面会404。7.2 数据库与配置文件分离生产环境和开发环境的数据库地址、Redis地址、文件上传路径都不同所以配置采用application.yml加application-prod.yml的分环境方式。敏感信息数据库密码不要明文写在配置文件里提交到版本库用环境变量取代spring.datasource.password: ${MYSQL_PASSWORD}。7.3 文档沉淀比代码本身更值钱的部分标题里标了含文档这也是我坚持的交付习惯。这套项目的文档由四部分组成环境搭建文档JDK、Maven、MySQL、Node的版本要求和安装步骤确保新人在两台不同电脑上能半小时跑起来。数据库初始化说明建库SQL、初始管理员账号、测试数据导入方式。接口文档每个接口的请求地址、参数说明、示例JSON。我是手动整理的也可以用Postman导出一份方便前端同学直接调试。部署手册从打包、上传、Nginx配置到常见问题的排查命令。文档的价值在项目交付三个月后体现得最明显——当你自己都忘了几周前写的某个接口逻辑时一份清晰的文档能帮你省下一整天的回忆时间。7.4 关于源码文档项目的一点个人体会整个项目从需求梳理到部署上线前后用了大概三周。回头看来真正让它能从一个课设变成一个可交付作品的关键不是用了什么高深框架而是完整走通了需求分析—数据库设计—后端开发—前端联调—部署上线—文档整理这一整条链路。很多朋友手上不缺代码功底缺的是把散落的经验固化下来的习惯。如果你正在做一个类似的校园类信息平台项目我的建议是先把重点放在业务流程上——失物招领怎么闭环、二手交易怎么保证不踩坑、审批权限怎么落实这些比某个框架的新特性重要得多。技术栈能跑通就行业务逻辑清晰才是这类系统真正能拿出来说的东西。这套系统的源码、建表SQL和接口文档我已经整理好放在本地资源库后续如果有同学在做课设或毕设遇到问题比较通用的技术问题欢迎评论区交流。项目本身还在迭代下一步准备加一个校园拼车匹配和一键导出活动参与人名单做完再回来更新。