基于SpringBoot的窗帘报价管理系统:从智能报价引擎到业务闭环实现
我做毕业设计的时候选了个挺有意思的题目基于SpringBoot的窗帘报价管理系统正式一点的名字叫“云帘”软装布艺报价及销售管理系统。班里大部分同学都在做商城、博客、校园二手交易我却挑了这么个垂直行业的小项目当时还被人问“窗帘有什么好做的”。做下来才发现这个选题比想象中值钱得多窗帘报价不是简单地增删改查它背后有一套复杂的业务规则——不同窗型、不同面料、不同褶皱倍率报价结果能差出好几倍。系统最初版本叫“飞卷”取意窗帘成卷、报价飞快后来扩展成覆盖报价、订单、生产、收款全流程的“云帘”平台。这个系统解决的是软装门店最现实的问题手工量窗、Excel报价、口头砍价、纸质订单效率低不说报价口径还不统一同一个客户两次报价可能差出几百块。系统核心是一个智能报价引擎录入窗户尺寸选择面料和款式自动算出用料米数、辅料费用和总价确认报价后转入订单再往下走生产、安装、收款形成完整业务闭环。如果你是做Java毕设、想找SpringBoot项目切入点的同学或者本身在帮传统门店做信息化改造这篇文章应该都派得上用场。我会把项目从业务拆解、表设计、核心算法到权限控制和踩坑实录完整过一遍。1. 业务分析与项目定位窗帘报价为什么值得做一个系统1.1 一间软装门店的日常困境先还原一个真实场景。客户进店说家里三室两厅需要给客厅落地窗、主卧飘窗、次卧平开窗和书房转角窗配窗帘。销售员要做的事包括去现场量每扇窗的宽度和高度问清楚客户的遮光需求、风格偏好带客户翻面料册子雪尼尔、高精密、真丝棉、遮光布每米价格从几十到几百确定是装罗马杆还是静音轨道再考虑要不要加水波帘头、花边、挂球、铅坠。这一套下来人工算价至少二三十分钟而且很容易漏项。漏了一个轨道报价就得返工算错一个褶皱倍率门店就要自己贴钱。更麻烦的是报价口径不统一。同一个窗不同销售员可能用不同的褶皱倍率给客户报出来的总价能差几百块。老板想月底看哪个品类卖得好、哪类窗型利润高翻Excel要翻半天还经常发现数据对不上。窗帘这个品类有一个特点它不像普通商品一样有固定标价每一单都得按尺寸和配置单独算价这就天然需要一个“计算型”的系统而不是普通的商品展示加购物车。1.2 两个名字与一条主链路的由来我在标题里写了两个名字“飞卷”和“云帘”。其实它们是同一个项目的两个阶段。初版只做报价计算叫“飞卷”意思是输入尺寸、一键出价报价单像窗帘一样“卷”出来。后来把订单、生产、收款、报表都加进来改名叫“云帘”面向的是整个软装布艺门店的销售管理。这种命名在毕设题目里很常见建议大家在论文里把名字的由来写清楚答辩时也是一个不错的引入点。系统要服务三类角色店长关注全店销售数据、利润和折扣审批销售员负责接待、量窗、录报价、跟单财务或库管负责订单审核、生产安排和收款登记。主链路就一句话客户管理 - 量窗记录 - 智能报价 - 折扣审批 - 确认订单 - 按窗排产 - 安装交付 - 收款对账 - 销售报表。这个闭环就是整个系统的骨架后面的表设计、接口设计都围绕它展开。1.3 为什么不用通用商城模板也有人在选题时纠结过直接套一个电商商城模板改改商品和订单不就行了实际做下来就知道差别在哪。标准电商是“标价购买”商品价格是静态的窗帘是“定制计算”每个订单都要重新算价。窗帘报价的输入是窗户尺寸、面料、款式、辅料、折扣输出是明细项、合计金额和利润估算跟商城完全不是一个模型。ERP系统通常太重一套下来成本几十万门店根本用不起所以这种轻量的垂直报价系统正好卡在中间比Excel专业比ERP便宜。对毕设来说这反而给了一个很好的展示空间你可以在系统里植入行业算法、策略模式、状态机这些有深度的设计而不是千篇一律的CRUD。2. 技术选型与工程结构SpringBoot 3 MyBatis Plus MySQL2.1 后端技术栈与选型理由先上完整的技术清单都是我实际验证过能跑通的组合。技术版本/选型理由JDK17SpringBoot 3 基线版本长期支持稳定SpringBoot3.2.x自动配置强大内嵌Tomcat适合业务系统MyBatis Plus3.5.x单表CRUD零SQL复杂查询用LambdaQueryWrapperMySQL8.0关系型业务数据事务能力强Redis6.x/7.x登录状态、字典缓存Vue 3 Element Plus前端表单、表格组件成熟适合管理后台EasyExcel3.x导出报价单、销售报表省内存MinIO可选布料图片、报价单附件对象存储为什么持久层选 MyBatis Plus 而不是 Spring Data JPA我的理由是报价系统里多表关联查询非常多比如报价单主表加明细加客户加审批记录MyBatis Plus 的 QueryWrapper 和自定义 XML 都很顺手JPA 在复杂查询时要么写JPQL要么写原生SQL对很多人来说学习成本反而更高。MyBatis Plus 还提供逻辑删除、乐观锁、自动填充这些开箱即用的功能对毕设项目来说能省出大量时间。2.2 单模块还是多模块包结构怎么分毕设项目我强烈建议用单模块够用且好演示。真正的关键是把包结构设计清楚做到逻辑上的分层。我的工程包结构是这样com.yunlian ├── controller // 接口层只做参数接收与响应包装 ├── service // 业务层核心计算、事务都在这里 ├── mapper // 数据访问层继承BaseMapper ├── entity // 数据库实体字段与表一一对应 ├── dto // 入参对象接收前端请求参数 ├── vo // 出参对象返回前端展示数据 ├── common // 统一响应、分页对象、常量 ├── config // 配置类如Redis、拦截器、Jackson ├── utils // 工具类如价格引擎、Excel导出 └── exception // 全局异常与自定义业务异常这个结构看起来常规但有两个细节值得注意。第一VO 和 DTO 必须和 Entity 分离Entity 里的 createTime、updateTime、deleted 这些字段绝不能直接暴露给前端报价单的计算中间结果比如每个明细的折扣前金额、折扣后金额、节省金额都要在 VO 中定义好而不是从 Entity 里随便取字段拼。第二Controller 要瘦、Service 要厚。所有业务判断都写在 Service 层Controller 只做参数绑定和调用这样也方便在 Service 上直接加事务和权限注解。2.3 统一响应与全局异常后端接口的“标准格式”前后端分离项目里接口返回格式必须统一。我封装了一个 Result 结构是 code、message、data 三段式code 为 200 表示成功其他为失败或特殊状态码。效果就是每个 Controller 方法直接返回 Result.success(业务数据)不需要每个接口各自拼 JSON。配合 ResultCode 枚举把错误码集中管理比如 400 参数错误、401 未登录、403 无权限、500 系统异常、1001 报价单已失效这样前后端排查问题效率高很多。全局异常处理是保证接口稳定的关键。用 RestControllerAdvice 加 ExceptionHandler 做统一出口业务异常抛出 BusinessException参数校验异常抛出 MethodArgumentNotValidException数据库异常单独处理。特别是数据库唯一键冲突如果不单独捕获前端收到的堆栈信息又长又吓人捕获后会返回“该手机号已存在”这样友好的提示。这部分在答辩时基本都会被问到建议好好准备。3. 数据库设计一套报价单如何拆成六张核心表3.1 表结构总览窗帘报价系统的表可以分成三组基础数据表、业务单据表和系统权限表。基础数据包括客户、面料、辅料、窗户信息业务单据包括报价单、报价明细、订单、生产记录、收款记录权限表就是用户、角色、用户角色关联。一张报价单从录入到成交路径大致是先建客户档案再录量窗信息然后基于面料和辅料生成报价单主表和明细客户下单后转成订单订单按窗户拆成生产任务安装完成后登记收款。表之间的关系用一句话概括客户1对多报价单报价单1对多明细报价单1对1转订单订单1对多生产任务。3.2 核心表结构与关键字段下面给出报价和订单最核心的几张表设计字段以实际项目为准做了精简。CREATE TABLE quote ( id bigint NOT NULL AUTO_INCREMENT, quote_no varchar(32) NOT NULL COMMENT 报价单号规则QDyyyyMMdd序号, customer_id bigint NOT NULL COMMENT 客户ID, customer_name varchar(64) DEFAULT NULL COMMENT 冗余客户姓名列表不join, total_fabric_amount decimal(10,2) NOT NULL COMMENT 面料金额, total_accessory_amount decimal(10,2) NOT NULL COMMENT 辅料金额, total_amount decimal(10,2) NOT NULL COMMENT 总价, discount_amount decimal(10,2) NOT NULL COMMENT 优惠金额, final_amount decimal(10,2) NOT NULL COMMENT 应收金额, status tinyint NOT NULL COMMENT 0草稿 1已报价 2已确认 3已转订单 4已失效, sales_user_id bigint NOT NULL COMMENT 销售员ID, create_time datetime DEFAULT NULL, update_time datetime DEFAULT NULL, deleted tinyint DEFAULT 0, PRIMARY KEY (id), KEY idx_customer_id (customer_id), KEY idx_sales_user_id (sales_user_id) ) ENGINEInnoDB COMMENT报价单主表; CREATE TABLE quote_item ( id bigint NOT NULL AUTO_INCREMENT, quote_id bigint NOT NULL, window_id bigint NOT NULL COMMENT 窗户ID, fabric_id bigint NOT NULL, fabric_name varchar(64) DEFAULT NULL, window_width decimal(8,2) NOT NULL COMMENT 窗宽单位米, window_height decimal(8,2) NOT NULL COMMENT 窗高单位米, fold_ratio decimal(4,2) NOT NULL DEFAULT 2.00 COMMENT 褶皱倍率, piece_count int NOT NULL COMMENT 幅数, fabric_meters decimal(10,2) NOT NULL COMMENT 面料用量米数, unit_price decimal(10,2) NOT NULL COMMENT 面料单价, fabric_amount decimal(10,2) NOT NULL COMMENT 面料金额, accessory_amount decimal(10,2) NOT NULL COMMENT 辅料金额, subtotal decimal(10,2) NOT NULL COMMENT 小计, PRIMARY KEY (id), KEY idx_quote_id (quote_id) ) ENGINEInnoDB COMMENT报价明细表;这段 SQL 里有几个设计决定值得说道。第一个是金额和尺寸全部用 decimal金额用 decimal(10,2)尺寸用 decimal(8,2)单位统一是米而不用厘米避免前端展示和计算时到处换算。第二个是客户姓名、面料名称做了冗余列表页展示时减少联表查询这是典型的空间换时间。第三个是状态字段用 tinyint 加代码里的枚举映射不要在数据库里直接存中文否则改状态名要改库而且接口返回也要做转换。3.3 面料与辅料报价的“价格基础数据”报价引擎要计算就离不开面料和辅料两张基础表。面料表 fabric 的关键字段包括面料名称、类别遮光布/雪尼尔/高精密/绒布/真丝棉等、单价、计价方式按米还是按平米、布幅宽、损耗率、颜色/花纹描述、图片URL。辅料表 accessory 的关键字段包括名称罗马杆/静音轨道/水波帘头/花边/挂球/铅坠等、单位、单价、是否按长度为计费单位。这里要注意有的辅料按米算有的按个算所以辅料表里加一个 charge_type 字段区分是按长度计费还是按数量计费价格引擎在汇总时会按不同方式处理。面料价格是会波动的所以我在面料表里加了一个 price_version 字段。每次改价不直接update旧记录而是插入一条新版本记录报价单明细里会冗余当时的 unitPrice。这样做的直接好处是一个月后客户来问“我当时这个面料多少钱”系统里能查到历史报价快照。这本身也是报价系统比Excel强很多的地方——Excel在布料涨价后覆盖保存历史价格就彻底没了。4. 智能报价引擎这个项目最有技术含量的部分4.1 窗帘用料的行业计算逻辑窗帘报价最核心的公式不是简单的“长乘宽”而是由一系列行业规则组成的计算链条。先把关键参数说清楚窗宽 W实际量得的窗户宽度单位米窗高 H实际量得的窗户高度单位米褶皱倍率 K一般取 1.82.2决定了窗帘挂起来的褶皱效果。遮光布通常取 1.8纱帘取 2.0高档绒布可能取 2.2布幅宽 F面料门幅常见 1.5m 定宽、2.8m 定高折边量 S上下卷边、包边增加的米数习惯取 0.2m计算思路分两种情况。如果是“定高布”也就是面料幅宽 2.8m 作为高度方向买宽用料宽度 窗宽 × 褶皱倍率再考虑拼接总米数 用料宽度 / 布幅宽向上取整× 布幅宽实际就是按整幅买。如果是“定宽布”面料 1.5m 是宽度方向先算幅数 ceil(用料宽度 / 1.5)再算每幅需要的高度 窗高 折边量最终面料米数 幅数 × 每幅高度。这个“幅数”概念是窗帘报价和普通面积计算最大的区别也是报价引擎里最容易出bug的地方。为了让大家看得更直观我举个具体例子。一扇窗宽 3.2m、高 2.6m做 2.0 倍褶皱用 1.5m 定宽布。用料宽度 3.2 × 2.0 6.4m幅数 ceil(6.4 / 1.5) 5 幅。每幅高度 2.6 0.2 2.8m。面料总米数 5 × 2.8 14m。如果这布单价 68 元/米面料费用就是 14 × 68 952 元。再加轨道费用 窗宽 × 轨道单价比如 3.2 × 30 96 元。这扇窗的基础报价就是 1048 元。系统里把每一步中间结果都存到明细表客户问起来可以逐项解释。4.2 价格引擎的代码实现与 BigDecimal 取整这套逻辑落到代码里我用了一个独立的 CurtainCalcEngine 类输入参数封装成 QuoteCalcParam输出封装成 QuoteCalcResult。核心计算片段如下public QuoteCalcResult calc(QuoteCalcParam param) { BigDecimal width param.getWindowWidth(); BigDecimal height param.getWindowHeight(); BigDecimal ratio param.getFoldRatio(); BigDecimal fabricWidth param.getFabricWidth(); // 1. 计算需要的布料宽度 BigDecimal needWidth width.multiply(ratio); // 2. 计算幅数向上取整 BigDecimal pieceCount needWidth.divide(fabricWidth, 0, RoundingMode.CEILING); // 3. 每幅高度 窗高 折边量 BigDecimal heightPerPiece height.add(HEM_AMOUNT); // 4. 面料总米数 BigDecimal totalMeters pieceCount.multiply(heightPerPiece); // 5. 面料金额 BigDecimal fabricAmount totalMeters.multiply(param.getUnitPrice()); // 6. 辅料金额轨道按窗宽计费 BigDecimal railAmount width.multiply(param.getRailPrice()); // 7. 小计 BigDecimal total fabricAmount.add(railAmount); return QuoteCalcResult.builder() .pieceCount(pieceCount.intValue()) .fabricMeters(totalMeters) .fabricAmount(fabricAmount) .railAmount(railAmount) .totalAmount(total) .build(); }这段代码里有几个坑都是我实际踩过的先说最典型的两个。第一个是除法必须指定舍入模式。BigDecimal 的 divide 如果不指定精度和 RoundingMode遇到除不尽的情况直接抛 ArithmeticExceptionnon-terminating decimal expansion。我第一次跑测试数据时装了一个窗宽 1.1m 的用例算幅数 1.1×2.0/1.51.4666...系统当场崩了。改成 divide(fabricWidth, 0, RoundingMode.CEILING) 后结果是向上取整到 2 幅这才符合行业习惯——布料只能多买不能少买。第二个是金额计算的精度控制。单价是 decimal(10,2)算出来的金额可能是 68 × 14 952.00看起来没毛病但如果让系统保留多位小数再传到前端就会出现 952.00000001 这种显示。建议所有金额计算最后都要调 setScale(2, RoundingMode.HALF_UP)。我在金额字段的 setter 处没有做统一处理而是在每个引擎入口出口都有一步“收拢精度”的操作这个习惯比到处用 DecimalFormat 靠谱得多。4.3 阶梯折扣与审批流报价系统的“生意感”真实门店不会按标价卖东西系统必须支持折扣。我设计了 DiscountStrategy 策略接口具体的实现有三类普通客户不打折用量达到 30 米以上享受 95 折50 米以上 9 折会员在折扣基础上再 95 折。为什么用策略模式因为门店促销活动三个月就会调一次如果把这些判断用 if-else 堆在报价 Service 里每次调策略都要改业务代码改完还要重新测试整个流程。用策略模式每种折扣是一个独立的类新增一种活动就新增一个类部署时通过配置中心或数据库字典切换既不碰旧逻辑也方便回滚。折扣还要配合审批流。我做了这样的规则销售员默认最多能给 95 折想给 9 折必须申请店长审批超过 8.5 折则要店长加财务双重审批。审批记录表 approve_record 保存报价单ID、申请理由、申请金额、审批人、审批意见、审批时间。报价单的最终应收金额必须从“审批通过后的价格”重新计算而不是销售员在前端随便填否则审批就形同虚设。像这类规则在毕设论文里写清楚设计动机比堆技术点更有说服力。5. 订单管理与状态流转从报价到交付的完整闭环5.1 状态机把订单流程“锁死”报价经过客户签字确认之后就转成正式订单。订单状态我定义了六个待确认、待生产、生产中、待安装、已完成、已取消。从待确认到生产中的迁移并不是任何状态下都能跳的比如待生产才能改交货日期已完成才能发起售后已取消不能再回到生产中。如果只用一个 status 字段到处 set状态很容易被绕乱。我用一个状态机配置类来管理合法迁移private static final MapOrderStatus, SetOrderStatus TRANSITIONS new HashMap(); static { TRANSITIONS.put(OrderStatus.PENDING_CONFIRM, Set.of(OrderStatus.PENDING_PRODUCE, OrderStatus.CANCELED)); TRANSITIONS.put(OrderStatus.PENDING_PRODUCE, Set.of(OrderStatus.PRODUCING, OrderStatus.CANCELED)); TRANSITIONS.put(OrderStatus.PRODUCING, Set.of(OrderStatus.PENDING_INSTALL)); TRANSITIONS.put(OrderStatus.PENDING_INSTALL, Set.of(OrderStatus.COMPLETED)); }所有状态迁移都在一个 service 方法里统一校验非法迁移直接抛 BusinessException“订单当前状态不允许该操作”。这样做的好处是不管以后从后台还是APP进来改状态都必须走同一个入口规则不会出现两套。答辩时把状态机画出来评委一眼就能看到你对业务闭环的理解。注意我这里强调“画出来”是指论文里的示意图不是项目必须输出的图表组件。5.2 按“扇”排产订单子表的细节设计一个订单可能包含好几扇窗户而工厂生产是按扇推进的。整单一起排产有个现实问题落地窗和飘窗可能不在同一天加工完成客户收到货的时间也不一样。所以我在订单下建了一张 order_window 子表记录订单里每一扇窗户对应的尺寸、面料、褶皱倍率、生产状态、安装日期。主订单状态是汇总值子表各自推进自己的生产状态。这样销售员在后台可以看到“这单里落地窗已经在安装了飘窗还在排产”而不是笼统地看到一个“生产中”。这张子表让我在演示时非常加分。我做了个订单详情页左侧是订单主信息右侧是一扇窗一张卡每张卡上有布料图片、尺寸、状态、预计安装时间。你现场演示的时候点一张卡的“开始生产”整个页面状态跟着变比纯列表直观得多。5.3 收款记录与销售报表订单完成后进来的是钱。收款表 payment_record 记录每笔收款订单号、收款类型定金/尾款/退款、支付方式现金/微信/支付宝/刷卡、金额、收款人、时间。财务对账时按天汇总导出 Excel 清单用 EasyExcel 实现。相比直接用 POIEasyExcel 在导出大数据量时的内存占用低很多而且支持填模板导出生成一套带格式的《门店日销售汇总表》很方便。关于报表我做了一个销售看板按日/按月统计订单数、销售额、退款额按面料分类统计销量占比按销售员排行。图表用 ECharts 渲染后端接口返回的是聚合查询结果。这里有一个实用的优化报表查询走独立的统计 SQL而不是把订单全部查出来在内存里算因为订单多了内存统计会慢到没法看。6. 权限设计店长、销售员、财务各看各的数据6.1 RBAC 角色权限模型系统有角色就有权限问题。我用 Spring Security JWT 做认证和授权权限模型是最标准的 RBAC用户表、角色表、用户角色关联表。三个初始角色店长、销售员、财务/库管。接口层用 PreAuthorize 控制访问比如删除报价单只允许店长操作创建报价单销售员和店长都可以导出财务报表只有财务和店长可以。前端菜单也做了权限控制不同角色登录后看到的菜单项不同这个用 Vue Router 的动态路由实现。JWT 本身是无状态的但毕设项目里我加了一点“有状态”的处理把 JWT 的 tokenId 存到 Redis设置过期时间用户修改密码或账号被禁用时把 tokenId 拉进黑名单。这样既保留 JWT 的轻量又能解决“改密码后旧 token 还能用”的尴尬。这个设计在面试或答辩时能聊很久。6.2 行级数据权限同一个角色看到的数据也得分RBAC 解决的是“谁能访问这个接口”但解决不了“销售员张三不应该看到销售员李四的报价单”。这种按数据行划分的权限叫数据权限。我在 MyBatis Plus 的查询层做了处理用一个自定义拦截器读取当前登录用户信息执行报价单列表查询时自动拼接条件。销售员只能查 user_id 自己的数据店长不加限制财务按部门或全店范围查。实现上有个细节拦截器里拼接条件容易让 SQL 变得不可控尤其跟分页、排序组合时。我的做法是只有在特定方法的 Mapper 接口上才启用数据权限拦截器不是全局生效。在毕设项目里重点把这个逻辑讲清楚就够了为什么需要行级权限、怎么用拦截器实现、怎么避免误伤其它查询。这个问题是答辩的高频追问提前想好答案能省很多现场卡壳的风险。6.3 文件存储布料图片用 MinIO 还是本地磁盘系统里每款布料要有平铺图和效果图报价单详情页要能展示布料小图。图片上传的存储方案毕设里常见的有两种存本地磁盘路径、存对象存储。如果项目要部署在服务器上演示本地磁盘是最省事的配置一个虚拟路径映射即可。但热门技术词里也有人提到 springboot 整合 minio如果你的毕设想展示企业级技术栈把图片上传到 MinIO 是很好的加分项。MinIO 的接口兼容 S3 协议Spring Boot 里集成很简单上传后返回一个可访问的 object URL前端直接img展示。我的建议是如果时间充裕用 MinIO如果赶时间本地磁盘 Nginx 映射完全够用。不要为了炫技术把项目复杂化重点是业务闭环能跑通。布料图片的 URL 存到 fabric 表里报价明细里冗余一张缩略图 URL这样列表页不需要再 join 面料表取图片。7. 实战踩坑记录与答辩准备7.1 我踩过的四个大坑第一个坑是金额精度。早期我用 double 类型存金额测试时 0.1 0.2 不等于 0.3 这种经典问题直接出现在报价单上。后来全部改成 BigDecimal前端传过来的金额字符串也用 BigDecimal 构造坚决不用 Double.parseDouble。凡是涉及金额的相加、相乘、折扣必须走 BigDecimal 并指定舍入模式这个习惯我建议从第一天就养成。第二个坑是 LocalDateTime 的序列化。Spring Boot 默认把 LocalDateTime 序列化成数组格式前端拿到 2024,5,20,15,30,0 一脸懵。统一在 Jackson 配置里注册 JavaTimeModule并且设置格式为 yyyy-MM-dd HH:mm:ss。这个配置很小如果不处理前后端时间显示就会出各种奇奇怪怪的格式。第三个坑是 MyBatis Plus 逻辑删除和唯一索引冲突。我给客户表加了 deleted 字段做逻辑删除同时又给手机号字段建了唯一索引。结果用户 A 删除了再新增一个同手机号的用户 B数据库直接报唯一键冲突——因为逻辑删除的记录还在表里。解决方案是把 deleted 字段放进联合唯一索引也就是 UNIQUE KEY uk_mobile_deleted (mobile, deleted)这样不同 deleted 值的记录不算重复。这个坑比较隐蔽网上资料不算多踩过一次就很酸爽。第四个坑是列表页的 N1 查询。报价单列表页如果每行都要查一次明细去算金额100 条报价单就是 1 100 条 SQL。后来改成先批量查询所有报价单再用 in 查询所有明细在内存里按 quoteId 分组组装。数据量上来后这个优化的效果体感非常明显。7.2 常见问题速查表问题现象可能原因解决方式报价单算出来的金额比预期高褶皱倍率设置过高或幅数向上取整检查面料幅宽与倍率配置窗帘是整幅销售不能按小数买BigDecimal 除法报 ArithmeticException未指定舍入模式divide 必须带 scale 和 RoundingMode前端时间显示成数组LocalDateTime 未做序列化配置注册 Jackson JavaTimeModule设置时间格式删除客户后无法新增同手机号客户逻辑删除与唯一索引冲突联合唯一索引加上 deleted 字段改了密码旧 token 仍有效JWT 无状态未做服务端校验Redis 黑名单或版本号校验列表页接口非常慢循环查询明细的 N1 问题批量查询 内存分组7.3 答辩演示准备数据与话术做完项目一定要准备一套完整的演示数据而且最好是一套有“故事感”的数据。我的演示数据是一户三室两厅业主客厅落地窗、主卧飘窗、次卧平开窗选了两款遮光布和一款纱帘报价 8000 多然后打折审批、转订单、排产、收款、看报表。整套流程走下来五分钟能把系统所有核心能力都覆盖到。我见过不少同学答辩时现场临时录数据录到一半报错或者数据太少看不了报表场面非常尴尬。提前把演示脚本写好每一步点哪里、用什么台词解释至少走两遍。话术上有一个建议不要只说“我用了 SpringBoot 和 MyBatis Plus”要说“这个系统的核心是一个报价引擎它把窗帘行业的幅数计算和褶皱倍率固化成可配置参数再结合策略模式实现折扣体系”。技术点要落到业务上评委听到的不是名词而是你解决问题的思路。做这个项目给我最大的体会是垂直行业的小系统最值钱的不是框架版本多新而是你有没有真正吃透业务规则。我把报价引擎反复算了三遍拿家里真实窗帘尺寸验证发现手工算和系统算能对上时才觉得这个系统真的“活”了。如果你正准备做类似的毕设项目我的建议是先找一间真实窗帘门店聊一小时记录他们的报价流程和一张真实的报价单这比对着文档设计三天都管用。有了业务细节表结构、算法、界面、甚至论文目录都会自己长出来。

相关新闻

SpringBoot+Vue构建企业级知识管理系统:全栈开发实战解析

SpringBoot+Vue构建企业级知识管理系统:全栈开发实战解析

直接说结论:这套企业级知识管理系统,是典型的前后端分离架构项目,后端用SpringBoot做主体框架,MyBatis负责数据库持久层操作,MySQL存数据,前端Vue做单页应用。整体源码结构完整,适合拿来当毕业设…

2026/10/10 14:19:03 阅读更多 →
多语言 NER 三强横评:GLiNER 2.5、spaCy、HanLP,中文谁最能打

多语言 NER 三强横评:GLiNER 2.5、spaCy、HanLP,中文谁最能打

多语言 NER 三强横评:GLiNER 2.5、spaCy、HanLP,中文谁最能打 【免费下载链接】gliner2.5-multi-v1 项目地址: https://ai.gitcode.com/hf_mirrors/fastino/gliner2.5-multi-v1 多语言命名实体识别(NER)的选型&#xff0c…

2026/10/10 14:19:03 阅读更多 →
C# WinForm迷宫游戏开发核心难点解析

C# WinForm迷宫游戏开发核心难点解析

简介:这是一份面向C#初学者与高校课程设计学生的WinForm桌面游戏开发实战项目,聚焦迷宫游戏核心功能实现,覆盖GUI界面搭建、算法集成与交互逻辑开发等关键能力训练。资源包含165个文件,以11个C#源码文件(.cs&#xff0…

2026/10/10 14:19:03 阅读更多 →

最新新闻

K-means聚类定K值:手肘法原理与Matlab实现

K-means聚类定K值:手肘法原理与Matlab实现

做聚类分析时,估计每个人都卡在过同一个问题上:K-means的K值到底该设成几?随手填个3或者5,跑出来的结果总觉得哪里不对劲,簇与簇之间边界模糊,甚至有些样本被硬塞进了一个和它八竿子打不着的簇里。我之前接…

2026/10/10 15:08:22 阅读更多 →
Notepad++ 8.3.3绿色版实战指南:大日志分析与配置批量处理

Notepad++ 8.3.3绿色版实战指南:大日志分析与配置批量处理

简介:本资源是为开发者与系统管理员定制的 Notepad 8.3.3 增强版集成包,聚焦高效文本处理与工程化编辑需求。在官方版本基础上,预装并调优了12款高实用性插件,涵盖文件对比(Compare)、拼写校验(…

2026/10/10 15:08:22 阅读更多 →
Cursor iOS:智能体工作流的移动端延续而非远程控制

Cursor iOS:智能体工作流的移动端延续而非远程控制

1. 这不是“手机遥控电脑”,而是智能体工作流的终端延伸最近在某跨平台系统开发中,团队内部测试了一个新场景:一位在通勤地铁上的工程师,用手机打开刚上线的 Cursor iOS 应用,直接调起昨晚写到一半的 Python 数据清洗脚…

2026/10/10 15:08:22 阅读更多 →
Python多模态虚假新闻检测源码实战:BERT与LightGBM融合项目复现指南

Python多模态虚假新闻检测源码实战:BERT与LightGBM融合项目复现指南

简介:这份资源面向计算机相关专业的本科生与研究生,以及需要完成毕业设计、期末大作业或课程设计的学习者,提供一套基于Python的虚假新闻检测多模态识别完整项目源码与文档说明。项目融合文本与图像等多模态特征,采用BERT等预训练…

2026/10/10 15:08:22 阅读更多 →
在线逆向优化:从决策行为实时反推目标函数

在线逆向优化:从决策行为实时反推目标函数

1. 这不是传统优化,而是一场“边跑边学”的决策革命你有没有遇到过这样的场景:一个物流调度系统刚上线,但客户订单的分布规律和运输成本结构每天都在变;或者一个智能灌溉控制器部署在新地块,土壤湿度响应模型根本没来得…

2026/10/10 15:08:22 阅读更多 →
C语言实现PL/0编译器:从词法分析到递归下降的完整编译流水线

C语言实现PL/0编译器:从词法分析到递归下降的完整编译流水线

简介:PL/0编译程序C语言版源码是一份面向编译原理课程设计与实验的经典教学代码,适合计算机专业学生、教师及自学者研读。资源将N.Wirth设计的PL/0语言编译程序用C语言完整呈现,共两个文件,分别为C源文件与头文件,涵盖…

2026/10/10 15:07:21 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起:为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念,很多人会觉得它离自己很远——不就是天上的星星怎么转吗?但如果你正在做航天任务规划、遥感数据接收、星座设计,甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起:为什么你的代码里到处都是重复逻辑刚入行那会儿,我写过一个用户管理模块,注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么,能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介:这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目,以Boss直聘岗位数据为对象,适合用作毕业设计、课程设计或期末大作业。资源包共38个文件,约246KB,以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 11:14:25 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 11:14:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →