【Java项目-企悦抽】02-AI赋能产品需求规格说明书

声明:本文档AI辅助完成,内容仅供参考
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨

🎯 你正在阅读「Java项目-企悦抽」系列文章 🎯

✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨

🔥 弹简特 个人主页

❄️ 个人专栏直通车:

✨ 靠热爱去书写自己,靠勇敢去书写生活!

✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨


🌟 博主简介:


文章目录:

  • 企悦抽-产品需求规格说明书
    • 修订记录
    • [1. 引言](#1. 引言)
      • [1.1 编写目的](#1.1 编写目的)
      • [1.2 文档关系](#1.2 文档关系)
    • [2. 系统概述](#2. 系统概述)
      • [2.1 系统名称](#2.1 系统名称)
      • [2.2 技术栈规格](#2.2 技术栈规格)
      • [2.3 模块划分](#2.3 模块划分)
    • [3. 系统架构规格](#3. 系统架构规格)
      • [3.1 逻辑架构](#3.1 逻辑架构)
      • [3.2 抽奖异步链路规格](#3.2 抽奖异步链路规格)
      • [3.3 缓存规格](#3.3 缓存规格)
    • [4. 数据规格](#4. 数据规格)
      • [4.1 数据库表](#4.1 数据库表)
      • [4.2 枚举规格](#4.2 枚举规格)
      • [4.3 字段校验规格](#4.3 字段校验规格)
      • [4.4 加密规格](#4.4 加密规格)
    • [5. 功能规格详述](#5. 功能规格详述)
      • [5.1 FR-001 ~ FR-005 用户与认证](#5.1 FR-001 ~ FR-005 用户与认证)
        • [5.1.1 注册(FR-001)](#5.1.1 注册(FR-001))
        • [5.1.2 发送验证码(FR-004)](#5.1.2 发送验证码(FR-004))
        • [5.1.3 密码登录(FR-002)](#5.1.3 密码登录(FR-002))
        • [5.1.4 短信登录(FR-003)](#5.1.4 短信登录(FR-003))
        • [5.1.5 登录拦截(FR-005)](#5.1.5 登录拦截(FR-005))
      • [5.2 FR-006 ~ FR-007 人员管理](#5.2 FR-006 ~ FR-007 人员管理)
        • [5.2.1 创建普通用户(FR-006)](#5.2.1 创建普通用户(FR-006))
        • [5.2.2 用户列表(FR-007)](#5.2.2 用户列表(FR-007))
      • [5.3 FR-008 ~ FR-010 奖品管理](#5.3 FR-008 ~ FR-010 奖品管理)
        • [5.3.1 创建奖品(FR-008)](#5.3.1 创建奖品(FR-008))
        • [5.3.2 奖品分页列表(FR-009)](#5.3.2 奖品分页列表(FR-009))
        • [5.3.3 奖品全量列表(FR-010)](#5.3.3 奖品全量列表(FR-010))
      • [5.4 FR-011 ~ FR-013 活动管理](#5.4 FR-011 ~ FR-013 活动管理)
        • [5.4.1 创建活动(FR-011)](#5.4.1 创建活动(FR-011))
        • [5.4.2 活动列表(FR-012)](#5.4.2 活动列表(FR-012))
        • [5.4.3 活动详情(FR-013)](#5.4.3 活动详情(FR-013))
      • [5.5 FR-014 ~ FR-016 抽奖与结果](#5.5 FR-014 ~ FR-016 抽奖与结果)
        • [5.5.1 提交抽奖(FR-014)](#5.5.1 提交抽奖(FR-014))
        • [5.5.2 查询中奖记录(FR-015)](#5.5.2 查询中奖记录(FR-015))
        • [5.5.3 抽奖大屏(FR-016)](#5.5.3 抽奖大屏(FR-016))
      • [5.6 FR-017 ~ FR-018 通知](#5.6 FR-017 ~ FR-018 通知)
        • [5.6.1 中奖邮件(FR-017)](#5.6.1 中奖邮件(FR-017))
        • [5.6.2 中奖短信(FR-018)](#5.6.2 中奖短信(FR-018))
    • [6. 接口规格索引](#6. 接口规格索引)
    • [7. 前端页面规格](#7. 前端页面规格)
      • [7.1 实现说明](#7.1 实现说明)
      • [7.2 页面清单](#7.2 页面清单)
      • [7.3 通用前端约定](#7.3 通用前端约定)
    • [8. 状态机规格](#8. 状态机规格)
      • [8.1 状态扭转顺序](#8.1 状态扭转顺序)
      • [8.2 抽奖正向扭转](#8.2 抽奖正向扭转)
      • [8.3 异常回滚](#8.3 异常回滚)
    • [9. 安全规格](#9. 安全规格)
    • [10. 异常与容错规格](#10. 异常与容错规格)
      • [10.1 MQ 重试](#10.1 MQ 重试)
      • [10.2 死信队列](#10.2 死信队列)
      • [10.3 校验失败静默返回](#10.3 校验失败静默返回)
    • [11. 验收标准](#11. 验收标准)
      • [11.1 功能验收](#11.1 功能验收)
      • [11.2 非功能验收](#11.2 非功能验收)

企悦抽-产品需求规格说明书

文档属性 内容
产品名称 企悦抽(QiYueChou)
文档类型 产品需求规格说明书(PRS)
文档版本 V1.0
编写日期 2025-07-03
上游文档 AI赋能产品需求文档
关联代码 lottery-system
文档状态 已定稿

修订记录

版本 日期 修订说明
V1.0 2025-07-03 首版,由 PRD 拆解为可开发/可测试规格

1. 引言

1.1 编写目的

本文档在 PRD 基础上,将产品需求转化为可实施、可验证的功能规格、数据规格、状态规则与验收标准,供研发与测试直接使用。

1.2 文档关系

复制代码
PRD(为什么做、做什么)
  ↓
PRS(怎么做、做到什么标准)  ← 本文档
  ↓
接口设计文档(接口级定义)
  ↓
接口测试需求文档(测试用例输入)

2. 系统概述

2.1 系统名称

  • 产品名:企悦抽
  • 工程名:lottery-system
  • 默认端口(开发用):8080

2.2 技术栈规格

层次 技术 版本/说明
后端框架 Spring Boot 3.2.6
语言 Java 17
ORM MyBatis 3.0.3(注解方式)
数据库 MySQL 库名 lottery_system
缓存 Redis Lettuce,默认端口配置 8888
消息队列 RabbitMQ Direct 交换机 + 死信队列
认证 JWT jjwt 0.11.5,Header: user_token
前端 HTML + jQuery + CSS 静态资源 /static;UI 借助 AI 辅助实现,主打美观效果
日志 SLF4J + Logback logback-spring.xml
短信 Spug 推送 SMSUtil HTTP 调用
邮件 Spring Mail QQ SMTP

2.3 模块划分

模块 包路径/组件 职责
用户模块 UserController、UserServiceImpl 注册、登录、用户列表
验证码模块 VerificationCodeServiceImpl 验证码生成、Redis 缓存、Spug 发送
奖品模块 PrizeController、PrizeServiceImpl 奖品 CRUD、图片上传
活动模块 ActivityController、ActivityServiceImpl 活动创建、列表、详情、Redis 缓存
抽奖模块 DrawPrizeController、DrawPrizeServiceImpl 发 MQ、校验、落库、查记录
MQ 消费 MqReceiver、DlxReceiver 异步抽奖、回滚、死信重投
状态管理 ActivityStatusManagerImpl + Operator 活动/奖品/用户状态扭转
通用 LoginInterceptor、GlobalException 鉴权、统一响应

3. 系统架构规格

3.1 逻辑架构

3.2 抽奖异步链路规格

步骤 组件 行为
1 DrawPrizeController 接收 JSON,调用 drawPrize()
2 DrawPrizeServiceImpl.drawPrize 构造 {messageId, messageData} 发 MQ
3 Controller 立即返回 {code:200, data:true}
4 MqReceiver.process 消费消息
5 --- checkDrawPrizeParam 校验
6 --- ActivityStatusManager.handlerEvent 扭转状态
7 --- saveWinnerRecords 写库 + 缓存
8 --- 线程池异步发邮件(短信占位)
9 异常 回滚 + 抛异常 → MQ 重试 → 死信

3.3 缓存规格

键前缀 内容 TTL
VERIFICATION_CODE_{phone} 4 位验证码 60 秒
ACTIVITY_{activityId} 活动详情 JSON 3 天
WINNING_RECORDS_{activityId} 活动全量中奖记录 2 天
WINNING_RECORDS_{activityId}_{prizeId} 单奖品中奖记录 2 天

4. 数据规格

4.1 数据库表

表名 说明 关键约束
user 用户 邮箱唯一、手机号唯一
prize 奖品库 ---
activity 活动 status: RUNNING/COMPLETED
activity_prize 活动-奖品 uk(activity_id, prize_id)
activity_user 活动-用户 uk(activity_id, user_id)
winning_record 中奖记录 uk(winner_id, activity_id, prize_id)

4.2 枚举规格

用户身份 UserIdentityEnum

值 含义
ADMIN 管理员
NORMAL 普通用户

活动状态 ActivityStatusEnum

值 含义
RUNNING 进行中
COMPLETED 已完成

活动奖品状态 ActivityPrizeStatusEnum

值 含义
INIT 未抽取
COMPLETED 已抽取

活动用户状态 ActivityUserStatusEnum

值 含义
INIT 未中奖
COMPLETED 已中奖

奖品等级 ActivityPrizeTiersEnum

值 code 中文
FIRST_PRIZE 1 一等奖
SECOND_PRIZE 2 二等奖
THIRD_PRIZE 3 三等奖

4.3 字段校验规格

字段 规则 实现
邮箱 小写邮箱格式 RegexUtil.checkMail
手机号 1 开头 11 位 RegexUtil.checkMobile
密码 6~12 位字母数字 RegexUtil.checkPassword
管理员密码 必填 注册/创建时校验

4.4 加密规格

数据 算法 说明
密码 SHA256 Hex 注册/登录时 DigestUtil.sha256Hex
手机号 AES EncryptTypeHandler,密钥 16 字节

5. 功能规格详述

5.1 FR-001 ~ FR-005 用户与认证

5.1.1 注册(FR-001)

输入 :name、mail、phoneNumber、password(管理员必填)、identity(ADMIN/NORMAL)

处理逻辑:

  1. JSR303 非空校验
  2. 邮箱/手机号格式校验
  3. 身份合法性校验
  4. 管理员密码必填且强度校验
  5. 邮箱/手机号唯一性校验
  6. 密码 SHA256 后入库;手机号 AES 加密入库

输出 :{ userId }

页面 :register.html;管理员后台创建时 admin=false&jumpList=true

5.1.2 发送验证码(FR-004)

输入 :Query 参数 phone

处理逻辑:

  1. 手机号格式校验
  2. MyCaptchaUtil.getCaptcha(4) 生成 4 位数字
  3. SMSUtil.sendSms(phone, code) 调用 Spug
  4. Redis 存储,键 VERIFICATION_CODE_{phone},60 秒

输出 :{ code:200, data:true }

5.1.3 密码登录(FR-002)

输入 :loginName(手机/邮箱)、password、mandatoryIdentity(可选)

处理逻辑:

  1. 按格式查用户(手机需 Encrypt 包装)
  2. 校验用户存在、身份匹配、密码 SHA256 比对
  3. JWT Claims: { id, identity },有效期 1 小时

输出 :{ token, identity }

5.1.4 短信登录(FR-003)

输入 :loginMobile、verificationCode、mandatoryIdentity(可选)

处理逻辑:

  1. 查用户、校验身份
  2. 从 Redis 取验证码比对
  3. 签发 JWT
5.1.5 登录拦截(FR-005)

白名单(无需 Token):

  • 静态资源 /**/*.html、/css/**、/js/**、/pic/** 等
  • /**/login(含 /password/login、/message/login)
  • /register
  • /verification-code/send
  • /winning-records/show

鉴权失败:HTTP 401,无 JSON Body


5.2 FR-006 ~ FR-007 人员管理

5.2.1 创建普通用户(FR-006)

与注册共用 /register,identity=NORMAL,不传密码。

5.2.2 用户列表(FR-007)

输入 :Query identity(可选,ADMIN/NORMAL,空则全部)

输出 :[{ userId, userName, identity }]


5.3 FR-008 ~ FR-010 奖品管理

5.3.1 创建奖品(FR-008)

Content-Type :multipart/form-data

Part 名 类型 说明
param JSON 字符串 { prizeName, description, price }
prizePic File 奖品图片,最大 10MB

处理 :图片存本地 pic.local-path,文件名 UUID+后缀;imageUrl 存文件名

输出:奖品 ID(Long)

5.3.2 奖品分页列表(FR-009)

输入 :currentPage(默认 1)、pageSize(默认 10)

输出 :{ total, records:[{ prizeId, prizeName, description, price, imageUrl }] }

5.3.3 奖品全量列表(FR-010)

输出 :[{ prizeId, prizeName }]


5.4 FR-011 ~ FR-013 活动管理

5.4.1 创建活动(FR-011)

输入:

json 复制代码
{
  "activityName": "string, 必填",
  "description": "string, 必填",
  "activityPrizeList": [
    { "prizeId": "long", "prizeAmount": "long", "prizeTiers": "FIRST_PRIZE|SECOND_PRIZE|THIRD_PRIZE" }
  ],
  "activityUserList": [
    { "userId": "long", "userName": "string" }
  ]
}

校验:

  • 奖品 ID、用户 ID 均存在于主表
  • 参与人数 ≥ 奖品总数
  • 奖品等级合法

事务:写 activity + activity_prize + activity_user,初始状态分别为 RUNNING、INIT、INIT

缓存 :组装 ActivityDetailDTO 写入 Redis

输出 :{ activityId }

5.4.2 活动列表(FR-012)

输出字段 valid :status == RUNNING 为 true

前端行为:

  • valid=true → 「活动进行中,去抽奖」
  • valid=false → 「活动已完成,查看中奖名单」
5.4.3 活动详情(FR-013)

输入 :Query activityId

输出:

字段 说明
valid 活动是否进行中
prizes\[\].valid 奖品是否未抽取(INIT=true)
prizes\[\].prizeTierName 等级中文名
prizes 按等级 code 升序排列
users\[\].valid 用户是否未中奖(INIT=true)

读取顺序:Redis → MySQL 多表组装 → 回写 Redis


5.5 FR-014 ~ FR-016 抽奖与结果

5.5.1 提交抽奖(FR-014)

输入:

json 复制代码
{
  "activityId": "long, 必填",
  "prizeId": "long, 必填",
  "winningTime": "Date, 必填",
  "winnerList": [
    { "userId": "long, 必填", "userName": "string, 必填" }
  ]
}

同步行为:仅发送 RabbitMQ 消息,不做业务落库

MQ 消费校验(失败则 return 或回滚):

  • 活动、活动奖品存在
  • 活动非 COMPLETED
  • 奖品非 COMPLETED
  • winnerList.size() == prizeAmount
5.5.2 查询中奖记录(FR-015)

输入 :activityId(必填)、prizeId(可选)

逻辑:

  • 无 prizeId:查活动维度全量
  • 有 prizeId:查该奖品维度

读取:Redis → MySQL → 回写 Redis

免登录:在白名单中

5.5.3 抽奖大屏(FR-016)

URL 参数 :activityId、activityName、valid(true/false)

权限:

  • valid=true 且 localStorage user_identity=ADMIN:可抽奖
  • valid=true 且非 ADMIN:弹窗提示,不可操作
  • valid=false:直接展示全量中奖名单

刷新恢复 :已抽完奖品(valid=false)点击开始 → 直接查 /winning-records/show?prizeId= 展示名单

分享 :复制链接,附加 valid=false&hideButton=true,隐藏操作按钮


5.6 FR-017 ~ FR-018 通知

5.6.1 中奖邮件(FR-017)

触发:MQ 消费成功后,线程池异步执行

模板(实际代码):

复制代码
Hi,{winnerName}!
有个好消息要告诉你:你在【{activityName}】活动中,获得了{prizeTier中文}的惊喜福利:{prizeName}!
福利发放时间:{HH:mm:ss},快来领取你的专属惊喜吧~

收件人:中奖用户邮箱

5.6.2 中奖短信(FR-018)

状态 :V1.0 未实现,MqReceiver.sendMessage 仅打印日志


6. 接口规格索引

序号 方法 路径 鉴权 详细定义
1 POST /register 否 见接口设计文档 §4.1
2 GET /verification-code/send 否 见接口设计文档 §4.2
3 * /password/login 否 见接口设计文档 §4.3
4 * /message/login 否 见接口设计文档 §4.4
5 GET /base-user/find-list 是 见接口设计文档 §4.5
6 * /pic/upload 是 见接口设计文档 §5.1
7 * /prize/create 是 见接口设计文档 §5.2
8 GET /prize/find-list 是 见接口设计文档 §5.3
9 GET /prize/find-listAll 是 见接口设计文档 §5.4
10 * /activity/create 是 见接口设计文档 §6.1
11 GET /activity/find-list 是 见接口设计文档 §6.2
12 GET /activity-detail/find 是 见接口设计文档 §6.3
13 * /draw-prize 是 见接口设计文档 §7.1
14 * /winning-records/show 否 见接口设计文档 §7.2

注:标注 * 的接口 Controller 使用 @RequestMapping,未限定 HTTP 方法;前端实际使用 POST(JSON)或 GET(Query)。


7. 前端页面规格

7.1 实现说明

本项目 Web 前端(src/main/resources/static)在页面结构、样式与部分交互实现上 借助 AI 辅助完成 ,设计取向为 美观、现代化视觉与现场展示效果,而非传统 B 端「功能优先、样式从简」风格。典型体现包括:

  • 登录/注册页:分栏布局、渐变背景与表单视觉优化
  • 管理后台:iframe 导航 + 统一卡片/表格样式
  • 抽奖大屏(draw.html):全屏背景、奖品展示、人名滚动动效、中奖确认交互

规格边界 :本文档对前端的约束以 页面路由、功能行为、Token 传递、接口调用 为准;颜色、间距、动效细节以当前 AI 辅助产出为基线,不作为像素级设计稿验收项。后端 REST 接口规格不受 UI 实现方式影响。

7.2 页面清单

页面 路径 功能
后台登录 /blogin.html 密码/验证码 Tab 登录
管理后台 /admin.html iframe 框架
注册用户 /register.html 管理员/普通用户注册
人员列表 /user-list.html 用户列表
创建奖品 /create-prizes.html 表单 + 图片
奖品列表 /prizes-list.html 分页
创建活动 /create-activity.html 圈选奖品/人员
活动列表 /activities-list.html 分页 + 跳转 draw
抽奖大屏 /draw.html 核心抽奖交互

7.3 通用前端约定

Token 存储 :localStorage.user_token、localStorage.user_identity

请求头 :user_token: {JWT}


8. 状态机规格

8.1 状态扭转顺序

ActivityStatusManagerImpl.handlerEvent:

  1. Sequence 1 :PrizeOperator、UserOperator(并行遍历,先奖品/用户)
  2. Sequence 2 :ActivityOperator(全部奖品 COMPLETED 后才转活动)

8.2 抽奖正向扭转

对象 原状态 目标状态
activity_prize INIT COMPLETED
activity_user(中奖者) INIT COMPLETED
activity RUNNING COMPLETED(全部奖品抽完后)

8.3 异常回滚

对象 回滚目标
activity_prize INIT
activity_user INIT
activity RUNNING
winning_record 删除对应记录 + 清缓存

9. 安全规格

编号 规格
SEC-01 JWT Header 名称固定为 user_token
SEC-02 Token 解析失败返回 HTTP 401
SEC-03 业务异常统一 { code:500, msg:业务消息 }
SEC-04 图片上传限制 10MB
SEC-05 手机号不得明文落库

10. 异常与容错规格

10.1 MQ 重试

  • spring.rabbitmq.listener.simple.retry.enabled=true
  • max-attempts=5

10.2 死信队列

  • 正常队列:DirectQueue → 死信交换机 DlxDirectExchange
  • 死信消费者 DlxReceiver 重新投递到正常队列

10.3 校验失败静默返回

checkDrawPrizeParam 返回 false 时,MqReceiver 直接 return,不抛异常(消息被 ACK)


11. 验收标准

11.1 功能验收

编号 验收项 通过标准
AC-01 管理员注册登录 可进入 admin.html
AC-02 创建普通用户 列表可见 NORMAL 用户
AC-03 创建奖品 列表展示图片与信息
AC-04 创建活动 返回 activityId,详情正确
AC-05 完整抽奖流程 每轮落库,最终活动 COMPLETED
AC-06 刷新恢复 已抽奖品不重复抽
AC-07 分享链接 仅展示结果,无操作按钮
AC-08 邮件通知 中奖者收到邮件
AC-09 未登录拦截 受保护接口返回 401

11.2 非功能验收

编号 验收项 通过标准
AC-N01 抽奖接口响应 500ms 内返回(P95)
AC-N02 缓存命中 二次查活动详情走 Redis
AC-N03 异常回滚 模拟落库异常后状态恢复

文档结束,下一文档我们将手动实现数据库的设计。

相关推荐
程序喵大人1 分钟前
【C++入门】编译链接模型 - 01 一个 C++ 程序是怎样变成可执行文件的
开发语言·c++·编译链接模型
sunshine22 girl6 分钟前
Java学习七 Java 项目实战2-注册业务
java·学习
省钱兄--zs28 分钟前
北京24小时自助健身房系统软件开发实战:从架构设计到部署指南
java·数据仓库·spring boot·小程序·需求分析
youdexiang1 小时前
Android录音软件时间关键词检索功能分析
java·人工智能
sunshine22 girl2 小时前
Java学习七 Java 项目实战1-项目创建和主页面搭建
java·开发语言·学习
code2cat2 小时前
Java进阶篇之AtomicStampedReference:引用回到原值,版本戳仍能记住变化
java·cas·并发编程·aba
菜鸟~noob2332 小时前
【电子战】第三季预告—波武器与电子系统强力毁伤
开发语言·数据库·matlab·电子战
惊讶的猫3 小时前
工具选对之后还不够:商城 Agent 的参数与权限治理
开发语言·python
cmpxr_3 小时前
【MicroPython】如何刷新ESP8266的固件
开发语言·micropython
泡茶喝茶写代码3 小时前
A股量化数据工程:从 REST 接口到策略信号(第 8 篇):成长能力因子:营收与利润增速
java·python·股票数据api·股票数据api接口·股票量化数据api·股票量化数据接口·股票数据api数据