前阵子有个学弟找我,说毕设想做一个“服装生产管理系统”,但在GitHub上翻来翻去,不是代码太老就是结构太乱,好不容易找到一个SpringBoot+Vue的项目,下载下来却跑不起来。我帮他顺了一遍,又把这套基于Java+MySQL的服装生产管理平台从头到尾搭了一回,发现这类项目表面看就是“增删改查”,可真要把订单、生产工单、工序流转、库存出入库、质检这几条线串起来,能踩的坑比想象中多得多。这篇就把我从设计到实现、再到部署运行的完整思路和细节写下来,既能给毕设/课设做参考,也适合当成SpringBoot和Vue整合的练手项目。你如果不是为了应付答辩,而是真想弄懂一个业务管理系统是怎么长出来的,这篇文章应该能帮你少走不少弯路。
1. 服装生产管理平台到底在管什么:业务模型与模块拆解
1.1 服装生产的业务链条与系统边界
很多初学者上来就问“我要不要做一个完整的ERP”,我一般会先泼一盆冷水:你不可能在毕设周期里复刻一个SAP。服装生产管理平台的核心,是把“接到订单之后厂里怎么干活”这件事说清楚。
一条标准的服装生产业务链大致是这样:销售部接到客户订单,订单里有款式、数量、交期。计划部根据订单拆出生产计划,生产计划再拆成不同的生产工单。工单到了车间,裁剪、缝纫、后道、整烫这些工序按顺序流转。车间领料要管库存,做完了要质检,质检合格入成品库,最后发货。整个过程会产生大量的单据和状态变化:订单从“待审核”到“已下达”,工单从“待生产”到“生产中”再到“已完成”,库存从“在库”变“占用”再变“出库”。
这套系统要管的不是财务,也不是供应链全流程,而是生产执行这一段。所以业务边界要清楚:客户信息要留,但不需要做客户对账;物料要管,但不需要做采购付款;质检要记结果,但不需要做批次追溯厂商。边界划得清楚,数据库设计才不会越做越乱,也更符合一个毕设项目该有的复杂度。
1.2 核心模块划分:订单、工单、工序、库存、质检
我把这个平台拆成了六个核心模块,每个模块互相之间有依赖,但又能独立展示:
| 模块 | 主要功能 | 关键实体 |
|---|---|---|
| 订单管理 | 维护客户订单、订单明细、订单审核与状态跟踪 | customer, sale_order, sale_order_item |
| 生产计划 | 根据订单生成生产计划,计划拆单为工单 | production_plan, production_plan_item |
| 工单管理 | 工单指派、工序流转、进度汇报 | production_order, work_order_process |
| 工序管理 | 维护工序字典、工单工序完成情况 | process, work_order_process |
| 库存管理 | 原料出入库、成品出入库、库存查询 | material, stock, stock_record |
| 质检管理 | 待检任务、质检记录、合格率统计 | quality_check, quality_check_record |
除此之外还有员工管理和系统管理(用户、角色、菜单、字典),这两个模块不直接参与生产,但负责给系统做支撑。从开发角度来说,系统管理里的用户登录和角色权限是几乎所有管理系统的通用骨架,先把它跑通,后面业务功能才能安心写。
1.3 为什么这些模块适合作为毕设/课设选题
我见过太多同质化选题,比如“图书管理系统”“学生选课系统”,不是不行,而是很难讲出深度。服装生产管理的好处在于:它有一个明确的业务主线,可以考察你数据库设计里一对多、多对多的处理能力;它有状态机流转,订单到计划到工单再到入库,中间每一步都有状态变更,这是能写出业务亮点的;它还能体现SpringBoot的事务、Vue的组件通信。
同时它的复杂度又很可控。你不需要接触消息队列、分布式事务这些重装备,一张工单表加一张工序表就能完成核心生产追踪。如果学校评委问“你项目里最复杂的地方是什么”,你可以很自信地说“生产计划拆单时的事务一致性”——这是真实问题,也是能答得上来的问题。
2. 技术选型逻辑:SpringBoot + Vue + MySQL 的搭配依据
2.1 后端为什么选 SpringBoot + MyBatis-Plus
其实做这种管理系统,传统Spring MVC + JSP也能做,甚至用Python的Django也能做六个模块。但既然选了Java,SpringBoot几乎是当代Java后端项目的默认起点。
SpringBoot最大的价值不是“性能有多高”,而是起步成本低:内嵌Tomcat,不用配置一堆XML,一个main方法跑起来。配合MyBatis-Plus,单表的CRUD基本不用写SQL,像订单明细这种简单的增删改查,继承一个BaseMapper就完了。你会发现开发节奏快很多,省下来的时间可以去搞清楚生产计划拆单的逻辑。
用MyBatis-Plus还有一个实际原因:毕设项目里大部分查询是简单的单表查询加分页,偶尔有一两个多表联查。MyBatis-Plus的分页插件很好用,写多表查询也支持自定义SQL,不会像纯JPA那样在复杂查询里容易翻车。代码生成器还可以根据表结构生成实体、Mapper、Service,这对赶工期的课设来说是神器。
2.2 前端为什么选 Vue + Element UI
前端技术栈里,Vue属于“上手快、坑少、资料多”的类型。如果你学过原生JavaScript,再来看Vue的数据绑定和组件化,思维方式很容易转过来。Element UI(或Element Plus)提供了表格、表单、对话框、分页、日期选择这些组件,做管理后台基本够了。
为什么不用React?不是不行,但如果你是毕设,大概率是你自己一个人写,React的生态选择太多,Hooks、状态管理、路由方案都能让人纠结半天。Vue的官方全家桶(Vue Router + Pinia/Vuex + axios)足够规范,遇到问题搜得到的答案也多。
前端目录我建议按下面这样组织,职责清楚,不易乱:
src/ api/ // 按模块封装axios请求 router/ // 路由配置 store/ // 用户信息、菜单、权限等状态 views/ order/ // 订单管理页面 plan/ // 生产计划页面 workorder/ // 工单管理页面 stock/ // 库存管理页面 quality/ // 质检管理页面 system/ // 系统管理页面 components/ // 公共组件 utils/ // 请求封装、工具函数2.3 数据库为什么选 MySQL(含版本选择)
MySQL是管理系统最常见的数据库选择,免费、稳定、生态好。Navicat或者DBeaver都能连,部署到服务器上也方便。要注意的是版本选择:SpringBoot 2.x里用的MySQL驱动版本,到了MySQL 8.0之后,连接驱动变成了com.mysql.cj.jdbc.Driver,而且需要显式配置时区。如果你的学校机房是老MySQL 5.7,驱动用com.mysql.jdbc.Driver没问题;如果本地装的是MySQL 8.0,最好统一按新版来。
我在这个项目里用的就是MySQL 8.0。建表的时候统一用utf8mb4字符集,排序规则用utf8mb4_general_ci,防止中文乱码。数据量控制在几百条到几千条就够演示了,不用刻意造太多数据。
2.4 项目工程结构初始化
一个很好的工程结构能让你后面少加班。后端我建议把包名按模块划分,而不是按技术分层硬切。比如:
com.example.garment controller/ // 控制层 service/ // 业务层,接口 + 实现 mapper/ // 数据访问层 entity/ // 数据库实体 dto/ // 接收前端参数 vo/ // 返回前端视图对象 common/ // 通用返回结果、异常处理、配置类这种结构的好处是:每个模块的controller、service、mapper都在对应包下找得到,而不是所有controller堆在一起。刚开始写可能觉得无所谓,等模块一多,你就能体会到清晰的包结构对排查问题有多大帮助。
3. 数据库设计与核心表关系:从ER图到建表SQL
3.1 基础数据表:客户、款式、物料、员工
基础数据是业务单据的“字典表”。客户表记录客户名称、联系人、电话、地址;物料表记录物料名称、编号、规格、单位、库存数量;款式表记录款号、名称、颜色、尺码;员工表记录工人姓名、岗位、所属车间。
这些表看起来简单,但有几个字段需要提前想好:物料表里的stock字段,到底存总库存还是存每个库位单独库存?我建议毕设阶段直接用一个总库存字段,配合库存记录表做流水追踪。这样查询简单,做报表也容易。员工表最好加一个status字段,用来表示在职/离职,不要删数据,生产工单可能还要用历史员工信息。
基础表示例片段:
CREATE TABLE material ( id BIGINT PRIMARY KEY AUTO_INCREMENT, material_code VARCHAR(32) NOT NULL UNIQUE COMMENT '物料编码', material_name VARCHAR(64) NOT NULL COMMENT '物料名称', spec VARCHAR(128) COMMENT '规格', unit VARCHAR(16) COMMENT '单位', stock DECIMAL(12,2) DEFAULT 0 COMMENT '库存数量', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='物料表';3.2 单据类表:订单、工单、工序记录、出入库单
单据类表是系统的核心,也是最容易设计出问题的地方。以销售订单为例,主表sale_order放订单编号、客户ID、订单日期、订单状态、备注;子表sale_order_item放订单ID、款式ID、颜色、尺码、数量、单价。
这种“主表+子表”的设计,对应业务里一个订单包含多行明细。工单表production_order同样有主表,记录工单编号、关联生产计划ID、生产状态、计划开始/结束时间;工序明细用work_order_process记录每一道工序的完成情况。
生产计划和工单之间的关系很多同学会迷惑。我当时的做法是:生产计划主表关联订单ID,计划明细就是款式和数量;工单则由计划明细进一步生成,一个计划可以生成多个工单。比如一个订单要做1000件T恤,按尺码拆成S码300、M码400、L码300,这就是三个工单,分别对应不同的生产批次。
3.3 关键表字段设计与关联关系
我整理了一张关联关系的对照表,方便你倒推表结构:
| 业务动作 | 主表 | 子表/关联表 | 外键逻辑 |
|---|---|---|---|
| 录入订单 | sale_order | sale_order_item | item.order_id => order.id |
| 生成计划 | production_plan | production_plan_item | plan_item.plan_id => plan.id |
| 计划拆单 | production_order | work_order_process | order.plan_item_id => plan_item.id |
| 工序完成 | work_order_process | quality_check_record | check.work_order_process_id => process.id |
| 出入库 | stock_record | stock_record_item | record.material_id => material.id |
这里最需要留意的是完工数量与计划数量的关联。工单表里会有planned_quantity和completed_quantity,库存表更新时,既要扣原料库存,又要加成品库存,还要在工单上累计完工数量。这三个动作不在一起执行的话,数据很容易对不上。
3.4 数据初始化脚本的处理技巧
项目里要有一个初始化SQL脚本,包含建库、建表、基础字典数据,最好还要带管理员账号和几条演示数据。演示数据别偷懒,一定要造得“像真的”:客户名不要叫“客户1”,要叫“某某服饰有限公司”;款式名不要叫“款1”,要叫“夏季圆领短袖T恤”;物料名就写“32支棉纱”“涤纶缝纫线”。这样答辩演示的时候,页面上的数据一出来就有说服力。
如果脚本里涉及多条insert数据,表之间的外键关系一定要按顺序写:先基础表,再单据表,不然外键约束会报错。另外管理员密码不要直接存明文,先用SpringBoot启动时跑一个加密工具类,把MD5或BCrypt加密后的密文写进SQL里。这个细节很多教程不会讲,但评委会看。
4. 后端接口实现:从登录鉴权到生产计划联动
4.1 统一返回结构与异常处理
一个规范的后端项目,几乎所有接口都会返回统一格式。我定义的返回结构是:
{ "code": 200, "message": "操作成功", "data": { } }Java实体对应这样写:
public class R<T> { private Integer code; private String message; private T data; public static <T> R<T> ok(T data) { R<T> r = new R<>(); r.code = 200; r.message = "操作成功"; r.data = data; return r; } public static <T> R<T> fail(String msg) { R<T> r = new R<>(); r.code = 500; r.message = msg; return r; } }同时写一个全局异常处理器,用@RestControllerAdvice把业务异常统一拦下来。这样控制层不需要到处写try-catch,代码干净很多。
4.2 登录鉴权的实现与绕过方式
毕设项目我不建议引入太重的Spring Security + OAuth2,学习成本高,而且很多人配了半天都没跑通。简单起见,我用JWT做无状态登录:登录成功后,服务端生成一个Token返回前端,前端每次请求带在Header里,后端用拦截器校验Token是否合法。
思路是这样:
- 用户提交用户名密码,Service校验后生成Token。
- 拦截器继承
HandlerInterceptorAdapter(或者实现HandlerInterceptor),对白名单路径(比如/api/login)直接放行。 - 其他请求从Header里取Token,解析失败就返回401。
- 前端在axios响应拦截器里对401做统一跳转到登录页。
开发期有一个小技巧:把拦截器里的放行路径配置成可配置项,可以通过application.yml切换。这样你联调前端页面时不需要带Token就能打开接口文档,做完再关掉放行。
4.3 生产计划生成工单的核心逻辑
这是整个项目里最值得写进答辩稿的业务逻辑。当用户在前端点击“生产计划确认下达”,后端要做的不只是插入一条工单记录,而是要完成一组联动操作:
@Transactional(rollbackFor = Exception.class) public void releasePlan(Long planId) { // 1. 查出计划明细 List<PlanItem> items = planItemMapper.selectByPlanId(planId); // 2. 更新计划状态为“已下达” planMapper.updateStatus(planId, PlanStatus.RELEASED); for (PlanItem item : items) { // 3. 按尺码/批次拆工单 ProductionOrder order = new ProductionOrder(); order.setPlanItemId(item.getId()); order.setStyleId(item.getStyleId()); order.setPlannedQuantity(item.getQuantity()); order.setStatus(OrderStatus.WAITING); orderMapper.insert(order); // 4. 把默认工序列表复制到工单工序表 List<Process> processes = processMapper.selectDefaultByStyle(item.getStyleId()); for (Process process : processes) { workOrderProcessMapper.insert(new WorkOrderProcess(order.getId(), process.getId())); } } }这个方法必须加@Transactional,因为计划下达涉及多张表更新,如果中间有一条SQL失败,已经插入的工单和工序就会变成脏数据。用事务把它们包起来,任何一个环节异常,全部回滚,数据保持一致。
4.4 事务处理与数据一致性(扣库存/记流水)
除了解析工单,库存更新也是典型的需要事务的接口。比如原材料领料出库,你既要更新material表的stock减掉数量,又要往stock_record表插入一条出库流水。如果只更新库存不写流水,后期查账根本查不清;只写流水不更新库存,页面显示又会错误。
更麻烦的是并发问题。两个用户同时领料,如果没有锁机制,都读到库存100,然后各自减10,最后可能变成90,而不是80。毕设项目虽然并发量不大,但为了体现严谨性,可以在更新库存的SQL里加一个条件:UPDATE material SET stock = stock - #{num} WHERE id = #{id} AND stock >= #{num}。如果更新返回0,说明库存不够,直接抛出业务异常,事务回滚。
这个写法比先查再改更可靠,也更容易在答辩时讲清楚。
5. 前端页面实现:Vue组件化设计与跨域联调
5.1 路由设计、侧边栏与权限菜单
前端页面用的Vue Router,路由表和后端菜单表可以做成对应的。最简单的方式是:后端返回用户角色对应的菜单列表,前端根据菜单列表动态生成侧边栏。每个菜单项对应一个路由路径,比如/order/list、/plan/list、/stock/in。
路由需要做权限控制,我用的是路由守卫:
router.beforeEach((to, from, next) => { const token = localStorage.getItem('token'); if (!token && to.path !== '/login') { next('/login'); } else { next(); } });这是最基础的守卫逻辑,够用也容易理解。如果想让菜单按角色区分,可以给菜单表加perms字段,后端根据用户角色动态拼接成一个可访问的路径数组,前端根据这个数组过滤路由。
5.2 axios封装与Token处理
axios不能直接用,一定要封装一层。不然每个页面都写请求头、复制错误处理代码,项目会变得又臭又长。我的封装思路是这样:
// utils/request.js import axios from 'axios'; import router from '@/router'; import { ElMessage } from 'element-plus'; const service = axios.create({ baseURL: '/api', timeout: 10000 }); service.interceptors.request.use(config => { const token = localStorage.getItem('token'); if (token) { config.headers['Authorization'] = 'Bearer ' + token; } return config; }); service.interceptors.response.use( response => { const res = response.data; if (res.code !== 200) { ElMessage.error(res.message || '请求失败'); return Promise.reject(new Error(res.message)); } return res.data; }, error => { if (error.response && error.response.status === 401) { localStorage.removeItem('token'); router.push('/login'); } ElMessage.error(error.response?.data?.message || '网络异常'); return Promise.reject(error); } ); export default service;这样封一层,业务页面里就只需要写:
const res = await listOrder(params); orderList.value = res.records;页面代码会清爽非常多。
5.3 核心页面拆解:订单管理、生产进度看板、库存出入库
订单管理页面是典型的“表格+搜索+新增/编辑弹窗”。组件上建议把订单明细表单独抽成一个子组件,用v-model绑定选中的款式明细列表。这样订单新增页面的代码不会塞到几百行还难维护。
生产进度看板页面是展示项目亮点的好地方。我用一个表格展示所有生产工单,每个工单行显示当前工序名称和进度条。进度条用Element Plus的el-progress,每次工序完成汇报后,前端刷新工单详情接口,进度条跟着更新。这里不需要用WebSocket,一个简单的轮询(每5秒刷新一次)就能有“实时更新”的演示效果。
库存出入库页面要注意表单联动:选择物料后自动显示当前库存;输入出库数量时,当前端本地校验数量超过库存,就直接弹红色提示,不用走后端接口。这个交互细节做好了,用户体验会好很多,评委也会觉得你想得周全。
5.4 和SpringBoot联调时最容易被卡住的跨域/参数格式问题
前后端分离联调时,最常遇到的三个问题我挨个说。
第一个是跨域。前端端口是8080,后端端口是9090,浏览器会拦截跨域请求。解决办法有两个:后端配置CORS过滤器和前端使用Vite/Webpack的proxy。我推荐前端proxy,因为这样部署到Nginx时也能用同样的反向代理逻辑。简单配置示例:
// vite.config.js server: { proxy: { '/api': { target: 'http://localhost:9090', changeOrigin: true, rewrite: path => path.replace(/^\/api/, '') } } }第二个是日期参数。后端用Java 8的LocalDateTime接收前端“2025-01-01 12:00:00”这样的字符串时,默认可能解析失败。解决方案是在application.yml里配置Jackson的日期格式:
spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8第三个是Long类型精度。如果主键ID是雪花算法生成的,前端拿到的Long会丢失精度。最好统一用一个Long序列化器,把ID转成字符串返回给前端。这个坑很隐蔽,等你发现前端拿到的ID最后几位变成了0时,再查就晚了。
6. 环境搭建、部署运行与常见踩坑记录
6.1 本地运行完整流程(后端+前端)
如果你拿到一套源码,第一步一定是先按README把项目跑起来,而不是急着改代码。我建议按这个顺序:
- 在MySQL中创建数据库,导入
sql/init.sql脚本。 - 修改后端的
application.yml,把数据库账号密码改成自己本地的。 - 启动SpringBoot主类,看控制台日志是否报错。
- 前端目录执行
npm install,然后npm run dev。 - 浏览器打开前端地址,用脚本里的管理员账号登录。
后端启动后,可以先用浏览器直接访问几个查询接口,比如/api/order/list?page=1&size=10,看到返回JSON就说明后端没问题。然后再从前端页面点菜单,看接口是否通。
6.2 MySQL连接和初始化脚本的坑
我见过太多人倒在这一步。常见错误有几种:
第一种是Public Key Retrieval is not allowed。MySQL 8.0默认用caching_sha2_password插件,连接时会报这个错。解决办法是在JDBC URL后面加allowPublicKeyRetrieval=true&useSSL=false,比如:
jdbc:mysql://localhost:3306/garment?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai第二种是初始化脚本执行失败。多半是因为脚本开头用了CREATE DATABASE,但你当前登录的用户没有建库权限。解决办法是手动建好库,然后用USE database;再执行脚本里的建表语句。如果脚本里含有外键,中途失败会导致后续表建不出来,这时要清空重来。
第三种是Unknown column之类的错误,通常是因为脚本版本和实体类版本不一致。源码里的实体类字段如果比SQL多,启动时MyBatis-Plus不会报错,但查询接口会报找不到字段。这时要仔细比对SQL和实体,别急着怀疑框架。
6.3 前后端端口配置与Nginx部署要点
本地开发时后端端口我习惯用9090,前端用Vite默认的5173(Vue3)或8080(Vue2)。如果后端的端口改成了别的,记得同步修改前端代理target。
部署到服务器时,用Nginx把前端打包后的静态文件和服务端接口放在同一个域名下。我的习惯配置是:
server { listen 80; server_name localhost; root /usr/share/nginx/html; index index.html; location /api/ { proxy_pass http://127.0.0.1:9090/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { try_files $uri $uri/ /index.html; } }注意最后的try_files,这一行必须加。否则Vue Router用了history模式,刷新页面就会404。
6.4 打包发布时的经典问题
后端打包成jar之后运行,容易遇到两个问题。一个是静态资源路径问题,如果你用了文件上传功能,上传文件保存的路径要配置成外部目录,不要打包进jar里。另一个是运行时的字符集问题,在Linux下启动jar时,最好在脚本里加-Dfile.encoding=utf-8,不然中文乱码。
前端打包时常见的问题是接口请求404。如果前端配置了baseURL: '/api',那打包后部署到Nginx时,必须确保Nginx把/api开头的请求转发到后端,否则页面能打开,但数据加载不出来。
7. 作为毕设/课设/学习项目,如何二次开发和答辩加分
7.1 学习这个项目源码的正确顺序
很多人下载了源码后喜欢从头到尾一行行读,结果读两天就放弃了。我建议换个顺序:先把项目跑起来,然后顺着一条业务线走一遍,比如从新建订单开始,到生成生产计划,再到生成工单,最后做一次出库。走通之后,再打开对应的Controller、Service、Mapper,一层层往下看。因为你有操作数据了,看到代码时脑子里会自动把数据和逻辑对应起来,理解速度快很多。
接着再去看你怎么改代码最容易。比如想把“订单状态”从下拉框改成接口自动流转,就去改订单状态字段的set方法。自己去动一次代码,比读十遍印象都深。
7.2 可以加分的扩展点:报表、权限细化、消息提醒
如果想让项目真正有亮点,不要只停留在增删改查。我推荐几个不太费时间但效果很好的扩展方向:
- 用ECharts做一个生产统计报表,展示每周产值、工序合格率、库存周转率。报表能非常直观地让评委看到“这个系统真的有价值”。
- 把权限从前端路由层面细化到按钮层面,比如“新增”按钮和“删除”按钮用不同权限控制。这个改动不大,但很能体现你对RBAC的理解。
- 加一个“生产异常提醒”,比如工单超过计划交期没完成,在首页弹一个公告提醒。简单的实现就是查询超期工单列表,在Dashboard展示,不需要做消息队列。
7.3 我在测试和答辩演示时踩过的小教训
我说几个真实经历,希望你别再踩。
第一个是演示时千万别点“删除”按钮然后刷新页面。如果删的是订单,关联的子表和工单都跟着没了,评委看到空空的列表会觉得很没底气。演示前最好准备一份“核心流程脚本”,每一步点哪里都提前跑一遍。
第二个是数据库里的时间字段,如果你用CURRENT_TIMESTAMP,要注意时区。服务器时区如果不是中国时区,页面显示的时间会差8小时。这个在答辩前一定要看一眼。
第三个是如果前端调用了后端接口报500,先看后端控制台的异常栈,不要只在前端弹窗提示里找原因。很多人被“系统错误”四个字搞得一头雾水,其实后端日志早就告诉你哪一行SQL出错了。
第四个建议也很实用:答辩前把项目打包成jar,用服务器上的Nginx部署好,然后准备一台备用电脑。万一你自带电脑的电源适配器坏了,也不至于现场开天窗。虽然听起来很琐碎,但每年都有同学栽在这些细节上。
最后再分享一个我自己的习惯:拿到一套源码后,先别急着删除任何“看起来没用”的文件。很多初始化配置、工具类、SQL片段,都是前人踩坑之后留下的解决方案。你删了可能一时跑得通,等换到另一台电脑或者部署环境一变,问题就会冒出来。学项目,先学会敬畏这些“隐藏代码”,再慢慢去理解它们,比一上来就重构靠谱得多。