做毕设,我觉得最怕的不是技术难,而是题目没选好。题目选好了,后面所有事情都顺。这个题号07467的“基于JavaWeb的美妆消费辅助决策网站”,名字看着有点长,但拆开看其实特别清晰:技术栈是JavaWeb,业务是美妆消费辅助决策,交付物是一个可以跑起来的完整网站。我第一次看这个题的时候就觉得它讨巧——美妆是当下年轻人聊天绕不开的话题,消费决策又是一个非常真实的痛点,把它做成一个JavaWeb项目,既能用到Servlet、JSP、MySQL这些基本功,又有足够具体的业务故事可以讲。
这种项目适合谁?一个是正在选毕设题目的计算机专业学生,另一个是想用一个完整项目提升简历竞争力、但没想好做什么的Java自学者。它难度不大,不需要你会微服务、不需要你会分布式,但你得把JavaWeb的核心知识串一遍:前端页面、Servlet控制层、后端业务逻辑、数据库操作,缺一不可。如果你正好也需要一个带源码的完整案例来参考,那这篇内容基本就是把整个项目思路和实现过程从头到尾拆给你看。
下面我从设计思路、技术选型、核心模块、部署运行、常见排查以及升级扩展这几个方面,把整件事讲透。代码只挑关键片段展示,完整源码在项目包里都有,如果你想跑起来,按第四章的步骤操作就行。
1. 项目定位与核心需求拆解
1.1 美妆消费决策场景下的真实痛点
先说业务。美妆消费者在买东西之前,其实是有很多犹豫的。我接触过不少做这类课题的同学,他们一开始都把项目做成了“美妆商城”,上来就是商品列表、加入购物车、下单支付,结果做完发现这不过是一个电商系统换了个皮,没有任何“辅助决策”的影子。
那“辅助决策”到底辅助什么?我认为核心要解决三个问题。
第一,这个产品适不适合我。每个人的肤质不一样,油皮和干皮对同一款面霜的感受完全不同。这个需求落到系统里,就是肤质测评和产品-肤质匹配。
第二,它的真实口碑怎么样。现在网上种草和广告混在一起,用户需要一个能沉淀真实评价的地方。对应功能就是评论和评分。
第三,价格是不是合理,值不值得现在买。对应功能就是价格记录、价格比较。
所以这个项目的核心功能设计不是从“管理员要管理什么”出发,而是从“用户在下单前需要知道什么”出发。这也是这个题目在答辩时最值得讲的地方:你有明确的业务问题意识,而不是单纯在做CRUD。
1.2 功能清单与角色划分
整个系统我按用户角色拆成两个端。
用户端功能包括:注册登录、肤质测评、美妆产品浏览与搜索、产品详情查看、成分解析、产品收藏、评论评分、价格记录查看。
管理员端功能包括:美妆产品管理(增删改查)、产品分类管理、用户管理、评论审核管理、价格信息维护。
功能数量上,不多不少,刚好符合毕设要求。有的同学喜欢把功能堆到十几个页面,但功能越多,代码越容易乱,答辩时也讲不清楚。这里的取舍原则是:基础增删改查必须有,但一定要有一个核心业务逻辑作为亮点。这套系统里的亮点就是肤质测评与推荐匹配、成分关键词解析这两个模块,它们不复杂,但能体现你的思考。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与架构设计
2.1 为什么还是选了JSP+Servlet,而不是Spring Boot
很多同学会纠结这个问题:现在企业里都用Spring Boot了,毕设还用JSP+Servlet是不是太老?
我的建议是:看清题目再决定。题目明确写的是“基于JavaWeb”,那么最合理的理解就是用JavaWeb课程体系里的核心技术来实现。JSP+Servlet+MySQL+Tomcat,这套组合本身就是JavaWeb的定义。用Spring Boot当然行,但三个风险你要想清楚:一是如果老师按题目要求检查,你做的东西可能偏离课程考核范围;二是Spring Boot封装了太多东西,你在答辩时如果被问到底层请求流程,讲不清楚反而扣分;三是这个项目本身逻辑不复杂,用Servlet完全够用,没必要引入框架增加自己写代码的负担。
从这个项目的分工来看,JSP负责视图展示,Servlet负责请求控制和转发,JavaBean和DAO负责数据操作,前端用HTML、CSS、JavaScript做页面交互。这些都是大三JavaWeb课程里反复练过的东西,做成毕设,老师一眼就能看出你确实掌握了这门课的核心内容。
2.2 分层架构与项目目录结构
目录结构上,我按标准的MVC分层来组织,同时兼顾“源码可读性”。这里我给出一个参考结构,你如果自己复刻可以直接用。
code复制src
├── com.example.filter
│ └── EncodingFilter.java
├── com.example.servlet
│ ├── UserServlet.java
│ ├── ProductServlet.java
│ ├── SkinTestServlet.java
│ ├── CommentServlet.java
│ └── AdminServlet.java
├── com.example.service
│ ├── UserService.java
│ ├── ProductService.java
│ ├── SkinTestService.java
│ └── CommentService.java
├── com.example.dao
│ ├── BaseDao.java
│ ├── UserDao.java
│ ├── ProductDao.java
│ ├── SkinTestDao.java
│ └── CommentDao.java
├── com.example.entity
│ ├── User.java
│ ├── Product.java
│ ├── Category.java
│ ├── SkinType.java
│ └── Comment.java
└── com.example.util
├── DBUtil.java
└── IngredientMatcher.java
webapp
├── static
│ ├── css
│ └── js
├── WEB-INF
│ └── web.xml
├── index.jsp
├── login.jsp
├── register.jsp
├── product_list.jsp
├── product_detail.jsp
├── skin_test.jsp
└── admin
└── product_manage.jsp
有几点经验:第一,把JSP文件放在web根目录下,访问路径比较短,调试方便;没有非要塞进WEB-INF,除非你想做严格的权限控制。第二,Servlet类不要一窝蜂写在一个类里,按用户、产品、评论等不同资源拆成多个Servlet,职责清晰,答辩时也容易讲。第三,DBUtil和BaseDao放util包,BaseDao里面封装通用增删改查方法,减少重复代码。
2.3 数据库设计:核心表结构与关联关系
数据库是这个项目的根基,表结构设计好了,后面写代码会省很多事。我这里按核心业务拆了六张表。
用户表(t_user):存储用户的账号密码、昵称、肤质类型、创建时间。密码存的是MD5加密后的值,不能明文存。
分类表(t_category):产品分类,比如护肤、彩妆、香水、个护。字段包括分类ID、分类名称。
产品表(t_product):字段包括产品ID、产品名称、品牌、分类ID、价格、功效描述、适用肤质、主要成分、图片路径、库存、产品评分。
评价表(t_comment):关联用户ID和产品ID,存评论内容、评分、评论时间。
收藏表(t_favorite):关联用户ID和产品ID,加上创建时间,这是典型的多对多关系中间表。
价格记录表(t_price):关联产品ID,存价格、记录时间和记录来源。
这几个表之间的外键逻辑我建议在Java代码里维护,不要过度依赖数据库物理外键,否则插入数据的时候总会被约束卡住。逻辑外键对毕设来说完全够用,还能避免一些级联操作的坑。
3. 核心功能模块的实现与代码解读
3.1 用户模块:注册登录与会话管理
用户模块是所有系统的基础,这个模块做的好坏直接影响到后面所有功能的演示。注册的核心逻辑是:用户名唯一性校验、密码加密存储、验证码校验。
验证码我用的是Java原生生成的图片验证码,不用第三方库,代码不复杂,也能体现基本功。登录成功后,把用户对象放进Session,同时记录一下用户ID,后面收藏、评论、测评都从这个Session里取用户信息。
为了防止中文乱码,我在Filter里统一处理了编码问题。这个Filter的代码量很小,但作用很大,属于实战里必须有的一环。
java复制@WebFilter("/*")
public class EncodingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
chain.doFilter(req, resp);
}
}
Session的另一个作用是权限控制。用户访问收藏、评论等接口时,如果Session里没有用户信息,直接重定向到登录页。管理员访问后台也是同样的判断,但是管理员的身份用一个单独字段来标记。
3.2 肤质测评与推荐匹配:核心业务逻辑
这是这个网站区别于普通电商系统的灵魂模块。皮肤测评的设计思路是:设计一套问卷,每道题的选项对应不同肤质维度的加分。
比如问卷有三个维度:油脂分泌情况、敏感程度、水分保持情况。测试者回答5-8道题之后,系统根据得分综合判断肤质类型,可能是油性、干性、混合性、敏感性中的一种。
测评完之后,推荐匹配逻辑就可以做了。产品表里有个字段叫suitable_skin,就是“适用肤质”,值可以是“油性”、“干性”、“混合性”、“敏感性”,也可以是组合,比如“油性/混合性”。推荐时把用户肤质和产品适用肤质做匹配,匹配上的产品按评分倒序排列。
核心查询SQL大概长这样:
sql复制SELECT * FROM t_product
WHERE suitable_skin LIKE '%${skinType}%'
ORDER BY product_score DESC
LIMIT 10;
这里有个细节要注意:用LIKE做匹配虽然简单,但如果适用肤质字段写法不统一,比如一条数据是“油性、混合性”,另一条是“油性/混合性”,就会出现匹配不上。所以我在设计表结构时,用了统一的分隔符规范,同时在插入数据时做清洗。这个点我在项目里专门写了一个数据检查工具类,用来扫描全表的分隔符格式。
如果你的毕设想再提升一个档次,可以在测评后多做一步:记录用户测评结果的历史表,下次进来可以看到上次测评时间和肤质变化趋势。这个模块看起来不多,但在答辩时很有展示性。
3.3 产品展示与成分解析模块
产品列表页和详情页是一个纯展示和技术细节结合的地方。列表页除了分页展示,我还加了两个筛选维度:按分类筛选、按价格区间筛选。这里我用的是ProductServlet接收参数,拼SQL条件查询,参数直接拼SQL很危险,有SQL注入风险,所以我用了PreparedStatement,参数统一用?占位。
成分解析是我觉得比较有意思的地方。美妆产品详情页上往往有一串化学名词,普通用户根本看不懂。我在数据库里维护了一张成分词典表,把常见的烟酰胺、水杨酸、视黄醇、传明酸、玻尿酸等成分的作用说明都存进去。当用户打开产品详情页时,后台把该产品的成分字段拆分成一个个关键词,然后用成分词典表匹配,匹配到了就把说明显示在成分清单里。
java复制public static List<String> matchIngredients(String ingredients) {
List<String> result = new ArrayList<>();
if (ingredients == null || ingredients.isEmpty()) {
return result;
}
for (String item : dictionary) {
if (ingredients.contains(item)) {
result.add(item);
}
}
return result;
}
这个模块的技术含量不高,但业务代入感很强。你想象一下演示时的场景:用户打开一个产品详情页,看到“烟酰胺”后面跟着一行小字“具有美白提亮、控油等功效”,这比干巴巴的产品列表有说服力多了。
3.4 收藏、评论与价格比较
收藏功能是一个典型的多对多关系实现。用户和产品之间通过t_favorite表关联。我在ProductServlet里预留了一个favUser接口,登录用户点击收藏按钮,先检查是否已经收藏,没有则插入记录,有则取消收藏。收藏列表页展示的是当前用户收藏过的所有产品。
评论功能也相对简单,但要注意两个点:一是评论内容要做XSS过滤,不能直接把用户输入的
