每年到了毕设季总有一批同学找我聊同一个题目老师用Java写股票管理系统行不行行当然行但真正把它做成一个能过查重、能跑通演示、能扛住答辩老师追问的系统跟拿个开源项目改个LOGO是两回事。这篇文章我想把带学生做Java毕设时攒下的经验尤其是股票管理系统这类题目的完整落地链路一次性讲透从选题评估、功能拆解、技术选型到数据库设计、核心代码、实测踩坑最后再说说论文和答辩怎么准备。这个题目适合谁适合后端基础一般、想稳扎稳打完成毕设的同学也适合想用“管理系统”作为敲门砖、顺便刷一刷Java基础集合、事务、并发的人。它的优点是数据模型足够标准、业务边界清晰、演示效果好缺点是如果不控制范围很容易做成一个“什么都有但什么都没做完”的半成品。下面我按自己做项目时的实际推进顺序来写你可以直接照着这个思路走。1. 为什么“股票管理系统”是毕设选题的性价比之选1.1 这个选题天然适合展示基本功先想明白一件事毕设管理系统类题目千千万为什么股票管理系统值得选我的判断标准是“数据关系能否自然体现数据库设计能力”。股票系统里有用户、股票、行情、持仓、交易流水、资金账户、自选列表这些实体之间的关联关系非常标准画ER图、建表、写SQL都能拿出东西来。相比“图书管理系统”这种做了无数遍的题目股票系统明显更有区分度相比“秒杀系统”这类高并发题目它又不需要复杂的中间件工作量可控。业务规则也足够清晰注册后有模拟资金买入扣钱加持仓卖出减持仓加资金每笔操作生成流水。这些规则说难不难说简单又比纯CRUD多了一层业务逻辑正是答辩时老师喜欢追问的部分。换句话说这个题目做出的系统既能体现“你会写代码”又能体现“你懂一点业务”两全其美。1.2 开题前先给系统划好边界我见过太多人把毕设题目想得过大要实时行情、要股票预测、要社群交流、要做App……最后学期末交上来一个空壳。非常不建议这样。你可以在论文“展望”部分写这些但系统里不要全做。我给学生的建议是把系统定位成“模拟交易教学演示系统”明确以下边界不接入真实券商接口不接第三方实时行情行情数据用本地静态数据或批量导入的模拟数据。交易撮合简化为“按当前最新价即时成交”不去模拟真实盘口的买卖队列。不做真实支付、不做资金存取用户注册后自动发放一笔初始模拟资金。涨跌停限制、T1、手续费等规则可以作为扩展项时间充裕再加不加也不影响论文完整性。把这些边界提前写进开题报告和需求分析后面开发和写论文都会轻松很多。老师看到你把边界写得清楚第一印象就是“这个学生思路清晰”。1.3 一个明确的系统雏形是什么样结合标题来看这套股票管理系统至少要覆盖以下闭环管理员维护股票基础信息与行情数据用户可以注册登录浏览股票列表和行情K线把感兴趣的股票加入自选用模拟资金买入、卖出查看自己的持仓和交易流水最后能在个人中心看到资产盈亏统计。我把这个闭环作为整个项目的“骨架”。当你把闭环跑通再去扩展用户管理、数据统计、公告发布等外围功能系统的完整度已经足够撑起一篇本科论文。记住一个原则先做纵向闭环再做横向扩展不要一上来就铺摊子。2. 系统功能拆解与角色权限先把“做什么”钉死2.1 两类角色权限边界清清楚楚股票管理系统虽然功能多但角色就两类管理员和普通用户。权限设计越简单越好不要把RBAC搞得花里胡哨否则代码量和答辩复杂度都会上升。我常用的方案是给用户表加一个role字段1表示管理员0表示普通用户后端拦截器按角色判断接口访问权限。管理员能做的事股票基础信息管理、行情数据录入与导入、用户列表查询与禁用、系统公告发布。普通用户能做的事注册登录、浏览股票行情、维护自选列表、模拟买入卖出、查看持仓与交易记录、查看个人资金账户。核心原则是普通用户只能动自己的数据不能改系统和股票数据管理员不做交易操作。这个原则写清楚后数据库查询语句的WHERE user_id ?条件就有了依据不会出现越权bug。2.2 功能模块清单这里我列一份可以直接抄进开题报告的功能清单用户模块注册、登录、信息修改、密码加密存储股票管理模块股票新增、编辑、删除、分页查询、关键字搜索行情管理模块录入每日行情数据、展示最近K线、模拟涨跌幅计算自选股模块添加自选、取消自选、展示自选列表及实时模拟价格模拟交易模块买入、卖出、持仓查询资金账户模块初始资金发放、资金变动流水、可用余额查询交易流水模块按时间倒序展示用户全部交易记录系统管理模块管理员对用户启停用、数据初始化重置这8个模块做下来代码量大概在1.5万行左右含前端工作量适中正好适合一个学期。2.3 关键业务规则要在编码前定下来以下是股票系统最核心的业务规则建议做成文档贴在自己电脑前买入用户买入数量必须大于0买入金额 成交价 × 数量可用余额必须不小于买入金额买入成功后持仓数量增加可用余额减少交易流水记录“买入”。卖出卖出数量必须不大于该股票持仓数量卖出金额 成交价 × 数量卖出成功后持仓数量减少可用余额增加交易流水记录“卖出”。持仓均价更新买入时新均价 (旧持仓市值 本次买入金额) / (旧持仓数量 本次买入数量)卖出时不改变持仓均价只减少数量。这些规则看起来简单但它们是答辩时老师最常问的“业务逻辑细节”也是代码里最容易出bug的地方。比如持仓均价很多同学直接用(旧均价 成交价) / 2这就是错的后面算总盈亏时全部乱掉。建议先把规则写在文档里再动手写代码。3. 技术选型与项目结构Spring Boot MyBatis Plus 这套组合为什么稳3.1 选型不是越新越好主动权才是关键每次有学生问我“能不能用最新的Spring Boot 3.x、JDK 21”我都先反问一句你能保证折腾环境不花掉两个星期吗毕设的技术选型原则是“稳定靠谱、资料多、你熟悉”。目前Java毕设最稳的组合还是 Spring Boot 2.7.x MyBatis Plus MySQL 5.7/8.0前端可以用 Vue 2 Element UI也可以直接用 Thymeleaf 服务端渲染。JDK 用 8 或 11 都行不要为了赶新潮选一个你完全没接触过的版本。有人会问用JSP/Servlet行不行也行但你手写控制层、事务、分页的工作量会大很多答辩时也容易被追问底层细节。Spring Boot的价值在于自动配置和约定优于配置让你把精力集中在业务逻辑上这是毕设最需要的。MyBatis Plus更适合这种单表CRUD占大头的项目手写SQL只留给多表联合查询和复杂统计开发效率能翻一倍。3.2 推荐项目包结构我强烈建议用标准的 Controller-Service-Mapper-Entity 四层结构包名按功能模块划分。下面这棵目录树可以直接拿来用com.example.stock ├── StockApplication.java ├── common │ ├── Result.java // 统一返回结果 │ ├── ResultCode.java │ └── GlobalExceptionHandler.java ├── config │ ├── CorsConfig.java // 跨域配置 │ ├── JwtInterceptor.java // 登录拦截器 │ └── WebMvcConfig.java ├── controller │ ├── AuthController.java │ ├── UserController.java │ ├── StockController.java │ ├── TradeController.java │ ├── PositionController.java │ ├── HistoryController.java │ └── AdminController.java ├── service │ ├── UserService.java │ ├── StockService.java │ ├── TradeService.java │ ├── PositionService.java │ └── DashboardService.java ├── mapper │ ├── UserMapper.java │ ├── StockMapper.java │ ├── StockQuoteMapper.java │ ├── PositionMapper.java │ └── TradeRecordMapper.java ├── entity │ ├── User.java │ ├── Stock.java │ ├── StockQuote.java │ ├── Position.java │ └── TradeRecord.java └── dto ├── BuyRequest.java ├── SellRequest.java └── LoginRequest.java按模块划分的好处是论文里的“系统设计”章节可以直接引用这个结构画包图、类图都方便答辩被问“某个功能在哪”时一眼就能指出来。很多同学喜欢按技术层分包controller包放所有控制器项目一大了找文件能找到怀疑人生别踩这个坑。3.3 核心依赖与配置照抄即可pom.xml 关键依赖如下parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependency dependency groupIdcommons-codec/groupId artifactIdcommons-codec/artifactId version1.15/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId scopeprovided/scope /dependency /dependencies注意一点Spring Boot 2.7.x 搭配的 jjwt 0.9.1 缺javax.xml.bind依赖JDK8以上会报错记得补上dependency groupIdjavax.xml.bind/groupId artifactIdjaxb-api/artifactId version2.3.1/version /dependencyapplication.yml 关键配置server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/stock_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0 jwt: secret: your-secret-key expire: 604800serverTimezoneAsia/Shanghai这个配置必须写不然数据库时间会比当前时间差8小时后面踩坑部分我会详细说。4. 数据库设计股票系统最容易被问倒的是“钱怎么算”4.1 七张核心表别贪多我把这套系统的表精简为7张足够支撑全部功能表名用途关键字段sys_user用户表id, username, password, role, status, balance, create_timestock_info股票基础信息表id, stock_code, stock_name, industry, total_market_cap, statusstock_quote行情数据表id, stock_code, trade_date, open_price, close_price, high_price, low_price, volumestock_position持仓表id, user_id, stock_code, stock_name, quantity, avg_price, update_timetrade_record交易流水表id, user_id, stock_code, stock_name, trade_type, price, quantity, amount, trade_timeuser_watchlist自选表id, user_id, stock_code, create_timesys_notice公告表id, title, content, create_time这套设计覆盖了“股票数据管理、用户操作、交易记录”三个层面ER图也容易画。不要额外加太多冗余表否则写代码和写论文都是负担。4.2 金额字段为什么必须用 decimal踩过坑的人都懂这是一个答辩高频问题也是实际开发中最容易出bug的地方。如果用double存金额买入0.1万股、价格3.14159时计算出来的金额会变成314.15899999999996给用户展示非常难看累计盈亏统计多了还会出现几分钱的误差。正确做法是统一使用decimal类型。我建议的字段定义是金额字段decimal(18,2)本金、成交金额、余额都用它价格字段decimal(10,3)股票价格保留3位小数足够数量字段int或decimal(10,0)因为A股交易单位是“股”取整即可均价字段decimal(10,3)均价保留3位展示时自己再四舍五入到2位Java代码层面所有金额计算统一使用BigDecimal不要用double运算然后再转。我在下面的代码实现章节里会专门写。4.3 核心建表SQL附初始化数据CREATE TABLE sys_user ( id int NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL, password varchar(64) NOT NULL, role tinyint DEFAULT 0 COMMENT 0普通用户,1管理员, status tinyint DEFAULT 1 COMMENT 1启用,0禁用, balance decimal(18,2) DEFAULT 1000000.00 COMMENT 模拟资金余额, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;CREATE TABLE stock_position ( id int NOT NULL AUTO_INCREMENT, user_id int NOT NULL, stock_code varchar(10) NOT NULL, stock_name varchar(50) NOT NULL, quantity int NOT NULL DEFAULT 0 COMMENT 持仓数量, avg_price decimal(10,3) NOT NULL DEFAULT 0.000 COMMENT 持仓均价, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_user_stock (user_id,stock_code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;CREATE TABLE trade_record ( id int NOT NULL AUTO_INCREMENT, user_id int NOT NULL, stock_code varchar(10) NOT NULL, stock_name varchar(50) NOT NULL, trade_type varchar(10) NOT NULL COMMENT BUY/SELL, price decimal(10,3) NOT NULL, quantity int NOT NULL, amount decimal(18,2) NOT NULL COMMENT 实际成交金额, trade_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;初始化数据时我会插入一个管理员账号密码用MD5或BCrypt加密、若干个正常的用户以及沪深两市约20只常见股票的模拟行情。没有演示数据答辩时系统打开是一片空白那场面太尴尬了。4.4 数据初始化技巧用 CommandLineRunner 还是 SQL 脚本两种都可以我更推荐在resources目录下放data.sql配合spring.sql.init配置应用启动时自动执行。也可以写一个DataInitializer类实现ApplicationRunner启动时检查用户表是否有数据没有就用代码插入。用代码初始化的好处是可以在里面加密密码生成不用手工处理SQL里的哈希值。我实际用的是“SQL脚本初始化基础数据 代码初始化行情演示数据”的组合因为行情数据往往需要按日期批量生成SQL写起来太累代码里循环生成更轻松。答辩时你可以说这是“方便演示的模拟数据”老师能理解本来这就是模拟系统。5. 核心代码实现与关键逻辑从登录到下单5.1 JWT登录与拦截器密码必须加密存储密码绝对不能明文存数据库这是涉及用户信息安全的基本底线。建议用BCryptPasswordEncoderSpring Security自带或MD5 salt。用Spring Security只是为了它的加密工具不用接管整个安全框架不然过滤器链会把你折腾崩溃。我用DigestUtils.md5DigestAsHex做一次简单加盐处理演示够用但答辩时说清楚“生产环境会用BCrypt”就稳了。登录接口逻辑前端传用户名和密码后端查用户校验密码生成JWT返回前端。JWT里只放 userId 和 role设置过期时间。拦截器里从请求头取token解析成功才放行失败返回401。Component public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (OPTIONS.equals(request.getMethod())) { return true; } String token request.getHeader(Authorization); if (token ! null token.startsWith(Bearer )) { token token.substring(7); String userId JwtUtil.parseToken(token); if (userId ! null) { request.setAttribute(userId, userId); return true; } } response.setStatus(401); request.getRequestDispatcher(/common/unauthorized).forward(request, response); return false; } }5.2 股票列表分页与搜索MyBatis Plus 一行搞定股票列表无非是按代码或名称模糊搜索再分页返回。用 MyBatis Plus 的LambdaQueryWrapper就行public PageStock pageStocks(int current, int size, String keyword) { LambdaQueryWrapperStock wrapper new LambdaQueryWrapper(); if (StringUtils.hasText(keyword)) { wrapper.like(Stock::getStockCode, keyword) .or() .like(Stock::getStockName, keyword); } wrapper.orderByAsc(Stock::getStockCode); return stockMapper.selectPage(new Page(current, size), wrapper); }这里有个小细节搜索的关键字可能会同时包含汉字和数字如果既按代码匹配又按名称匹配要注意or的括号问题否则会把条件“或”到别的条件上。用wrapper.and(w - w.like(...).or().like(...))包一层更稳妥。5.3 模拟买入卖出的核心逻辑事务、锁、BigDecimal 一个不能少这是整个系统技术含量最高的地方也是答辩时最值得讲的地方。买入接口需要做四件事查用户余额、查最新价、校验余额是否足够、扣减余额并增加持仓。这里最大的坑是并发问题如果用户同时发起两笔买入请求两个线程都读到余额够然后一起扣款余额就变负数了。我用的方案是数据库行锁配合事务。在查询用户余额时加select ... for update把这条用户记录锁住其他线程必须等当前事务提交后才能改。代码逻辑如下Service public class TradeServiceImpl implements TradeService { Resource private UserMapper userMapper; Resource private StockQuoteMapper stockQuoteMapper; Resource private PositionMapper positionMapper; Resource private TradeRecordMapper tradeRecordMapper; Override Transactional(rollbackFor Exception.class) public void buy(BuyRequest req) { User user userMapper.selectByIdForUpdate(req.getUserId()); if (user null || user.getStatus() ! 1) { throw new BusinessException(用户不存在或已被禁用); } StockQuote quote stockQuoteMapper.findLatestByStockCode(req.getStockCode()); if (quote null) { throw new BusinessException(该股票暂无行情数据); } BigDecimal price quote.getClosePrice(); BigDecimal amount price.multiply(BigDecimal.valueOf(req.getQuantity())); if (user.getBalance().compareTo(amount) 0) { throw new BusinessException(可用余额不足当前余额 user.getBalance()); } user.setBalance(user.getBalance().subtract(amount)); userMapper.updateById(user); Position position positionMapper.findByUserIdAndStockCode(req.getUserId(), req.getStockCode()); if (position null) { Position newPosition new Position(); newPosition.setUserId(req.getUserId()); newPosition.setStockCode(req.getStockCode()); newPosition.setStockName(req.getStockName()); newPosition.setQuantity(req.getQuantity()); newPosition.setAvgPrice(price); positionMapper.insert(newPosition); } else { // 新均价 (旧持仓数量 * 旧均价 本次买入金额) / 新持仓数量 BigDecimal oldMarketValue BigDecimal.valueOf(position.getQuantity()) .multiply(position.getAvgPrice()); BigDecimal newMarketValue oldMarketValue.add(amount); int newQuantity position.getQuantity() req.getQuantity(); position.setQuantity(newQuantity); position.setAvgPrice(newMarketValue.divide(BigDecimal.valueOf(newQuantity), 3, RoundingMode.HALF_UP)); positionMapper.updateById(position); } TradeRecord record new TradeRecord(); record.setUserId(req.getUserId()); record.setStockCode(req.getStockCode()); record.setStockName(req.getStockName()); record.setTradeType(BUY); record.setPrice(price); record.setQuantity(req.getQuantity()); record.setAmount(amount); tradeRecordMapper.insert(record); } }这个代码里最关键的就是selectByIdForUpdate在Mapper里是这么写的Select(SELECT * FROM sys_user WHERE id #{userId} FOR UPDATE) User selectByIdForUpdate(Param(userId) Integer userId);为什么要锁用户行而不是锁股票行因为余额是用户的扣钱和加持仓必须保持一致性锁用户行能确保同一用户的所有交易串行化不同用户之间互不影响并发性能也不会太差。这个细节答辩时主动讲出来老师会觉得你是真懂并发控制。卖出逻辑类似核心是先查持仓校验持有数量足够更新持仓数量和用户余额。卖出不改变均价所以只改数量不改avg_price。如果卖出后持仓数量为0最好删掉持仓记录这样持仓列表不会出现一堆数量为0的“僵尸股票”。5.4 持仓与K线数据接口前端展示要配图表持仓接口查当前用户的持仓列表每个持仓需要根据最新行情计算“当前市值”和“浮动盈亏”。这个用SQL联表或者Java里循环都能算数据量小循环简单直接。K线数据接口则从stock_quote表按日期升序取某只股票的记录返回日期、开、收、高、低这五个字段前端用ECharts的K线图组件直接画。建议后端返回字段名统一用英文字段date, open, close, high, low前端不用做额外映射省事很多。如果你想让系统看起来更有“实时感”可以写一个定时任务每30秒随机更新一下最近一条行情的收盘价页面轮询刷新。这个属于锦上添花加上之后演示效果会好很多。实现也不复杂用Scheduled(cron 0/30 * * * * ?)注解就行。6. 实测踩坑从开发到答辩最容易翻车的5个问题6.1 数据库时间差8小时坑了多少人症状前端页面显示的交易时间比本地时间慢了8小时。原因MySQL驱动连接串里没有指定serverTimezone默认用了UTC中国在东八区所以差8小时。解决办法就是配置里写serverTimezoneAsia/Shanghai同时MySQL的连接参数加上useSSLfalse避免握手警告。还有一点如果前端用到new Date()直接展示后端返回LocalDateTime会带 T 格式建议后端统一返回yyyy-MM-dd HH:mm:ss字符串或者在Jackson配置里设置全局格式spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai6.2 金额计算出现一堆小数根源是 double我踩过的真实bug用double计算 0.1 * 3.14159结果不是 0.314159 而是0.31415899999999996。前端展示还好但累计盈亏表里几个数一加误差就出来了。修复方案是上面说的数据库decimalJavaBigDecimal所有涉及金额的运算必须用BigDecimal除法要指定精度和舍入模式。上线前的自测里一定要拿几组极端数据验证买入0.01元价格的股票、卖完所有持仓、余额刚好等于成交金额。6.3 并发交易导致余额变负数百试百灵如果你把买接口写好用Postman同时发两个请求多半能复现余额变负数。原因就是两个事务同时读到同一个余额各自判断“够”然后都做减法。解决就靠锁要么用Transactionalselect for update行锁要么用乐观锁表加version字段更新时SET balance balance - ? , version version 1 WHERE version ?。毕设系统并发量不大行锁方案最简单也方便答辩时讲清楚。这个坑建议你一定要测试真出问题时修复的过程都可以写进论文的“系统测试”章节这是加分项。6.4 前后端联调时的跨域问题前后端分离开发时前端跑在8081端口后端8080端口浏览器会拦截跨域请求。解决办法是后端加CORS配置最省事的写法是Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }注意allowedOrigins(*)和allowCredentials(true)不能同时使用会报错。用allowedOriginPatterns(*)代替就行。如果部署时后端和前端同域这个配置可以去掉但开发阶段必须留着。6.5 打包部署后接口404静态资源也没了很多同学开发时一切正常一mvn package打成jar包再运行页面打不开接口404。原因通常是前端分离部署时前端dist没有并到后端resources里。我的建议是毕设阶段就别搞独立前端部署了把前端build后的dist文件复制到src/main/resources/static目录下和Spring Boot一起打包启动后直接访问http://localhost:8080就是完整系统演示和答辩都非常方便。这个操作放在开发最后阶段做前期开发用前后端分离模式效率更高。7. 源码如何变成论文与答辩演示过来人的几点建议7.1 论文结构别从绪论硬憋先写图表再补文字我见过许多同学先写绪论写了一天还在憋“研究背景”。我自己带学生的经验是反着来先画ER图、功能结构图、架构图再根据图表去写系统设计系统实现章节直接对照代码截图和核心代码讲最后再回头补绪论和技术介绍。这样写论文的速度至少快一倍而且前后逻辑对得上。本科论文的章节顺序通常是绪论背景、意义、国内外现状、相关技术介绍Java、Spring Boot、MySQL、MyBatis Plus、需求分析功能需求、非功能需求、用例图、系统设计总体架构、模块设计、数据库设计、系统实现分模块贴核心代码加界面截图、系统测试功能测试用例表、测试结果。每一章都有模板可循关键是不要出现“代码和描述不一致”的低级错误。7.2 演示脚本要刻意设计别现场乱点答辩演示时间通常只有5到8分钟你必须提前设计好“最佳路径”。我推荐的演示流程是启动后端浏览器打开登录页输入管理员账号登录。展示股票列表和搜索功能随便搜一个关键词。进入某只股票详情页展示K线图。退出管理员注册一个新用户或直接登录普通用户。演示买入操作选两只股票各买100股展示余额变化和持仓变化。演示卖出卖出一部分再展示持仓和交易流水。打开个人中心展示总资产和盈亏统计。整个流程控制在5分钟内。现场演示最怕的就是“点哪个都没反应”所以演示前至少完整走三遍尤其是数据库要记得重置成初始状态把之前测试的数据清掉。7.3 答辩提问预判用java基础就能应对的高频问题答辩老师最常问的几类问题其实都能在Java基础范围内找到答案为什么选Spring Boot答简化配置、快速搭建、生态成熟适合快速开发管理系统。事务是怎么实现的答用Transactional注解底层是Spring的声明式事务管理默认遇到运行时异常回滚。怎么保证并发买不超扣答数据库行锁select for update 事务或者乐观锁版本号。如果用户量变大你这个系统有什么瓶颈答单库单表性能瓶颈在数据库可以引入Redis缓存热点行情再考虑分库分表。这个答辩点可以作为论文的“扩展与展望”。密码为什么不是明文答MD5加盐或BCrypt防止拖库后用户密码泄露。这些问题本质上就是在考你的Java面试基本功平时刷过的HashMap、线程池、JVM调优之类的八股文在答辩时未必直接用得上但基础的事务、锁、集合、异常处理必须说得清楚。建议答辩前把你自己写的Service层每个方法过一遍做到“指哪讲哪”比背十篇八股文都管用。带过那么多轮毕设我最有体会的一点是股票管理系统这个题目的上限和下限都极高。下限是抄个CRUD框架换换皮老师一眼看穿上限是你能把资金计算、并发扣减、持仓均价这些业务细节讲出花来再配合一套完整的数据设计和测试用例就是妥妥的良到优。你不需要做什么高深的算法也不需要分布式微服务只需要把每一行代码的逻辑讲清楚、把每一个bug的修复过程记录明白这个项目就已经超过了绝大多数同期作品。如果你正在为毕设发愁希望这篇东西能帮你少走点弯路。
