【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 静态资源 /staticUI 借助 AI 辅助实现,主打美观效果
日志 SLF4J + Logback logback-spring.xml
短信 Spug 推送 SMSUtil HTTP 调用
邮件 Spring Mail QQ SMTP

2.3 模块划分

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

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)

输入namemailphoneNumberpassword(管理员必填)、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(手机/邮箱)、passwordmandatoryIdentity(可选)

处理逻辑

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

输出{ token, identity }

5.1.4 短信登录(FR-003)

输入loginMobileverificationCodemandatoryIdentity(可选)

处理逻辑

  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)

与注册共用 /registeridentity=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-Typemultipart/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)

输出字段 validstatus == 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 参数activityIdactivityNamevalid(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_tokenlocalStorage.user_identity

请求头user_token: {JWT}


8. 状态机规格

8.1 状态扭转顺序

ActivityStatusManagerImpl.handlerEvent

  1. Sequence 1PrizeOperatorUserOperator(并行遍历,先奖品/用户)
  2. Sequence 2ActivityOperator(全部奖品 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 异常回滚 模拟落库异常后状态恢复

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

相关推荐
自强的小白3 分钟前
重点!起始时间和结束时间与创建时间和修改时间的区别(localdate与LocaldateTIME区别)以及增删改查什么时候设置目前时间(添加和更新员工时)
java
Brilliantwxx11 分钟前
【Linux】 软件包管理器(yum)+ Vim使用
linux·运维·服务器·开发语言·编辑器·vim
雪的季节14 分钟前
Python 线程同步与异步编程 全解析
java·开发语言
吃饱了得干活26 分钟前
缓存与数据库一致性:从理论到实战
java·后端·面试
520拼好饭被践踏33 分钟前
JAVA+Agent学习day22
java·开发语言·后端·学习
笨蛋不要掉眼泪1 小时前
Java虚拟机:堆的参数配置
java·开发语言·jvm
霸道流氓气质1 小时前
SpringBoot中使用JasperReports 报表引擎 — 介绍、原理与使用实践
java·spring boot·后端
冰暮流星1 小时前
javascript之date对象
开发语言·javascript·ecmascript
花生了什么事o1 小时前
DDD 分层架构:六层分层架构
java·架构·ddd
Y3815326621 小时前
3 种 SERP 数据处理方案对比:流式 / 批处理 / 缓存
java·spring·缓存