ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于UML的网上招聘系统需求规格说明书:从建模到数据库落地

基于UML的网上招聘系统需求规格说明书:从建模到数据库落地 简介这份《基于UML的需求规格说明书(网上招聘系统)》面向软件工程专业学生、需求分析初学者及需要撰写规格文档的开发人员以网上招聘系统为案例完整演示如何用统一建模语言描述系统需求。文档从导言、系统定义、应用环境到功能规格逐层展开涵盖应聘者、雇主、管理员等角色定义并借助用例图、类图、序列图、状态图直观呈现系统的静态结构与动态行为是理解需求工程与UML建模的实用参考。资源包共1个PDF文件约746KB内容为完整规格说明书正文目录清晰、章节分明便于按模块查阅。目前已有306人学习。读者可从中掌握需求规格说明书的编写框架、角色与用例的划分方法以及如何将业务目标转化为可落地的功能与非功能需求适合课程设计、项目实训或面试准备时对照参考。1. 网上招聘系统的需求规格说明书为什么UML画了三十张图开发还是靠猜做过校招系统或者企业内推平台的人都有个体会需求评审会上产品经理把用例图、类图、时序图往投影上一放开发点头说“懂了”测试说“没问题”结果第一版提测时登录注册流程能跑通但“简历投递状态流转”和“面试邀约冲突检测”这两个模块直接翻车——因为图里只画了“投递”这个动作没写清楚“已投递”“已查看”“待面试”“已拒绝”之间的状态迁移条件。这就是典型的“UML画了需求没写透”。网上招聘系统这类项目表面上是CRUD堆出来的实际上它的复杂度集中在三处多角色权限求职者、HR、猎头、管理员、状态机职位发布→审核→上线→下线简历投递→筛选→面试→录用、以及并发场景同一岗位多人同时投递、面试官时间冲突。一份合格的《基于UML的需求规格说明书》不是把23种UML图挨个画一遍而是用用例图锁定边界、用类图定死数据模型、用状态图和活动图把流转规则写死。这份文档的读者是开发、测试和后续接手的人它得能当“合同”用——图里没画的代码里就不该有图里画了的测试用例就得覆盖到。如果你正在做网上招聘系统的课设、毕设或者公司里要重构一套老招聘后台这篇笔记会按“先立住UML建模的骨架再落到文档章节怎么写、图怎么画、参数怎么定”的顺序走一遍。新手能照着把需求规格说明书的目录搭起来熟手能直接拿走状态迁移表和接口字段定义去对代码。不扯虚的直接开干。2. 先搞清楚UML在这份文档里到底该画哪几张图从用例图到状态图的取舍逻辑2.1 用例图定边界网上招聘系统里哪些角色该出现哪些该合并网上招聘系统的用例图最容易犯的错是角色爆炸。有人把“求职者”拆成“应届生”“社招人员”“海归”把“HR”拆成“初级HR”“HRBP”“招聘总监”。用例图的作用是定系统边界不是画组织架构。我一般只保留四个主角求职者JobSeeker、招聘方Recruiter、猎头Headhunter、系统管理员Admin。猎头这个角色在中小型系统里可以直接合并进招聘方但如果是猎头平台必须独立出来因为猎头能代投简历、能看候选人联系方式权限模型不一样。用例的粒度也有讲究。“登录”这种用例不要单独画它是所有角色的公共前置画进去只会让图变乱。真正要画的是有业务价值的动作求职者侧——“搜索职位”“投递简历”“查看投递状态”“参加在线笔试”招聘方侧——“发布职位”“筛选简历”“发起面试邀约”“填写面试评价”管理员侧——“审核职位”“冻结账号”“导出招聘报表”。画用例图时include和extend别乱用。include用在“每次投递简历都必须触发简历完整性校验”这种必然发生的子流程extend用在“在线笔试”这种可选扩展——不是所有投递都需要笔试。很多人的用例图被扣分就是因为把可选流程画成了包含关系导致开发以为笔试是必选模块白白多写了一套定时任务。提示用例图里不要出现数据库表名、不要出现接口路径那是类图和时序图的事。用例图只回答“谁、做什么、系统边界在哪”。2.2 类图定数据模型招聘系统里哪几个实体是核心关联关系怎么标类图是需求规格说明书里最容易被开发直接拿来建表的部分。网上招聘系统的核心实体不超过十个User用户基类、JobSeeker求职者、Recruiter招聘方、Job职位、Resume简历、Application投递记录、Interview面试邀约、Offer录用通知、Department部门、Attachment附件。其中User是抽象类JobSeeker和Recruiter继承它这样登录鉴权只用查一张表。关联关系要标清楚多重性。一个JobSeeker可以投递多个Job一个Job可以被多个JobSeeker投递这是多对多中间表就是Application。一个Application可以关联零到多个Interview有的岗位一轮面试有的三轮一个Interview必须关联一个Application。这些多重性数字0..*、1..1、1..*不是装饰它们直接决定外键怎么建、级联删除怎么配。我见过有人把Application和Interview标成一对一结果二面三面的数据没地方存只能加字段硬扛后期查询全是坑。类图里的属性要带类型和可见性。-表示private表示public。Resume类里的fileUrl是StringparseStatus是枚举待解析/解析中/解析成功/解析失败。枚举类型在类图里可以用enumeration单独画一个框也可以直接在属性后面标注。我习惯单独画因为枚举值在需求文档里要列全开发建表时直接抄。2.3 状态图和活动图的分工投递状态流转为什么必须用状态图而不是活动图这是最容易被混淆的地方。活动图Activity Diagram画的是“流程”有开始节点、判断节点、合并节点、结束节点适合描述“发布职位”这种从填写表单到审核通过的操作步骤。状态图State Machine Diagram画的是“一个对象在生命周期内的状态变化”适合描述“投递记录”从创建到结束的完整状态迁移。网上招聘系统里Application投递记录的状态至少有已投递SUBMITTED、已查看VIEWED、筛选中SCREENING、待面试INTERVIEW_PENDING、面试中INTERVIEWING、待录用OFFER_PENDING、已录用HIRED、已拒绝REJECTED、已撤回WITHDRAWN。这些状态之间的迁移条件必须写死比如从SUBMITTED到VIEWED的触发事件是“招聘方打开简历详情页”从VIEWED到SCREENING的触发事件是“招聘方点击‘进入筛选’按钮”从SCREENING到REJECTED的守卫条件是“简历评分低于60分”。用活动图画这个就翻车了——活动图没有“状态”的概念它只能画“从A步骤到B步骤”画不出“同一个投递记录在数据库里状态字段的值变化”。测试同学拿着活动图写用例只能测流程通不通测不了状态机有没有死锁。所以我的习惯是操作流程用活动图对象生命周期用状态图两张图配合着看。2.4 时序图定接口面试邀约模块的时序图要细到什么程度时序图Sequence Diagram在需求规格说明书里的作用是锁定接口调用顺序和参数传递。以“招聘方发起面试邀约”为例时序图里至少要出现这几个对象Recruiter招聘方、InterviewController面试控制器、ApplicationService投递服务、CalendarService日历服务、NotificationService通知服务、Database数据库。消息顺序是Recruiter → InterviewController: 提交面试邀约applicationId, interviewTime, interviewerIdInterviewController → ApplicationService: 校验投递状态是否为INTERVIEW_PENDINGApplicationService → Database: 查询ApplicationDatabase → ApplicationService: 返回状态ApplicationService → InterviewController: 状态合法InterviewController → CalendarService: 检查面试官时间冲突CalendarService → Database: 查询面试官日历Database → CalendarService: 返回已有面试CalendarService → InterviewController: 无冲突InterviewController → Database: 插入Interview记录InterviewController → NotificationService: 发送面试通知NotificationService → Recruiter: 返回邀约成功。这张图里每个消息箭头上的参数名都要写全。开发照着这张图写Controller和Service的方法签名基本不用猜。测试照着这张图写集成测试用例覆盖“状态不合法”“时间冲突”“通知失败”三个异常分支。时序图不用画太深画到Service层就够了再往下画DAO层就成代码注释了。3. 需求规格说明书的文档骨架怎么搭从GB/T 8567章节到招聘系统特有章节的裁剪3.1 标准章节里哪些必须留哪些可以合并GB/T 8567《计算机软件文档编制规范》里需求规格说明书的章节有引言、总体描述、具体需求、附录。网上招聘系统的课设或毕设如果按标准模板硬套会写出一堆“项目背景”“编写目的”“术语定义”这种凑字数的内容。我的裁剪原则是引言只留“编写目的”和“范围”两节术语定义直接并到附录总体描述里只留“用户角色”和“运行环境”具体需求按功能模块拆。具体需求这一章是核心按“功能需求”“外部接口需求”“性能需求”“设计约束”四节展开。功能需求按用例图里的角色分求职者功能、招聘方功能、管理员功能。每个功能下面用表格列“输入、处理、输出、异常”。外部接口需求写清楚“简历文件上传接口”支持的文件格式PDF/DOC/DOCX、单文件大小上限10MB、存储路径规则。性能需求写“职位搜索响应时间不超过2秒”“简历投递并发量不低于500 TPS”。设计约束写“必须使用MySQL 8.0”“必须支持Chrome 90以上”。3.2 招聘系统特有的“状态迁移表”和“权限矩阵”怎么放进文档标准模板里没有“状态迁移表”和“权限矩阵”的位置但这两个东西对网上招聘系统至关重要。我的做法是在“功能需求”下面加两个子节3.2.1 状态迁移说明3.2.2 权限矩阵。状态迁移表用表格写列是当前状态、触发事件、守卫条件、目标状态、副作用。以Application为例当前状态触发事件守卫条件目标状态副作用SUBMITTED招聘方查看简历无VIEWED更新viewedAt时间戳VIEWED招聘方点击筛选无SCREENING无SCREENING系统自动评分评分60REJECTED发送拒信SCREENING系统自动评分评分60INTERVIEW_PENDING通知招聘方INTERVIEW_PENDING招聘方发起邀约面试官时间空闲INTERVIEWING创建Interview记录INTERVIEWING招聘方填写评价评价为通过OFFER_PENDING无INTERVIEWING招聘方填写评价评价为不通过REJECTED发送拒信OFFER_PENDING招聘方发Offer无HIRED发送录用通知SUBMITTED求职者撤回状态为SUBMITTEDWITHDRAWN无权限矩阵用表格写行是角色列是操作。比如“查看简历联系方式”这个操作求职者看不到招聘方在简历状态为VIEWED之后才能看到猎头可以直接看到管理员只能看到脱敏后的手机号。这个矩阵直接决定后端接口的鉴权注解怎么写。3.3 用PlantUML把图嵌进Markdown文档一份可版本管理的需求说明书Word画UML图的问题是版本管理灾难——改一个类名要重新导出图片Git diff全是二进制。我现在的习惯是用PlantUML写图嵌在Markdown里用plantuml代码块。这样需求文档和代码一起进Git改图就是改文本diff看得清清楚楚。startuml class User { -id: Long -username: String -password: String -role: UserRole login(): Boolean } class JobSeeker { -realName: String -phone: String -education: String submitResume(): void } class Recruiter { -companyName: String -department: String publishJob(): Job } User |-- JobSeeker User |-- Recruiter enduml这段PlantUML代码定义了一个继承关系JobSeeker和Recruiter都继承User。-表示私有属性表示公开方法。|--是继承箭头指向父类。把这段代码放进Markdown用支持PlantUML的渲染器比如VS Code的PlantUML插件或者CI流水线里的plantuml.jar就能生成类图。参数说明startuml和enduml是必须的包裹标记类名后面跟花括号属性格式是可见性 名称: 类型方法格式是可见性 名称(参数): 返回类型。注意PlantUML的类图不支持直接画多重性数字在关联线上需要用1 -- 0..*这种语法。比如Job 1 -- 0..* Application : 收到表示一个职位收到零到多个投递。3.4 需求条目怎么编号才能让开发和测试对得上需求编号是需求规格说明书的“身份证”。我的编号规则是REQ-模块缩写-三位数字。比如REQ-JOB-001表示职位模块的第一条需求REQ-APP-003表示投递模块的第三条需求。每个需求条目下面跟“描述、优先级、来源、验收标准”。验收标准要写成可测试的断言。比如REQ-APP-003的验收标准是“当求职者投递简历时如果简历完整度低于80%系统返回错误码RESUME_INCOMPLETE并在前端提示‘请完善简历后再投递’。”测试同学直接拿这句话写自动化测试脚本不用再问“完整度怎么算”。需求条目和用例图、状态图、时序图的对应关系也要标。在需求条目后面加“关联图用例图-求职者-投递简历状态图-Application”。这样开发改代码时能顺着编号找到所有相关图不会漏改。4. 从UML模型到数据库表类图里的关联关系怎么变成外键和索引4.1 类图到关系模型的映射规则一对一、一对多、多对多分别怎么建表类图里的关联关系映射到关系数据库有三条铁律。一对一外键放在任意一侧但通常放在“被动方”。比如User和JobSeeker是一对一JobSeeker表里放user_id外键。一对多外键放在“多”的那一侧。Recruiter和Job是一对多Job表里放recruiter_id外键。多对多必须建中间表。JobSeeker和Job是多对多中间表Application里放job_seeker_id和job_id外加投递时间、状态等业务字段。继承关系的映射有三种策略单表继承整个继承树一张表用type字段区分、具体表继承每个子类一张表公共字段重复、类表继承父类一张表子类各一张表用主键关联。网上招聘系统我推荐单表继承因为User、JobSeeker、Recruiter的字段差异不大单表查询不用join性能好。建一张user表字段包括id、username、password、role枚举JOB_SEEKER/RECRUITER/ADMIN、real_name、phone、company_name、department。role为JOB_SEEKER时company_name为空role为RECRUITER时education为空。4.2 用SQL建表验证类图招聘系统核心表的DDL与索引设计-- 用户表单表继承role区分角色 CREATE TABLE user ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, username VARCHAR(64) NOT NULL COMMENT 登录名, password VARCHAR(128) NOT NULL COMMENT bcrypt哈希, role ENUM(JOB_SEEKER,RECRUITER,ADMIN) NOT NULL, real_name VARCHAR(32) DEFAULT NULL, phone VARCHAR(20) DEFAULT NULL, company_name VARCHAR(128) DEFAULT NULL, department VARCHAR(64) DEFAULT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username), KEY idx_role (role) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 职位表recruiter_id外键指向user表 CREATE TABLE job ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, recruiter_id BIGINT UNSIGNED NOT NULL, title VARCHAR(128) NOT NULL, description TEXT, status ENUM(DRAFT,PENDING,ONLINE,OFFLINE) NOT NULL DEFAULT DRAFT, salary_min INT DEFAULT NULL, salary_max INT DEFAULT NULL, city VARCHAR(32) DEFAULT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_recruiter_status (recruiter_id, status), KEY idx_city_status (city, status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 投递记录表多对多中间表带状态机字段 CREATE TABLE application ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, job_id BIGINT UNSIGNED NOT NULL, job_seeker_id BIGINT UNSIGNED NOT NULL, resume_id BIGINT UNSIGNED NOT NULL, status ENUM(SUBMITTED,VIEWED,SCREENING,INTERVIEW_PENDING, INTERVIEWING,OFFER_PENDING,HIRED,REJECTED,WITHDRAWN) NOT NULL DEFAULT SUBMITTED, score TINYINT DEFAULT NULL COMMENT 自动评分0-100, viewed_at DATETIME DEFAULT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_job_seeker (job_id, job_seeker_id), KEY idx_seeker_status (job_seeker_id, status), KEY idx_job_status (job_id, status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这三张表的DDL直接对应类图里的User、Job、Application。uk_job_seeker唯一索引保证同一个求职者对同一个职位只能投递一次对应类图里Application的多重性约束。idx_job_status索引支撑“招聘方按状态筛选投递”的查询。status字段的枚举值就是状态图里的所有状态一个不多一个不少。参数说明BIGINT UNSIGNED用于主键支持大表utf8mb4支持emoji求职者姓名里可能有生僻字ON UPDATE CURRENT_TIMESTAMP让updated_at自动更新省去手动维护。索引命名规则uk_前缀唯一索引idx_前缀普通索引后面跟字段名。4.3 状态字段的枚举值怎么和状态图对齐一个都不能多一个都不能少状态图里画了九个状态数据库枚举就必须是九个。我见过有人在数据库里多加了DELETED状态但状态图里没画结果测试同学不知道这个状态什么时候触发上线后发现有数据卡在DELETED状态出不来。状态图的每个状态都要在数据库枚举里有对应值数据库枚举里的每个值都要在状态图里有迁移路径。状态迁移的守卫条件在代码里实现不在数据库层实现。比如“评分低于60分才能从SCREENING转到REJECTED”这个判断写在Service层数据库只存结果。这样状态机逻辑可测试、可修改不用改表结构。4.4 简历附件的存储设计文件路径存库还是存对象存储简历附件PDF/DOCX的存储有两种方案存文件路径到数据库文件本身放本地磁盘或对象存储或者直接把文件二进制存数据库BLOB字段。网上招聘系统我强烈推荐前者。BLOB字段会让数据库体积暴涨备份恢复慢而且MySQL的max_allowed_packet默认4MB大文件直接插入失败。具体做法resume表里存file_url字段值是对象存储的URL或者本地磁盘的相对路径。文件上传接口收到文件后先算MD5去重然后按/resume/{yyyy}/{MM}/{md5}.pdf的路径存盘返回URL给前端。数据库只存URL和文件元信息大小、格式、上传时间。这样数据库轻量文件服务可以独立扩容。提示文件路径不要存绝对路径存相对路径。换服务器或者迁移存储时绝对路径全废相对路径只用改配置。5. 避坑与排查网上招聘系统需求文档里最容易翻车的五个地方5.1 用例图里画了“删除职位”但状态图里没有“已删除”状态现象开发照着用例图实现了物理删除职位的接口结果投递记录里的job_id变成孤儿数据查询投递详情时关联不到职位页面报500。原因用例图只画了“删除职位”这个动作但状态图里Job的状态只有DRAFT、PENDING、ONLINE、OFFLINE没有DELETED。开发以为删除就是DELETE FROM job。解决在状态图里加DELETED状态迁移条件是“招聘方点击删除且该职位下无有效投递”副作用是“逻辑删除status置为DELETED”。数据库里Job表加deleted_at字段做软删除所有查询默认过滤deleted_at IS NULL。5.2 类图里Application和Interview标了一对一二面数据没地方存现象第一轮面试结束后招聘方想发起第二轮面试系统提示“该投递已存在面试记录”。原因类图里Application和Interview的关联多重性标成了1..1开发建表时在Interview表里把application_id设成了唯一索引。解决把多重性改成0..*Interview表去掉唯一索引加round字段1表示一面2表示二面。状态图里INTERVIEWING状态可以自循环迁移触发事件是“发起下一轮面试”。5.3 时序图里漏了“通知服务”上线后发现面试邀约发了但候选人没收到现象招聘方发起面试邀约数据库里Interview记录插入了但求职者没收到任何通知面试当天没来。原因时序图里只画了Recruiter → InterviewController → Database漏了NotificationService。开发写代码时只实现了入库没实现发通知。解决时序图里补上NotificationService的调用消息顺序是“插入Interview记录 → 调用NotificationService.sendInterviewNotice() → 返回邀约成功”。需求文档里加一条外部接口需求“面试邀约成功后系统必须在30秒内通过站内信和邮件通知求职者。”5.4 权限矩阵没写“猎头能否看联系方式”开发默认给了全部权限现象猎头登录后能看到求职者的手机号和邮箱但求职者只授权了“投递时可见”。原因权限矩阵里只写了求职者、招聘方、管理员三行漏了猎头。开发在鉴权拦截器里把猎头当成了招聘方。解决权限矩阵补上猎头行“查看联系方式”列填“仅当求职者简历状态为VIEWED且猎头已绑定该职位时可见”。后端接口加PreAuthorize注解SpEL表达式里判断角色和状态。5.5 状态迁移表里“已撤回”状态没有出口撤回后无法重新投递现象求职者撤回投递后想重新投递同一个职位系统提示“已投递过该职位”。原因状态迁移表里WITHDRAWN状态没有出边而且数据库唯一索引uk_job_seeker还在。解决状态迁移表加一条“WITHDRAWN → SUBMITTED触发事件求职者重新投递守卫条件距上次撤回超过24小时”。数据库唯一索引改成uk_job_seeker_active只对非WITHDRAWN状态生效MySQL不支持部分索引可以用触发器或者在应用层判断。6. 用状态迁移表反向生成测试用例一个被低估的验证技巧需求规格说明书写完怎么验证它有没有漏我的习惯是拿状态迁移表反向生成测试用例。状态迁移表里每一行就是一个测试场景当前状态、触发事件、守卫条件、目标状态。测试用例的格式是前置条件当前状态、操作步骤触发事件、预期结果目标状态副作用。以Application的九条迁移为例至少能生成九条正向用例和若干条异常用例。异常用例覆盖“守卫条件不满足”的情况比如从SCREENING触发“系统自动评分”但评分60预期结果是转到INTERVIEW_PENDING而不是REJECTED。再覆盖“非法迁移”从HIRED触发“求职者撤回”预期结果是拒绝操作并返回错误码INVALID_STATE_TRANSITION。# 状态迁移测试用例生成器伪代码 transitions [ (SUBMITTED, 招聘方查看简历, None, VIEWED, 更新viewed_at), (VIEWED, 招聘方点击筛选, None, SCREENING, None), (SCREENING, 系统自动评分, score60, REJECTED, 发送拒信), (SCREENING, 系统自动评分, score60, INTERVIEW_PENDING, 通知招聘方), (INTERVIEW_PENDING, 招聘方发起邀约, 面试官空闲, INTERVIEWING, 创建Interview), (INTERVIEWING, 招聘方填写评价, 评价通过, OFFER_PENDING, None), (INTERVIEWING, 招聘方填写评价, 评价不通过, REJECTED, 发送拒信), (OFFER_PENDING, 招聘方发Offer, None, HIRED, 发送录用通知), (SUBMITTED, 求职者撤回, None, WITHDRAWN, None), ] for current, event, guard, target, side_effect in transitions: print(f用例前置状态{current}操作{event}守卫{guard} f预期状态{target}预期副作用{side_effect})这段代码遍历状态迁移表打印出每条迁移对应的测试用例描述。参数说明current是当前状态event是触发事件guard是守卫条件None表示无条件target是目标状态side_effect是副作用。测试同学拿到这个列表直接往TestRail或者Excel里填覆盖率达到100%。我一般还会加一个“状态覆盖检查”把所有状态列出来看每个状态是否至少出现在一条迁移的起点或终点。如果某个状态只出现在终点不出现在起点比如HIRED说明它是终态合理如果某个状态既不出现在起点也不出现在终点说明状态图漏画了迁移。最后一个习惯需求评审会之前把状态迁移表和权限矩阵打印出来让产品、开发、测试三方各自签字。签字不是走形式是逼着每个人把表里的每一行读一遍。我经历过一次测试同学在签字时发现“OFFER_PENDING → HIRED”的守卫条件写的是“招聘方发Offer”但没写“求职者接受Offer”这个动作导致状态机缺了一半。当场补上省了上线后一个P0故障。需求文档的厚度不重要状态迁移表的行数才重要。希望帮到你。本文还有配套的精品资源点击获取
返回列表