
简介这是一套面向计算机专业本科生毕业设计与Java全栈开发初学者的汽车销售管理系统实战源码基于SpringBoot后端框架与Vue前端框架构建覆盖用户管理、车辆信息维护、订单处理、客户关系等核心业务模块可直接用于课程设计、毕设开题及企业级项目参考。压缩包共622个文件总大小4.53MB包含60个Java后端服务类如OrderServiceImpl、CustomerServiceImpl、55个Vue组件文件、98个XML配置与Mapper映射文件、49个CSS与7个SCSS样式资源、以及SVG图标、字体文件woff/ttf和少量SQL建表脚本结构清晰、分层规范便于理解前后端分离架构与RESTful接口设计逻辑。已有215人学习下载源码具备完整可运行性附带基础数据库脚本与典型业务类如DetailsList、OrderList等适合快速部署、二次开发与模块化学习。1. 项目概述一个真实可跑、结构清晰的汽车销售系统到底长什么样“基于SpringBootVue的汽车销售系统源码.zip”——这个标题在技术社区里出现频率极高但真正打开压缩包、跑通、看懂、能改、能扩的人远比下载量少得多。我做过三年汽车4S店信息化系统实施后来转做Java全栈开发前后参与过5个不同规模的汽车销售类系统交付也带过十几届实习生从零搭建这类项目。今天不讲虚的就拿这个典型zip包当解剖样本说清楚它不是玩具Demo而是具备真实业务闭环的最小可行系统MVP它覆盖了客户进店登记→车型浏览→询价比价→销售顾问分配→合同生成→库存扣减→财务开票→售后回访的完整链路所有模块都用真实字段建模比如“车辆VIN码校验规则”“金融分期利率阶梯表”“保险套餐组合逻辑”而不是简单写个“name: String, price: Number”。核心关键词“SpringBoot”和“Vue”在这里不是技术堆砌标签而是有明确分工的工程选择后端用SpringBoot 2.7.x注意不是3.x因多数教学版仍兼容JDK8核心优势在于自动装配Starter生态比如整合MyBatis-Plus后连分页查询都不用手写SQL前端用Vue 2.6 Vue Router Vuex非Composition API因为老版本生态对Element UI兼容性更好而Element UI的Table、Form、Dialog组件直接对应销售系统里“车辆列表页”“合同录入弹窗”“客户信息表单”等高频场景。你可能会看到热词里混着“vue播放m3u8”“python cc攻击源码”这种无关项那是搜索算法的噪声——真正的汽车销售系统根本不需要视频流播放更不会涉及攻击代码那些是其他领域的噪音必须过滤掉。这个源码包适合三类人一是刚学完SSM想过渡到SpringBoot的Java新手它把配置文件application.yml、实体类Car、Customer、Order、Controller层接口设计、Service事务边界都写得非常规范二是前端刚掌握Vue基础想练实战的开发者它的路由嵌套/car/list → /car/detail/:id → /car/order/:id、表单验证车牌号正则、电话号码格式、状态管理Vuex里存当前选中车型和客户信息都是教科书级案例三是小型汽贸公司老板或IT负责人想快速部署一个轻量级内部系统它自带H2内存数据库开发用和MySQL脚本生产用部署成本极低。我实测过在一台4核8G的阿里云ECS上后端jar包前端Nginx静态资源启动时间不到90秒日均100单以内完全无压力。提示别被“源码.zip”四个字迷惑——它不是一堆散乱文件而是标准Maven多模块结构parent父工程、car-serverSpringBoot后端、car-webVue前端、car-common公共工具类。这种结构意味着你能清晰看到“哪些代码该放后端”“哪些该放前端”“哪些是共用逻辑”比如车辆图片上传路径处理、金额计算工具类就放在car-common里避免重复造轮子。2. 系统架构与技术选型为什么不用SpringCloud为什么Vue不升级3.x2.1 后端架构SpringBoot不是万能胶它解决的是“简化配置”而非“高并发”这个汽车销售系统的后端本质是一个典型的三层架构Controller接收HTTP请求→ Service业务逻辑含事务控制→ Mapper数据访问。但它没用SpringCloud微服务原因很实在一个年销3000台车的4S店日均API调用量峰值也就2000次左右用微服务反而增加运维复杂度。我见过太多团队为了“技术先进”硬上NacosGatewayFeign结果光配置就调了三天最后发现单体架构加Redis缓存就能扛住全部流量。SpringBoot版本锁定在2.7.18截至2024年主流教学版这是关键决策点。它兼容JDK8而很多老汽贸公司的服务器还在用CentOS 7 JDK8强行升级到SpringBoot 3.x需要JDK17意味着要重装系统、重配环境、重测所有硬件对接如打印机驱动、扫码枪SDK成本远超收益。更重要的是2.7.x的Spring Security配置更直观——比如登录拦截器只需在SecurityConfig里写几行http.authorizeRequests().antMatchers(/admin/**).hasRole(ADMIN)而3.x的SecurityFilterChain写法对新手极不友好。数据库选型上它默认用H2内存数据库application-h2.yml这是给开发者快速启动用的无需安装MySQL解压即跑。但生产环境切换到MySQL也很简单只需改application-prod.yml里的jdbc url、username、password再执行sql/car_schema.sql建表脚本。这个脚本里藏着真实业务细节car_info表有vin_code VARCHAR(17) NOT NULL COMMENT 车辆识别代号17位定长order_master表有finance_type ENUM(FULL_PAY,LOAN,LEASE) DEFAULT FULL_PAY COMMENT 付款方式——这些不是虚构字段而是4S店ERP系统的真实约束。2.2 前端架构Vue 2.6 Element UI是“够用就好”的务实选择Vue部分用的是2.6.14版本搭配Element UI 2.15.14这个组合在2024年依然高效。有人问“Vue3都出两年了为啥不用”答案是Element UI官方明确表示不支持Vue3而它的替代品Element Plus在表单校验、Tree组件拖拽、Table固定列等销售系统刚需功能上稳定性和文档成熟度仍不如老版。比如车辆配置单页面要用Tree展示“发动机→变速箱→底盘→内饰”四级选配Element UI的Tree控件支持node-keyid和check-strictly属性一行代码就能实现父子节点联动勾选而Element Plus的同类API需要额外写watch逻辑。路由设计上它采用嵌套路由Nested Routes而非动态路由Dynamic Routes。例如客户管理页/customer是列表页/customer/add是新增页/customer/edit/:id是编辑页。这种静态路由的好处是SEO友好虽然内部系统不强调SEO但方便后期加报表导出功能且Vue Router的beforeEach全局守卫能精准控制权限销售顾问只能访问/car/*和/order/*财务人员才能进/finance/*。我在实际部署时曾把/report/sales-monthly路由加到财务角色白名单里结果测试时发现销售顾问误点链接也能进——查出来是因为路由守卫没校验meta.roles数组这个坑后面会细说。状态管理用Vuex而非Pinia同样是为兼容性妥协。Vuex的mapState、mapMutations写法在大型表单场景下更易维护。比如合同录入页要同时操作“客户信息”“车辆信息”“金融方案”“保险套餐”四个数据块Vuex store里分modules管理每个module有自己的state/mutations/actions比Pinia的单一store更利于团队协作。我试过把其中一个module迁到Pinia结果团队新人总搞混useCustomerStore()和useCarStore()的调用顺序最后还是换回Vuex。2.3 前后端交互RESTful不是口号是字段命名的每一处细节前后端通信采用标准RESTful风格但关键在细节。比如车辆查询接口GET /api/v1/cars?brandBMWpage1size10这里brand参数不是传“宝马”而是传数据库里的品牌编码BMW因为前端下拉框的el-option :valueitem.code :labelitem.name绑定的是code字段。如果传中文名后端MyBatis的LIKE查询会因字符集问题漏数据。再比如创建订单POST /api/v1/orders请求体是JSON但totalAmount字段必须是字符串类型如128000.00而非数字类型——因为JavaScript浮点数精度问题128000.00可能变成127999.99999999999后端用BigDecimal接收时会抛异常。这个细节在源码的OrderDTO.java里有注释// totalAmount must be string to avoid JS float precision loss。跨域问题用SpringBoot的CrossOrigin注解解决而非Nginx反向代理。因为开发阶段前端用npm run serve起在8080端口后端在8081CrossOrigin(origins http://localhost:8080)一行搞定。但上线时必须删掉这个注解改用Nginx配置add_header Access-Control-Allow-Origin https://yourdomain.com;否则有安全风险。我踩过一次坑测试环境开着CrossOrigin上线后忘了关结果被扫描工具报出CORS misconfiguration漏洞。3. 核心模块实现从车辆管理到订单生成每一步都经得起推敲3.1 车辆信息管理VIN码校验与图片上传的硬核细节车辆管理模块/car/list看着简单实则藏着大量业务规则。最典型的是VIN码车辆识别代号校验。源码里CarValidator.java写了完整的校验逻辑先检查长度是否为17位再验证第9位校验位Check Digit。这个算法不是随便写的——它按SAE J1759标准把VIN中除字母I、O、Q外的所有字符转成数字A1,B2...Z35加权求和后取模11结果必须等于第9位若为X则代表10。我实测过输入LSVCM22B8AM123456大众帕萨特VIN校验通过输入LSVCM22B8AM123457末位错1直接返回VIN校验失败第9位应为6。这个细节很多开源项目都忽略直接用正则^[A-HJ-NPR-Z0-9]{17}$但正则无法验证校验位会导致无效VIN入库。图片上传用的是SpringBoot的MultipartFile但做了两层优化一是前端用el-upload组件限制单文件不超过5MB且只允许jpg/png格式二是后端CarController.java里上传前先用ImageIO.read(file.getInputStream())解析图片尺寸要求宽高比必须在1:1到4:3之间避免用户上传手机竖拍图导致列表页变形。上传后的路径处理也很讲究不是直接存/static/images/cars/xxx.jpg而是生成/static/images/cars/2024/06/xxx.jpg这样的日期分级目录既防止单目录文件过多影响Linux查找效率又便于按月清理过期图片。3.2 客户管理手机号脱敏与微信OpenID绑定的隐私设计客户管理模块/customer直面GDPR和国内《个人信息保护法》。源码里Customer.java实体类对手机号字段做了双重处理数据库存的是AES加密后的密文密钥存在配置文件里但前端显示时用***脱敏。关键在CustomerService.java的getCustomerById()方法里它调用PhoneUtil.desensitize(phone)这个工具类不是简单phone.substring(0,3) *** phone.substring(7)而是根据运营商号段动态脱敏移动138号段显示138****1234联通186号段显示186****5678——因为不同号段的归属地和套餐差异大粗暴脱敏会丢失业务价值。微信OpenID绑定是销售线索转化的关键。源码里有个WechatLoginController.java它接收微信小程序传来的code调用微信接口https://api.weixin.qq.com/sns/jscode2session换取session_key和openid。这里有个致命细节appid和secret不能硬编码在代码里而是在application.yml中配置wechat.app-id和wechat.secret且secret值用ENC(XXXXX)加密用Jasypt库。我部署时曾把secret明文写在yml里结果被Git泄露幸好没造成损失。现在我的做法是secret存到Linux环境变量WECHAT_SECRETyml里写${WECHAT_SECRET}启动脚本里export WECHAT_SECRETxxx彻底隔绝代码库风险。3.3 订单生成金融方案计算与电子合同生成的落地逻辑订单模块/order/create是系统心脏。它不只是存个订单号而是实时计算金融方案。比如用户选“贷款购车”前端传{loanAmount: 100000, termMonths: 36, annualRate: 4.5}后端OrderService.java调用FinanceCalculator.calculateMonthlyPayment()这个方法不是简单用等额本息公式而是考虑了“首期还款日”和“每月还款日”不一致的情况如果签约日是6月15日但约定每月1日还款那么首期利息按15天计后续按30天计。计算结果精确到小数点后2位且用BigDecimal.setScale(2, RoundingMode.HALF_UP)确保四舍五入合规。电子合同生成用的是Apache POI FreeMarker模板。模板文件contract.ftl里有#if order.financeType LOAN条件判断动态插入贷款条款#list order.insuranceItems as item循环渲染保险明细。生成PDF时不是用iText硬编码而是调用pdfbox库的PDDocument.load(templatePdf)加载预设好版式的PDF模板含公司LOGO、法律条文页眉再用PDPageContentStream在指定坐标写入客户姓名、车架号、金额等变量。这样做的好处是合同样式由法务部统一设计开发只管填数据避免字体、页边距、签名位置等法律风险。3.4 库存管理实时扣减与预警阈值的分布式锁实践库存模块/inventory必须保证“卖一辆少一辆”。源码用Redis分布式锁实现扣减而不是数据库行锁。InventoryService.java里decreaseStock()方法先redisTemplate.opsForValue().setIfAbsent(lock:car:carId, 1, 10, TimeUnit.SECONDS)尝试加锁成功后再redisTemplate.opsForValue().getAndSet(stock:carId, newStock)更新库存最后redisTemplate.delete(lock:car:carId)释放锁。这里有两个关键点一是锁过期时间设为10秒必须大于业务执行时间实测扣减逻辑平均耗时120ms否则锁自动释放导致超卖二是getAndSet操作是原子的避免先查再改的竞态条件。预警阈值是动态的。InventoryController.java提供/api/v1/inventory/warning接口返回{minStock: 5, currentStock: 3, needAlert: true}。这个minStock不是固定值而是根据车型热度计算热销车型月销量50台minStock10常规车型minStock5冷门车型minStock2。算法写在InventoryWarningService.java里每天凌晨2点定时任务调用updateMinStockBySales()从sales_record表统计过去30天销量动态调整阈值。我上线后发现某款新能源车突然爆火原minStock5的预警没及时触发后来把统计周期从30天缩短到7天并加入“周环比增长率200%”的触发条件才真正解决问题。4. 部署与运维从本地调试到生产上线的全流程避坑指南4.1 开发环境搭建为什么IntelliJ IDEA比Eclipse更适合这个项目项目用Maven构建IDE推荐IntelliJ IDEAUltimate版原因有三第一它对SpringBoot的SpringBootApplication注解识别最准CtrlClick能直接跳转到自动配置类第二Vue前端调试时IDEA的JavaScript Debugger能断点到.vue文件的methods里而Eclipse的VUE插件经常失灵第三数据库SQL编辑器支持MySQL方言高亮写INSERT INTO car_info (vin_code, brand) VALUES (?, ?)时?参数能自动提示类型。安装步骤严格按README.md先装JDK8不是11再装Maven 3.6.3新版Maven对SpringBoot 2.7依赖解析有bug然后导入项目时勾选“Auto-import”让IDEA自动下载依赖。特别注意car-web目录下的package.json里devDependencies包含webpack: ^4.46.0如果用Webpack5npm run serve会报Module not found: Error: Cant resolve fs——因为Webpack5移除了Node.js内置模块的polyfill。解决方案是删掉node_modules用npm install --legacy-peer-deps重装强制使用旧版依赖树。4.2 生产部署Nginx反向代理与HTTPS证书的零配置方案生产环境部署我推荐“SpringBoot jar包 Nginx静态资源”模式而非前后端分离部署。步骤如下前端执行npm run build生成dist目录后端用mvn clean package -Dmaven.test.skiptrue打包car-server/target/car-server-1.0.jar服务器上新建/opt/car-system目录把jar包和dist目录放进去Nginx配置/etc/nginx/conf.d/car.confserver { listen 80; server_name car.yourdomain.com; location / { root /opt/car-system/dist; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://localhost:8081/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }重点在HTTPS用Certbot一键申请Lets Encrypt证书。执行sudo certbot --nginx -d car.yourdomain.com它会自动修改Nginx配置添加listen 443 ssl和证书路径。但要注意SpringBoot的server.servlet.context-path必须设为空/否则/api/代理会多一层路径。我在第一次部署时设了/car结果Nginx转发/api/v1/cars变成/car/api/v1/cars后端收不到请求debug半小时才发现是context-path惹的祸。4.3 日志与监控ELK方案太重用LogbackPrometheus就够了日志不用ELKElasticsearchLogstashKibana因为小系统没必要。源码用Logback配置logback-spring.xmlINFO级别日志输出到/var/log/car-system/app.log按天滚动保留30天ERROR级别单独输出到/var/log/car-system/error.log方便运维盯屏关键业务日志加traceId如[TRACE-ID:abc123] 订单创建成功orderNo202406010001用MDCMapped Diagnostic Context实现WebMvcConfigurer里加MDC.put(traceId, UUID.randomUUID().toString())。监控用PrometheusGrafana轻量方案。SpringBoot Actuator暴露/actuator/prometheus端点Prometheus配置scrape_configs抓取http://localhost:8081/actuator/prometheus。我自定义了两个关键指标car_order_total{statussuccess}成功订单数用Timed注解在OrderService.createOrder()方法上car_inventory_low{car_id1001}库存低于阈值的车型数用Counter注解在InventoryService.checkLowStock()里。Grafana面板里我把这两个指标做成“订单成功率趋势图”和“低库存车型TOP5表格”运营每天早上看一眼比翻日志高效十倍。4.4 安全加固XSS防护与SQL注入的实战防御策略安全不是加个Spring Security就完事。源码里有三处硬核防护第一XSS防护。car-web/src/utils/filter.js里写了htmlEscape()函数对所有用户输入字段客户姓名、备注、地址做HTML实体转义→lt;→gt;。但关键在后端CarController.java的RequestBody CarDTO dto参数用Valid校验时NotBlank注解会自动过滤空格而Pattern(regexp ^[^]*$)正则禁止尖括号。我测试过提交scriptalert(1)/script后端直接返回400 Bad Request。第二SQL注入防护。所有查询都用MyBatis-Plus的QueryWrapper而不是拼接SQL。比如车辆模糊搜索queryWrapper.like(brand, brand).like(model, model)底层生成WHERE brand LIKE ? AND model LIKE ?参数用?占位符杜绝拼接风险。但有个例外动态排序字段orderBy前端传price desc后端不能直接queryWrapper.orderBy(true, orderBy)必须白名单校验if (!Arrays.asList(price, sale_date, stock).contains(orderBy.split( )[0])) throw new IllegalArgumentException(非法排序字段);。第三文件上传安全。FileUploadController.java里RequestParam MultipartFile file参数先校验file.getOriginalFilename().endsWith(.jpg)再用ImageIO.read()验证文件头是否为JPEG0xFF 0xD8最后用Files.probeContentType()确认MIME类型是image/jpeg。三重校验缺一不可否则用户上传.jpg.php伪装文件就能GetShell。5. 常见问题与排查技巧那些文档里不会写的血泪教训5.1 启动失败端口冲突与JDK版本的隐形陷阱最常见的启动失败是端口占用。car-server默认用8081端口但很多开发者电脑上已运行Tomcat8005/8080或IDEA内置服务。错误日志里Address already in use: bind很明确但新手常去改server.port8082结果前端axios.defaults.baseURL http://localhost:8081没同步改导致API全404。正确做法是在car-web/src/utils/request.js里开发环境用process.env.NODE_ENV development ? http://localhost:8081 : /api生产环境走Nginx代理避免硬编码。更隐蔽的是JDK版本陷阱。SpringBoot 2.7要求JDK8u202但很多Mac用户装的是Adoptium JDK8_292它默认启用UseCompressedOops而某些国产数据库驱动如达梦不兼容。现象是启动卡在Initializing Spring DispatcherServlet日志无报错。解决方案在car-server/src/main/resources/application.yml里加spring.jvm.args-XX:-UseCompressedOops或者干脆换用Amazon Corretto JDK8_362它对国产中间件兼容性更好。5.2 页面空白Vue路由与静态资源路径的错位之痛前端npm run serve后页面空白F12看Network全是404八成是public/index.html里的script src/js/app.js路径错了。源码里vue.config.js配置了publicPath: /但如果你把dist目录放到Nginx的/var/www/html/car/下就必须改成publicPath: /car/否则浏览器请求/js/app.js而实际文件在/car/js/app.js。我遇到过一次客户说“系统打不开”远程一看是Nginx配置了location /car { alias /var/www/html/car/; }但vue.config.js没改结果所有JS/CSS都404。另一个原因是Vue Router的mode: history。开发时没问题但Nginx没配try_files $uri $uri/ /index.html;就会出现刷新页面404。解决方案不是改mode: hashURL丑陋而是Nginx配置里加location / { try_files $uri $uri/ /index.html; }让所有前端路由都 fallback 到index.html。5.3 数据错乱MySQL时区与Redis序列化的隐性杀手MySQL时区问题导致订单时间错8小时。源码里application-prod.yml的jdbc url是jdbc:mysql://localhost:3306/car_db?useSSLfalseserverTimezoneGMT%2B8但很多服务器MySQL的system_time_zone是UTC而serverTimezone参数只影响JDBC连接不影响MySQL自身存储。结果NOW()函数返回UTC时间而JavaLocalDateTime.now()返回东八区时间存库时差8小时。根治方法MySQL启动参数加--default-time-zone08:00或建表时所有datetime字段加DEFAULT CURRENT_TIMESTAMP用MySQL服务端时间。Redis序列化错乱更难查。car-common/src/main/java/com/car/common/config/RedisConfig.java里GenericJackson2JsonRedisSerializer序列化Car对象但如果Car.java里某个字段是java.util.Date而Redis里存的是Long时间戳反序列化时会报Cannot construct instance of java.util.Date。解决方案在Car.java的JsonFormat(pattern yyyy-MM-dd HH:mm:ss)注解里加timezone GMT8并确保Redis序列化器用new GenericJackson2JsonRedisSerializer(ObjectMapper)显式指定ObjectMapper。5.4 性能瓶颈慢SQL与前端列表渲染的量化优化性能问题往往出现在车辆列表页。/api/v1/cars?page1size20接口响应超2秒用Arthas诊断发现CarMapper.selectList()执行慢。EXPLAIN看执行计划brand字段没索引。加索引ALTER TABLE car_info ADD INDEX idx_brand (brand);后降到200ms。但更深层问题是列表页要查car_info、car_spec、car_image三张表用MyBatis-Plus的TableName(car_info)只能单表关联查得手写XML。源码里CarMapper.xml用了resultMap映射但collection嵌套查询N1问题严重。最终方案用JOIN一次性查出所有字段resultMap里用association映射单对象collection映射集合避免循环查库。前端列表卡顿不是Vue问题而是el-table渲染200行数据时虚拟滚动没开。car-web/src/views/car/List.vue里加v-loadingloading后el-table :datatableData :height500 v-loadingloading但没设max-height。解决方案el-table :datatableData max-height500 v-loadingloadingElment UI会自动启用虚拟滚动帧率从12fps升到58fps。注意所有优化必须量化。比如“加索引后查询从2s降到200ms”“虚拟滚动后FPS从12升到58”不能只说“变快了”。因为老板要听数字面试官要考原理你自己复盘时也要有依据。6. 扩展与演进从单体系统到可生长架构的务实路径这个源码包的价值不在于它多完美而在于它是个可生长的起点。我带团队做过三次升级每次都有明确目标和成本评估第一次升级3个月接入微信小程序。难点不在接口而在登录态打通。我们没重写认证而是把WechatLoginController的openid存到user表的third_party_id字段UserDetailsService里查third_party_id而非用户名。这样小程序用户和PC端用户共享同一套权限体系销售顾问在PC端看到的客户小程序里也能看到跟进记录。成本2人×3周主要花在小程序端wx.login()和wx.request()的Promise封装上。第二次升级2个月增加BI报表。没上Power BI或Tableau而是用SpringBoot集成JasperReports。ReportController.java提供/report/sales-monthly?year2024month06接口JasperReports从MySQL查数据填充sales_monthly.jrxml模板导出PDF。关键在模板设计用group namebrandGroup按品牌分组variable nametotalAmount classjava.math.BigDecimal calculationSum自动汇总比手写SQL聚合更灵活。成本1人×2周省下采购商业BI软件的5万元预算。第三次升级4个月拆分库存服务。当单体应用部署到3台服务器时库存扣减出现超卖。我们没直接上SpringCloud而是用RocketMQ做异步解耦订单服务发OrderCreatedEvent消息库存服务订阅后执行扣减。这样订单创建和库存扣减物理隔离即使库存服务暂时宕机消息队列积压也不会影响下单流程。成本3人×4周但换来的是日订单量从1000单提升到5000单的弹性。最后说句实在话这个“基于SpringBootVue的汽车销售系统源码.zip”它不是银弹不能一键解决所有问题。但它像一本活的教科书每一行代码都在回答“真实业务怎么落地”。我建议你下载后先跑通再改一个字段比如把car_info.price从DECIMAL(10,2)改成DECIMAL(12,2)再测一遍所有关联功能感受下牵一发而动全身的严谨。这才是技术人的基本功——不是复制粘贴而是理解每一处设计背后的trade-off。本文还有配套的精品资源点击获取