本系统是基于若依(RuoYi)框架开发的羽毛球活动管理系统,旨在为羽毛球俱乐部或组织提供一套完整的信息化管理解决方案。系统涵盖用户签到、活动记录、财务账单、采购管理等核心业务模块,通过数字化手段提升羽毛球活动的管理效率和财务透明度。
系统旨在实现以下核心目标:首先,建立规范化的采购管理流程,所有羽毛球及相关耗材的采购均需在系统中留痕,确保采购数据的完整性和可追溯性;其次,实现智能化的活动记录管理,用户每日签到后系统自动创建当日活动记录,所有授权用户均可编辑活动信息,包括羽毛球消耗数量等关键数据;第三,构建自动化的费用计算机制,活动记录中的羽毛球消耗可与采购模块联动,根据采购单价自动计算当日费用;最后,提供便捷的账单生成功能,系统每月根据活动记录自动生成参与用户的费用账单,支持财务对账和费用分摊。
本系统适用于各类羽毛球俱乐部、社区羽毛球协会、企业羽毛球兴趣小组等组织。无论是日常活动管理、费用分摊,还是采购库存监控,本系统都能提供有效的技术支撑。
前端采用现代化的Vue 3生态系统构建,具体技术选型如下:
| 技术组件 | 版本号 | 用途说明 |
|---|---|---|
| Vue.js | 3.5.16 | 核心前端框架,采用Composition API |
| Element Plus | 2.10.7 | UI组件库,提供丰富的表单、表格、弹窗等组件 |
| Vite | 6.3.5 | 构建工具,提供极速的开发体验和优化的打包策略 |
| Pinia | 3.0.2 | 状态管理,用于管理用户信息、全局配置等共享状态 |
| Vue Router | 4.5.1 | 路由管理,实现页面导航和权限控制 |
| Axios | 1.9.0 | HTTP客户端,用于与后端API进行数据交互 |
| ECharts | 5.6.0 | 图表库,用于数据可视化展示 |
| vue-cropper | 1.1.1 | 图片裁剪组件,用于用户头像编辑等场景 |
| @vueup/vue-quill | 1.2.0 | 富文本编辑器,用于活动描述等富文本内容编辑 |
| Sass | 1.89.1 | CSS预处理器,提供变量、嵌套、混入等高级特性 |
后端基于Spring Boot框架构建(需单独部署),具体技术选型如下:
| 技术组件 | 版本号 | 用途说明 |
|---|---|---|
| Spring Boot | 2.7.x 或 3.x | 后端核心框架 |
| MyBatis / MyBatis-Plus | - | 持久层框架 |
| MySQL | 8.0+ | 关系型数据库 |
| Redis | 6.x+ | 缓存数据库(可选) |
| JWT | - | Token认证 |
┌─────────────────────────────────────────────────────────────┐
│ 前端展示层 │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ 签到模块 │ 活动记录 │ 账单管理 │ 采购管理 │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ API网关层 │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ 统一响应 │ 权限校验 │ 请求路由 │ 日志记录 │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 业务服务层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ 签到服务 │ │活动服务 │ │账单服务 │ │采购服务 │ │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 数据访问层 │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ MyBatis / MySQL │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
前端项目目录结构遵循若依框架规范,具体如下:
src/
├── api/ # API接口定义
│ ├── activity/ # 活动记录相关接口
│ ├── billing/ # 账单相关接口
│ ├── procurement/ # 采购相关接口
│ └── signin/ # 签到相关接口
├── views/ # 页面组件
│ └── badminton/ # 羽毛球管理模块
│ ├── activity/ # 活动记录页面
│ ├── billing/ # 账单管理页面
│ ├── procurement/ # 采购管理页面
│ └── signin/ # 签到页面
├── store/ # Pinia状态管理
├── router/ # 路由配置
└── utils/ # 工具函数
系统涉及六个核心实体:用户(User)、签到记录(SignIn)、活动记录(Activity)、采购记录(Procurement)、羽毛球批次(ShuttlecockBatch)、账单记录(Bill)。各实体之间的关系为:用户与签到记录为一对多关系,签到记录与活动记录为一对一关系,活动记录与账单记录为一对多关系,羽毛球批次与活动记录通过羽毛球消耗明细进行关联(支持多活动消耗同一批次,也支持单活动消耗多批次)。
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ User │──────▶│ SignIn │──────▶│ Activity │
│ 用户表 │一对多 │ 签到记录 │一对多 │ 活动记录 │
└─────────────┘ └─────────────┘ └──────┬──────┘
│
┌────────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│Bill │ │Batch │ │BillDetail │
│ 账单记录 │ │ 羽毛球批次 │ │ 账单明细 │
└─────────────┘ └─────────────┘ └─────────────┘
│
│一对多
▼
┌─────────────────┐
│ShuttlecockUsage │
│ 羽毛球消耗明细 │
└─────────────────┘
用户与签到记录的关系为User(1)——(N)SignIn,一个用户可以有多条签到记录。
签到记录与活动记录的关系为SignIn(1)——(1)Activity,一个签到记录对应一个活动记录。
活动记录与羽毛球批次的关系为Activity(1)——(N)ShuttlecockUsage——(1)Batch,一次活动可以消耗多个批次的羽毛球,一个批次的羽毛球可以被多次活动消耗。
活动记录与账单记录的关系为Activity(1)——(N)BillDetail——(1)Bill,一次活动产生的费用会分摊到用户账单中。
采购记录与羽毛球批次的关系为Procurement(1)——(1)Batch,一次采购生成一个库存批次。
本表用于存储羽毛球爱好者的扩展信息,在系统用户的基础上增加羽毛球运动相关的属性。
| 字段名 | 数据类型 | 长度 | 允许为空 | 默认值 | 说明 |
|---|---|---|---|---|---|
| user_id | BIGINT | 20 | 否 | - | 用户ID(关联sys_user表) |
| nickname | VARCHAR | 30 | 是 | - | 昵称 |
| phone | VARCHAR | 11 | 是 | - | 联系电话 |
| level | VARCHAR | 20 | 是 | - | 球龄等级(初级/中级/高级) |
| is_active | TINYINT | 1 | 是 | 1 | 是否活跃会员(0否/1是) |
| balance | DECIMAL | 10,2 | 是 | 0.00 | 账户余额 |
| total_consumption | DECIMAL | 10,2 | 是 | 0.00 | 累计消费 |
| remark | VARCHAR | 500 | 是 | - | 备注 |
| create_time | DATETIME | - | 是 | - | 创建时间 |
| update_time | DATETIME | - | 是 | - | 更新时间 |
本表用于记录用户的日常签到信息,是活动记录的数据来源。
| 字段名 | 数据类型 | 长度 | 允许为空 | 默认值 | 说明 |
|---|---|---|---|---|---|
| id | BIGINT | 20 | 否 | - | 主键ID |
| user_id | BIGINT | 20 | 否 | - | 签到用户ID |
| sign_date | DATE | - | 否 | - | 签到日期 |
| sign_time | DATETIME | - | 否 | - | 签到时间 |
| activity_id | BIGINT | 20 | 是 | - | 关联的活动记录ID |
| status | TINYINT | 1 | 是 | 1 | 签到状态(0缺席/1已签到/2请假) |
| remark | VARCHAR | 500 | 是 | - | 备注说明 |
| create_by | VARCHAR | 64 | 是 | - | 创建者 |
| create_time | DATETIME | - | 是 | - | 创建时间 |
索引设计:签到日期和用户ID需建立联合索引,确保按日期查询和用户查询的高效性。
本表用于记录每日的羽毛球活动信息,是系统业务的核心表。
| 字段名 | 数据类型 | 长度 | 允许为空 | 默认值 | 说明 |
|---|---|---|---|---|---|
| id | BIGINT | 20 | 否 | - | 主键ID |
| activity_date | DATE | - | 否 | - | 活动日期 |
| title | VARCHAR | 100 | 否 | - | 活动标题 |
| venue | VARCHAR | 200 | 是 | - | 活动场地 |
| start_time | TIME | - | 是 | - | 开始时间 |
| end_time | TIME | - | 是 | - | 结束时间 |
| participant_count | INT | - | 是 | 0 | 参与人数 |
| shuttlecock_count | INT | - | 是 | 0 | 羽毛球消耗数量(个) |
| shuttlecock_cost | DECIMAL | 10,2 | 是 | 0.00 | 羽毛球费用(元) |
| batch_id | BIGINT | 20 | 是 | - | 采购批次ID(关联羽毛球批次) |
| batch_no | VARCHAR | 30 | 是 | - | 采购批次号 |
| other_cost | DECIMAL | 10,2 | 是 | 0.00 | 其他费用(元) |
| total_cost | DECIMAL | 10,2 | 是 | 0.00 | 总费用(元) |
| cost_per_person | DECIMAL | 10,2 | 是 | 0.00 | 人均费用(元) |
| weather | VARCHAR | 20 | 是 | - | 天气情况 |
| description | TEXT | - | 是 | - | 活动描述 |
| status | TINYINT | 1 | 是 | 1 | 状态(0草稿/1已完成) |
| create_by | VARCHAR | 64 | 是 | - | 创建者 |
| create_time | DATETIME | - | 是 | - | 创建时间 |
| update_by | VARCHAR | 64 | 是 | - | 更新者 |
| update_time | DATETIME | - | 是 | - | 更新时间 |
本表用于记录每次活动的参与人员明细,支持费用分摊计算。
| 字段名 | 数据类型 | 长度 | 允许为空 | 默认值 | 说明 |
|---|---|---|---|---|---|
| id | BIGINT | 20 | 否 | - | 主键ID |
| activity_id | BIGINT | 20 | 否 | - | 活动记录ID |
| user_id | BIGINT | 20 | 否 | - | 用户ID |
| signin_id | BIGINT | 20 | 是 | - | 关联的签到记录ID |
| is_paid | TINYINT | 1 | 是 | 0 | 是否已付款(0否/1是) |
| amount | DECIMAL | 10,2 | 是 | 0.00 | 分摊金额 |
| pay_time | DATETIME | - | 是 | - | 付款时间 |
| remark | VARCHAR | 500 | 是 | - | 备注 |
| create_time | DATETIME | - | 是 | - | 创建时间 |
唯一约束:activity_id和user_id组合唯一,防止同一用户重复参与同一活动。
本表用于记录羽毛球及相关耗材的采购信息。
| 字段名 | 数据类型 | 长度 | 允许为空 | 默认值 | 说明 |
|---|---|---|---|---|---|
| id | BIGINT | 20 | 否 | - | 主键ID |
| batch_no | VARCHAR | 30 | 否 | - | 采购批次号(格式:PC+日期+序号) |
| procurement_date | DATE | - | 否 | - | 采购日期 |
| category_id | INT | - | 否 | - | 耗材分类ID |
| category_name | VARCHAR | 50 | 是 | - | 耗材分类名称 |
| item_name | VARCHAR | 100 | 否 | - | 商品名称 |
| specification | VARCHAR | 100 | 是 | - | 规格型号 |
| unit | VARCHAR | 20 | 否 | - | 计量单位(个/筒/箱/瓶) |
| quantity | INT | - | 否 | 0 | 采购数量 |
| unit_price | DECIMAL | 10,2 | 否 | 0.00 | 单价(元) |
| total_amount | DECIMAL | 10,2 | 是 | 0.00 | 总金额(元) |
| supplier | VARCHAR | 100 | 是 | - | 供应商 |
| contact_person | VARCHAR | 50 | - | - | 联系人 |
| contact_phone | VARCHAR | 20 | - | - | 联系电话 |
| current_stock | INT | - | 是 | 0 | 当前库存数量 |
| remark | VARCHAR | 500 | 是 | - | 备注说明 |
| status | TINYINT | 1 | 是 | 1 | 状态(0未入库/1已入库/2已用完) |
| create_by | VARCHAR | 64 | 是 | - | 创建者 |
| create_time | DATETIME | - | 是 | - | 创建时间 |
| update_time | DATETIME | - | 是 | - | 更新时间 |
索引设计:batch_no字段建立唯一索引,确保批次号的唯一性;category_id和procurement_date建立联合索引,支持按分类和时间查询。
本表用于记录羽毛球各批次的库存详情,支持按批次核算成本和消耗追踪。该设计实现了采购批次的精细化管理,每个批次的羽毛球独立核算,便于进行成本分析和消耗追踪。
| 字段名 | 数据类型 | 长度 | 允许为空 | 默认值 | 说明 |
|---|---|---|---|---|---|
| id | BIGINT | 20 | 否 | - | 主键ID |
| batch_no | VARCHAR | 30 | 否 | - | 批次号(关联采购记录) |
| item_name | VARCHAR | 100 | 否 | - | 商品名称 |
| category_id | INT | - | 否 | - | 分类ID |
| unit_price | DECIMAL | 10,2 | 否 | 0.00 | 采购单价(元) |
| total_quantity | INT | - | 否 | 0 | 采购总量(个) |
| remaining_quantity | INT | - | 是 | 0 | 剩余数量(个) |
| used_quantity | INT | - | 是 | 0 | 已消耗数量(个) |
| procurement_date | DATE | - | 否 | - | 采购日期 |
| expiry_date | DATE | - | 是 | - | 有效期(羽毛球保质期) |
| status | TINYINT | 1 | 是 | 1 | 状态(0已用完/1有库存/2预警) |
| create_time | DATETIME | - | 是 | - | 创建时间 |
| update_time | DATETIME | - | 是 | - | 更新时间 |
设计说明:
唯一约束:batch_no唯一,确保每个批次只存在一条库存记录。
索引设计:remaining_quantity建立索引,支持库存预警查询;procurement_date建立索引,支持批次追溯查询。
本表用于记录各活动对羽毛球批次的消耗明细,实现批次的精确扣减和成本分摊。
| 字段名 | 数据类型 | 长度 | 允许为空 | 默认值 | 说明 |
|---|---|---|---|---|---|
| id | BIGINT | 20 | 否 | - | 主键ID |
| activity_id | BIGINT | 20 | 否 | - | 活动记录ID |
| batch_id | BIGINT | 20 | 否 | - | 羽毛球批次ID |
| batch_no | VARCHAR | 30 | 是 | - | 批次号 |
| usage_quantity | INT | - | 否 | 0 | 消耗数量(个) |
| unit_price | DECIMAL | 10,2 | 否 | 0.00 | 消耗时的单价(元) |
| total_cost | DECIMAL | 10,2 | 是 | 0.00 | 消耗总金额(元) |
| create_time | DATETIME | - | 是 | - | 创建时间 |
业务逻辑:
本表用于定义耗材的分类信息。
| 字段名 | 数据类型 | 长度 | 允许为空 | 默认值 | 说明 |
|---|---|---|---|---|---|
| id | INT | - | 否 | - | 主键ID |
| category_name | VARCHAR | 50 | 否 | - | 分类名称 |
| parent_id | INT | - | 是 | 0 | 父级分类ID |
| sort | INT | - | 是 | 0 | 排序号 |
| status | TINYINT | 1 | 是 | 1 | 状态(0禁用/1启用) |
| create_time | DATETIME | - | 是 | - | 创建时间 |
分类示例:羽毛球(YQ-01)、羽毛球拍(QP-01)、手胶(SG-01)、运动饮料(YP-01)、场地租赁(ZC-01)等。
本表用于记录每月生成的用户账单。
| 字段名 | 数据类型 | 长度 | 允许为空 | 默认值 | 说明 |
|---|---|---|---|---|---|
| id | BIGINT | 20 | 否 | - | 主键ID |
| bill_month | VARCHAR | 7 | 否 | - | 账单月份(格式:2024-01) |
| user_id | BIGINT | 20 | 否 | - | 用户ID |
| activity_count | INT | - | 是 | 0 | 参与活动次数 |
| total_amount | DECIMAL | 10,2 | 否 | 0.00 | 账单总金额 |
| paid_amount | DECIMAL | 10,2 | 是 | 0.00 | 已付金额 |
| balance | DECIMAL | 10,2 | 是 | 0.00 | 待付金额 |
| status | TINYINT | 1 | 是 | 0 | 账单状态(0未付款/1部分付款/2已结清) |
| generate_time | DATETIME | - | 是 | - | 账单生成时间 |
| pay_deadline | DATE | - | 是 | - | 付款截止日期 |
| remark | VARCHAR | 500 | 是 | - | 备注 |
| create_time | DATETIME | - | 是 | - | 创建时间 |
| update_time | DATETIME | - | 是 | - | 更新时间 |
本表用于记录账单的明细信息,关联具体活动。
| 字段名 | 数据类型 | 长度 | 允许为空 | 默认值 | 说明 |
|---|---|---|---|---|---|
| id | BIGINT | 20 | 否 | - | 主键ID |
| bill_id | BIGINT | 20 | 否 | - | 账单ID |
| activity_id | BIGINT | 20 | 否 | - | 活动记录ID |
| activity_date | DATE | - | 是 | - | 活动日期 |
| activity_title | VARCHAR | 100 | - | - | 活动标题 |
| amount | DECIMAL | 10,2 | 否 | 0.00 | 费用金额 |
| is_paid | TINYINT | 1 | 是 | 0 | 是否已付款 |
| pay_time | DATETIME | - | 是 | - | 付款时间 |
| create_time | DATETIME | - | 是 | - | 创建时间 |
┌─────────────────────────────────────────────────────────────────────────┐
│ 采购管理流程 │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────┐
│ 新建采购记录 │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ 自动计算库存增量 │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ 入库确认 │
└─────────────────────┘
│
┌─────────────────────────────────────────────────────────────────────────┐
│ 签到与活动流程 │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────┐
│ 用户签到 │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ 检查当日活动记录 │
└─────────────────────┘
│
┌────────────┴────────────┐
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ 活动记录存在 │ │ 活动记录不存在 │
└─────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ 绑定签到记录 │ │ 创建活动记录 │
└─────────────────┘ └─────────────────┘
│
▼
┌─────────────────────────────┐
│ 用户编辑活动记录 │
│ (填写消耗信息等) │
└─────────────────────────────┘
│
▼
┌─────────────────────────────┐
│ 羽毛球消耗与采购联动 │
│ 自动计算费用 │
└─────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ 账单生成流程 │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────┐
│ 每月1日自动生成 │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ 汇总上月活动记录 │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ 计算每人分摊金额 │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ 生成账单记录 │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ 用户查看并付款 │
└─────────────────────┘
API设计遵循RESTful规范,采用JSON格式进行数据交互,基础路径为/badminton/。
统一响应格式示例:
{
"code": 200,
"msg": "操作成功",
"data": {
// 业务数据
}
}
接口地址:POST /badminton/signin/checkin
请求参数:
{
"userId": 1,
"remark": "今天状态很好"
}
响应数据:
{
"code": 200,
"msg": "签到成功",
"data": {
"signinId": 1001,
"signDate": "2024-01-15",
"signTime": "2024-01-15 18:30:25",
"activityId": 501,
"activityDate": "2024-01-15"
}
}
业务逻辑说明:用户签到时,系统首先检查当日是否存在活动记录,如果不存在则自动创建新的活动记录。然后将签到记录与活动记录进行关联,最后返回签到结果和关联的活动信息。
接口地址:GET /badminton/signin/getTodaySigninList
请求参数:无
响应数据:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"signinId": 1001,
"userId": 1,
"nickname": "张三",
"avatar": "https://xxx.com/avatar1.jpg",
"signTime": "2024-01-15 18:30:25",
"status": 1
},
{
"signinId": 1002,
"userId": 2,
"nickname": "李四",
"avatar": "https://xxx.com/avatar2.jpg",
"signTime": "2024-01-15 18:35:10",
"status": 1
}
]
}
接口地址:GET /badminton/signin/getSigninStats
请求参数: | 参数名 | 类型 | 必填 | 说明 | |-------|------|------|------| | userId | Long | 否 | 用户ID,不传则查询全部 | | startDate | Date | 是 | 开始日期 | | endDate | Date | 是 | 结束日期 |
响应数据:
{
"code": 200,
"msg": "查询成功",
"data": {
"totalSigninCount": 25,
"totalActivityCount": 20,
"averageCost": 35.50,
"monthlyStats": [
{
"month": "2024-01",
"signinCount": 8,
"activityCount": 6
}
]
}
}
接口地址:GET /badminton/activity/list
请求参数: | 参数名 | 类型 | 必填 | 说明 | |-------|------|------|------| | activityDate | Date | 否 | 活动日期 | | status | Integer | 否 | 状态(0草稿/1已完成) | | pageNum | Integer | 是 | 页码 | | pageSize | Integer | 是 | 每页数量 |
响应数据:
{
"code": 200,
"msg": "查询成功",
"rows": [
{
"id": 501,
"activityDate": "2024-01-15",
"title": "周一例行活动",
"venue": "体育中心羽毛球馆",
"startTime": "19:00:00",
"endTime": "22:00:00",
"participantCount": 12,
"shuttlecockCount": 6,
"shuttlecockCost": 90.00,
"otherCost": 150.00,
"totalCost": 240.00,
"costPerPerson": 20.00,
"status": 1
}
],
"total": 50
}
接口地址:GET /badminton/activity/{id}
响应数据:
{
"code": 200,
"msg": "查询成功",
"data": {
"id": 501,
"activityDate": "2024-01-15",
"title": "周一例行活动",
"venue": "体育中心羽毛球馆",
"startTime": "19:00:00",
"endTime": "22:00:00",
"participantCount": 12,
"shuttlecockCount": 6,
"shuttlecockCost": 90.00,
"otherCost": 150.00,
"totalCost": 240.00,
"costPerPerson": 20.00,
"weather": "晴",
"description": "今天大家状态很好",
"participants": [
{
"userId": 1,
"nickname": "张三",
"isPaid": 1,
"amount": 20.00
}
],
"createTime": "2024-01-15 18:00:00"
}
}
接口地址:POST /badminton/activity
请求参数:
{
"activityDate": "2024-01-15",
"title": "周二例行活动",
"venue": "体育中心羽毛球馆",
"startTime": "19:00:00",
"endTime": "22:00:00",
"shuttlecockCount": 5,
"otherCost": 200.00,
"weather": "多云",
"description": "活动描述内容"
}
响应数据:
{
"code": 200,
"msg": "创建成功",
"data": {
"id": 502,
"totalCost": 275.00,
"costPerPerson": 0.00
}
}
接口地址:PUT /badminton/activity
请求参数:
{
"id": 502,
"title": "周二例行活动(修改)",
"shuttlecockCount": 8,
"otherCost": 100.00,
"description": "修改后的描述"
}
业务逻辑说明:更新羽毛球消耗数量时,系统自动调用采购模块计算最新的羽毛球平均单价,然后重新计算羽毛球费用和总费用。如果羽毛球库存不足,需要给出预警提示。
接口地址:POST /badminton/activity/calculateCost
请求参数:
{
"activityId": 502,
"shuttlecockCount": 6
}
响应数据:
{
"code": 200,
"msg": "计算成功",
"data": {
"averagePrice": 15.00,
"totalShuttlecockCost": 90.00,
"totalCost": 290.00,
"stockWarning": false,
"currentStock": 24
}
}
接口地址:GET /badminton/activity/{id}/participants
响应数据:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"userId": 1,
"nickname": "张三",
"avatar": "https://xxx.com/avatar1.jpg",
"signinTime": "2024-01-15 18:30:25",
"isPaid": true,
"amount": 20.00
}
]
}
接口地址:POST /badminton/activity/{id}/participant
请求参数:
{
"userId": 5
}
接口地址:GET /badminton/procurement/list
请求参数: | 参数名 | 类型 | 必填 | 说明 | |-------|------|------|------| | procurementDate | Date | 否 | 采购日期 | | categoryId | Integer | 否 | 分类ID | | itemName | String | 否 | 商品名称 | | status | Integer | 否 | 状态 | | pageNum | Integer | 是 | 页码 | | pageSize | Integer | 是 | 每页数量 |
响应数据:
{
"code": 200,
"msg": "查询成功",
"rows": [
{
"id": 201,
"procurementDate": "2024-01-10",
"categoryName": "羽毛球",
"itemName": "亚狮龙10号羽毛球",
"specification": "12个/筒",
"unit": "筒",
"quantity": 10,
"unitPrice": 15.00,
"totalAmount": 150.00,
"currentStock": 120,
"status": 1,
"createTime": "2024-01-10 10:30:00"
}
],
"total": 30
}
接口地址:GET /badminton/procurement/category/list
响应数据:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"id": 1,
"categoryName": "羽毛球",
"children": [
{
"id": 101,
"categoryName": "训练用球"
},
{
"id": 102,
"categoryName": "比赛用球"
}
]
}
]
}
接口地址:POST /badminton/procurement
请求参数:
{
"procurementDate": "2024-01-15",
"categoryId": 101,
"categoryName": "训练用球",
"itemName": "红超羽毛球",
"specification": "12个/筒",
"unit": "筒",
"quantity": 20,
"unitPrice": 18.00,
"supplier": "XX体育用品店",
"contactPerson": "王老板",
"contactPhone": "13800138000",
"remark": "月度采购"
}
响应数据:
{
"code": 200,
"msg": "采购成功",
"data": {
"id": 202,
"totalAmount": 360.00,
"currentStock": 140
}
}
接口地址:PUT /badminton/procurement/{id}/confirm
请求参数:无
响应数据:
{
"code": 200,
"msg": "入库确认成功",
"data": {
"id": 202,
"currentStock": 140,
"status": 1
}
}
接口地址:GET /badminton/procurement/stock/list
响应数据:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"itemId": 201,
"itemName": "亚狮龙10号羽毛球",
"categoryName": "羽毛球",
"currentStock": 120,
"unit": "个",
"averageCost": 15.00,
"lastProcurementDate": "2024-01-10",
"stockWarning": false,
"warningThreshold": 20
}
]
}
接口地址:GET /badminton/procurement/calculateAvgPrice
请求参数: | 参数名 | 类型 | 必填 | 说明 | |-------|------|------|------| | categoryId | Integer | 是 | 分类ID(羽毛球分类) |
响应数据:
{
"code": 200,
"msg": "计算成功",
"data": {
"categoryId": 101,
"categoryName": "羽毛球",
"averagePrice": 15.00,
"totalStock": 240,
"totalValue": 3600.00
}
}
接口地址:GET /badminton/procurement/stock/warning
响应数据:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"itemId": 201,
"itemName": "亚狮龙10号羽毛球",
"currentStock": 15,
"warningThreshold": 20,
"suggestQuantity": 30
}
]
}
接口地址:GET /badminton/procurement/batch/list
请求参数: | 参数名 | 类型 | 必填 | 说明 | |-------|------|------|------| | categoryId | Integer | 否 | 分类ID(羽毛球分类) | | status | Integer | 否 | 批次状态(0已用完/1有库存/2预警) |
响应数据:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"id": 1,
"batchNo": "PC-20240110-001",
"itemName": "亚狮龙10号羽毛球",
"categoryId": 101,
"unitPrice": 15.00,
"totalQuantity": 120,
"remainingQuantity": 120,
"procurementDate": "2024-01-10",
"status": 1,
"statusLabel": "有库存"
},
{
"id": 2,
"batchNo": "PC-20240110-002",
"itemName": "亚狮龙10号羽毛球",
"categoryId": 101,
"unitPrice": 16.00,
"totalQuantity": 60,
"remainingQuantity": 60,
"procurementDate": "2024-01-10",
"status": 1,
"statusLabel": "有库存"
},
{
"id": 3,
"batchNo": "PC-20240105-001",
"itemName": "尤尼克斯比赛级",
"categoryId": 102,
"unitPrice": 18.00,
"totalQuantity": 30,
"remainingQuantity": 30,
"procurementDate": "2024-01-05",
"status": 2,
"statusLabel": "库存预警"
}
]
}
接口地址:POST /badminton/procurement/batch/calculateCost
请求参数:
{
"batchId": 1,
"usageQuantity": 6
}
响应数据:
{
"code": 200,
"msg": "计算成功",
"data": {
"batchId": 1,
"batchNo": "PC-20240110-001",
"unitPrice": 15.00,
"usageQuantity": 6,
"totalCost": 90.00,
"remainingStock": 114,
"stockEnough": true
}
}
业务逻辑说明:该接口在用户选择羽毛球批次并输入消耗数量后调用,系统自动计算费用并检查库存是否充足。如果库存不足,返回stockEnough: false和警告信息。
接口地址:GET /badminton/procurement/batch/{batchId}/usageHistory
响应数据:
{
"code": 200,
"msg": "查询成功",
"data": {
"batch": {
"id": 1,
"batchNo": "PC-20240110-001",
"itemName": "亚狮龙10号羽毛球",
"unitPrice": 15.00,
"totalQuantity": 120,
"remainingQuantity": 78
},
"usageList": [
{
"id": 1,
"activityDate": "2024-01-15",
"activityTitle": "周一例行活动",
"usageQuantity": 6,
"totalCost": 90.00,
"createTime": "2024-01-15 20:30:00"
},
{
"id": 2,
"activityDate": "2024-01-13",
"activityTitle": "周六友谊赛",
"usageQuantity": 4,
"totalCost": 60.00,
"createTime": "2024-01-13 21:15:00"
}
],
"totalUsage": 42,
"totalCost": 630.00
}
}
接口地址:GET /badminton/bill/list
请求参数: | 参数名 | 类型 | 必填 | 说明 | |-------|------|------|------| | billMonth | String | 否 | 账单月份(2024-01) | | userId | Long | 否 | 用户ID | | status | Integer | 否 | 账单状态 | | pageNum | Integer | 是 | 页码 | | pageSize | Integer | 是 | 每页数量 |
响应数据:
{
"code": 200,
"msg": "查询成功",
"rows": [
{
"id": 301,
"billMonth": "2024-01",
"userId": 1,
"nickname": "张三",
"activityCount": 8,
"totalAmount": 280.00,
"paidAmount": 200.00,
"balance": 80.00,
"status": 1,
"payDeadline": "2024-02-15",
"createTime": "2024-02-01 00:00:00"
}
],
"total": 25
}
接口地址:GET /badminton/bill/{id}
响应数据:
{
"code": 200,
"msg": "查询成功",
"data": {
"id": 301,
"billMonth": "2024-01",
"userId": 1,
"nickname": "张三",
"activityCount": 8,
"totalAmount": 280.00,
"paidAmount": 200.00,
"balance": 80.00,
"status": 1,
"payDeadline": "2024-02-15",
"details": [
{
"id": 401,
"activityDate": "2024-01-15",
"activityTitle": "周一例行活动",
"amount": 35.00,
"isPaid": true,
"payTime": "2024-01-20 10:30:00"
}
],
"createTime": "2024-02-01 00:00:00"
}
}
接口地址:POST /badminton/bill/generate
请求参数:
{
"billMonth": "2024-01",
"userIds": [1, 2, 3]
}
响应数据:
{
"code": 200,
"msg": "账单生成成功",
"data": {
"generatedCount": 3,
"failedCount": 0,
"totalAmount": 840.00
}
}
接口地址:PUT /badminton/bill/{id}/pay
请求参数:
{
"payAmount": 200.00,
"remark": "微信转账"
}
响应数据:
{
"code": 200,
"msg": "付款确认成功",
"data": {
"billId": 301,
"paidAmount": 200.00,
"balance": 80.00,
"status": 1
}
}
接口地址:GET /badminton/bill/monthlySummary
请求参数: | 参数名 | 类型 | 必填 | 说明 | |-------|------|------|------| | billMonth | String | 是 | 账单月份 |
响应数据:
{
"code": 200,
"msg": "查询成功",
"data": {
"billMonth": "2024-01",
"totalUsers": 15,
"totalAmount": 5250.00,
"paidAmount": 3800.00,
"unpaidAmount": 1450.00,
"settlementRate": 72.38,
"billDetails": [
{
"userId": 1,
"nickname": "张三",
"activityCount": 8,
"totalAmount": 280.00,
"paidAmount": 200.00
}
]
}
}
接口地址:GET /badminton/bill/pending/{userId}
响应数据:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"id": 301,
"billMonth": "2024-01",
"totalAmount": 280.00,
"balance": 80.00,
"payDeadline": "2024-02-15"
},
{
"id": 302,
"billMonth": "2023-12",
"totalAmount": 320.00,
"balance": 0.00,
"payDeadline": "2024-01-15"
}
]
}
根据业务需求,前端页面按模块组织在src/views/badminton/目录下,结构如下:
src/views/badminton/
├── signin/ # 签到模块
│ └── index.vue # 签到首页
├── activity/ # 活动记录模块
│ ├── index.vue # 活动列表
│ ├── detail.vue # 活动详情
│ └── form.vue # 活动表单
├── procurement/ # 采购管理模块
│ ├── index.vue # 采购列表
│ ├── stock.vue # 库存管理
│ └── form.vue # 采购表单
├── billing/ # 账单模块
│ ├── index.vue # 账单列表
│ ├── detail.vue # 账单详情
│ └── generate.vue # 账单生成
└── statistics/ # 统计分析模块
└── index.vue # 数据统计
页面功能定位为用户签到和当日签到信息展示,是用户每日使用最频繁的入口页面。
页面布局采用顶部通栏设计,中间为签到大按钮,底部为当日签到列表。签到大按钮设计为圆形,直径120像素,点击时有明显的缩放动画效果。按钮颜色使用Element Plus的主题色,点击后变为禁用状态并显示"已签到"文字。
当日签到列表采用卡片式布局,每张卡片包含用户头像、昵称、签到时间和操作按钮。头像显示为圆形,使用Element Plus的Avatar组件。签到时间精确到秒,格式为"HH:mm:ss"。操作按钮包括"请假"和"移除"两个选项,需要管理员权限。
页面顶部显示当日活动的关键统计信息,包括已签到人数、活动总人数、羽毛球消耗预估等。数据实时更新,用户签到后自动刷新。
核心组件代码结构如下:模板部分包括统计卡片区域、签到按钮区域和签到列表区域;脚本部分使用Vue 3的Composition API,通过useSignin组合式函数处理签到逻辑;样式部分使用Sass编写,实现响应式布局和动画效果。
页面功能定位为历史签到数据的查询和统计,提供日、周、月三种维度的统计视图。
页面采用Tab标签页布局,默认显示日视图,支持切换周视图和月视图。日视图以日期为维度,展示每天的签到人数、签到率和参与活动情况。周视图以周为维度,展示每周的汇总数据。月视图以月为维度,展示月度签到趋势图。
统计图表使用ECharts实现,提供签到人数趋势图和参与率饼图。趋势图展示最近30天的签到人数变化,饼图展示已签到、请假、缺席三种状态的占比。
页面底部提供签到明细表格,支持按日期范围筛选和导出Excel功能。表格包含用户姓名、签到日期、签到时间、签到状态和关联活动等字段。
页面功能定位为活动记录的列表展示和快捷操作,支持按日期筛选和状态筛选。
页面采用查询表单加数据表格的标准布局。查询表单包含日期范围选择器、活动标题输入框和状态下拉选择器。表单支持回车查询和重置功能。数据表格采用Element Plus的Table组件,支持排序、列宽调整和固定列。
表格列包括:活动日期、活动标题、场地、参与人数、羽毛球消耗、总费用、人均费用、状态、操作按钮。操作按钮包括"查看详情"、"编辑"和"删除",根据用户权限显示。状态列使用Tag组件显示,"已完成"为绿色,"草稿"为灰色。
表格支持展开行功能,展开后显示参与人员列表。参与人员列表以标签形式展示用户头像和昵称,超出部分显示"+N"。
页面右上角提供"新建活动"按钮,点击弹出活动表单对话框。新建活动时可以设置活动日期、标题、场地、预计人数等信息,系统会自动创建空的参与人员列表。
页面功能定位为单次活动的详细信息展示和参与人员管理。
页面采用卡片布局,分为基本信息区、参与人员区、费用明细区和操作记录区。
基本信息区展示活动的所有字段信息,包括日期、标题、场地、时间、天气、描述等。描述字段支持富文本展示,使用VueQuill组件渲染。
参与人员区采用网格布局,每行显示4个用户卡片。用户卡片包含头像、昵称、付款状态标签和分摊金额。用户卡片支持点击操作,可以标记付款或修改分摊金额。
费用明细区展示费用的详细构成,包括羽毛球费用、其他费用和人均分摊。羽毛球费用可以点击展开,查看羽毛球消耗的数量和单价明细。
操作记录区展示活动的操作日志,包括创建时间、修改记录和参与人员变动记录。
页面底部提供操作按钮组,包括"编辑活动"、"登记消耗"、"添加参与者"、"生成分摊"和"关闭活动"。
页面功能定位为新建和编辑活动记录,采用弹窗表单形式。
表单包含以下字段:活动日期(日期选择器,必填)、活动标题(输入框,必填)、场地(输入框)、开始时间(时间选择器)、结束时间(时间选择器)、天气(选择器,选项为晴/阴/雨/雪/多云)、羽毛球消耗数量(数字输入框)、其他费用(数字输入框)、活动描述(富文本编辑器)。
羽毛球消耗数量字段具有联动特性,输入数值后系统自动计算费用并显示在下方。计算公式为:羽毛球费用等于消耗数量乘以系统计算的平均单价。平均单价根据采购记录自动计算,取最近N次采购的加权平均价。
表单验证规则:活动日期不能为空且不能是未来日期,活动标题长度不超过100个字符,羽毛球消耗数量为非负整数,其他费用为非负数。
保存前需要进行库存检查,如果消耗数量超过当前库存,需要提示用户并确认是否继续保存。
页面功能定位为采购记录的列表展示和管理。
页面布局采用查询表单加数据表格的形式。查询表单包含日期范围选择器、耗材分类级联选择器、商品名称输入框和状态下拉选择器。数据表格支持按采购日期倒序排列,每行显示采购的基本信息。
表格列包括:采购日期、商品名称、分类、规格、单位、数量、单价、金额、供应商、状态、操作按钮。金额自动计算并显示,状态使用Tag组件展示,"未入库"为黄色,"已入库"为绿色,"已用完"为红色。
操作按钮包括"查看详情"、"编辑"、"入库"和"删除"。入库操作仅对"未入库"状态的记录可见,点击后更新库存数量。
页面右上角提供"新建采购"按钮,点击弹出采购表单对话框。
页面功能定位为新建和编辑采购记录。
表单包含以下字段:采购日期(日期选择器,必填)、耗材分类(级联选择器,必填)、商品名称(输入框,必填)、规格型号(输入框)、计量单位(选择器或输入框,必填,选项为个/筒/箱/瓶/盒/袋)、采购数量(数字输入框,必填)、单价(数字输入框,必填)、供应商(输入框)、联系人(输入框)、联系电话(输入框)、备注(多行文本框)。
耗材分类采用级联选择器,选择上级分类后自动加载下级分类。选择分类后,系统自动填充分类名称字段。
数量和单价字段支持小数输入,金额自动计算并实时显示。计算公式为:金额等于数量乘以单价。
表单验证规则:采购日期不能为空且不能是未来日期之前,商品名称必填且不超过100个字符,数量必须大于0,单价必须大于等于0。
页面功能定位为耗材库存的实时监控和管理。
页面采用统计卡片加数据表格的布局。顶部统计卡片展示:耗材种类数、总库存数量、库存预警数量、上月消耗金额。数据采用数字动画效果展示,增强视觉冲击力。
库存表格展示所有耗材的库存信息,包括:商品名称、分类、当前库存、计量单位、平均单价、库存价值、最近采购日期、库存预警状态。库存预警状态使用Tag组件显示,正常为绿色,预警为红色。
表格支持按库存数量排序,便于快速定位库存不足的耗材。每行提供"采购"、"调整库存"和"查看历史"三个操作按钮。
页面侧边提供库存预警列表,固定显示在右侧边栏,实时提醒库存不足的耗材。预警列表支持一键跳转至采购页面。
页面功能定位为账单记录的列表展示和快速操作。
页面采用月份选择器加数据表格的布局。月份选择器默认为当前上一月,支持快速切换到历史月份。数据表格展示选定月份的所有账单。
表格列包括:用户信息(头像加昵称)、参与活动次数、账单金额、已付金额、待付金额、状态、付款截止日期、操作按钮。用户信息使用Element Plus的用户头像组件展示。
状态列使用Tag组件显示,"未付款"为红色,"部分付款"为橙色,"已结清"为绿色。表格支持按金额排序和按状态筛选。
页面右上角提供"生成账单"按钮,点击弹出账单生成配置对话框。配置对话框包含账单月份选择和用户范围选择,确认后系统自动生成账单。
页面功能定位为单份账单的详细信息展示和付款操作。
页面采用卡片布局,分为账单信息区、费用明细区和付款记录区。
账单信息区展示账单的基本属性,包括:账单月份、用户名、参与活动次数、账单总金额、已付金额、待付金额、状态、付款截止日期。金额信息使用大号字体突出显示。
费用明细区采用时间线组件展示,按活动日期倒序排列。每条明细包含:活动日期、活动标题、参与人数、应付金额、付款状态。已付款的明细显示绿色对勾,未付款的显示红色待付款标签。
付款记录区展示账款的收款记录,包括:收款时间、收款金额、收款方式、操作人。记录按时间倒序排列。
页面底部提供付款操作按钮组,包括"全部付款"、"部分付款"和"发送催款通知"。部分付款点击后弹出付款表单,包含付款金额和备注字段。
页面功能定位为手动触发账单生成流程,用于月末结算或补录场景。
页面采用步骤条引导用户完成账单生成流程。步骤一为选择账单月份,默认选择上一月,支持修改。步骤二为确认参与用户列表,显示可选用户列表,支持全选和逐个选择。步骤三为预览费用明细,展示即将生成的账单汇总信息。步骤四为确认生成,点击生成按钮后系统开始处理。
预览区域展示汇总信息,包括:生成账单数量、用户列表、总金额、平均金额。生成进度以进度条形式展示,生成完成后显示成功数量和失败数量。
失败的用户会显示失败原因,如:该用户本月无活动记录。
页面功能定位为系统数据的综合可视化展示,为管理者提供决策依据。
页面采用仪表盘加图表的布局,分为今日统计、月度趋势、消耗分析三个区域。
今日统计区域展示当日活动的关键指标卡片,包括:今日签到人数、进行中活动数、今日消耗羽毛球数、今日预计费用。卡片支持点击跳转至对应详情页面。
月度趋势区域使用折线图展示最近6个月的活动趋势,包括:活动场次、参与人次、消耗羽毛球数、账单金额。图表支持鼠标悬停显示详细数据点。
消耗分析区域使用饼图展示耗材消耗占比,包括:羽毛球消耗占比、其他消耗占比。下方配套柱状图展示各类耗材的月度消耗趋势。
页面侧边提供快捷操作区,包括:今日签到入口、快速建活动入口、快速采购入口、最近活动列表。
组件名称:ShuttlecockSelector
功能说明:专门用于选择羽毛球消耗数量,与采购模块联动计算费用。
组件属性包括:modelValue(绑定值,消耗数量)、disabled(禁用状态)、showCalculate(是否显示计算结果)。组件事件包括:update:modelValue(值变化事件)、calculate(计算完成事件)。
组件内部逻辑:输入数量后,自动调用采购模块接口获取羽毛球平均单价,计算消耗金额并显示在下方。如果库存不足,显示警告提示。
组件名称:CostSplitter
功能说明:根据活动总费用和参与人数,自动计算每人分摊金额。
组件属性包括:totalCost(总费用)、participants(参与人员数组)、excludeUsers(排除的用户ID数组)。组件计算属性包括:costPerPerson(人均费用)、userAmounts(每个用户的分摊金额)。
组件支持两种分摊模式:平均分摊(全员平摊总费用)和自定义分摊(可手动调整每个用户的分摊金额)。
组件名称:StockWarning
功能说明:显示库存不足的耗材预警信息,支持一键采购。
组件属性包括:threshold(预警阈值,默认为20)。组件展示库存低于阈值的耗材列表,每条显示商品名称、当前库存和预警阈值。提供"立即采购"快捷按钮。
组件名称:UserSelector
功能说明:用于选择参与活动的用户,支持多选和搜索。
组件属性包括:modelValue(绑定值,选中的用户ID数组)、multiple(是否多选,默认为true)、placeholder(占位文本)。组件基于Element Plus的Select组件扩展,增加用户头像显示和搜索功能。
组件名称:BillStatusTag
功能说明:根据账单状态显示对应的标签样式。
组件属性包括:status(账单状态,0未付款/1部分付款/2已结清)、balance(待付金额)。组件根据状态显示不同颜色的Tag,并显示对应的状态文本。
用户签到是整个系统的入口业务,流程设计如下:
第一步,用户点击签到按钮或扫描二维码发起签到请求。系统首先验证用户身份,获取当前登录用户信息。如果用户未登录,提示用户先登录。
第二步,系统查询当日是否已存在活动记录。活动记录表以活动日期为唯一索引,支持快速查询。如果当日不存在活动记录,系统自动创建一条新的活动记录,初始状态为"草稿",参与者列表为空。
第三步,系统创建签到记录,绑定用户ID、签到日期、签到时间。签到时间精确到秒,使用服务器时间确保一致性。
第四步,将签到记录与活动记录关联。如果活动记录的参与者列表中不存在该用户,自动添加到参与者列表中。
第五步,返回签到结果,更新前端界面。如果签到成功,显示成功提示并刷新当日签到列表;如果签到失败,显示错误信息。
系统需要防止用户在同一日内重复签到。判断逻辑为:查询当日的签到记录表中是否存在该用户的签到记录。如果存在且状态为"已签到",提示"您今日已签到,无需重复签到";如果存在且状态为"缺席"或"请假",更新状态为"已签到"并记录签到时间。
签到统计需要计算以下指标:
签到率等于已签到人数除以参与活动人数再乘以100%。参与活动人数为活动记录的participant_count字段值。
出勤率等于实际出勤人数除以报名人数再乘以100%。实际出勤人数为签到状态为"已签到"的用户数量。
连续签到天数需要查询用户最近N天的签到记录,计算连续签到的天数。SQL实现使用自连接或窗口函数实现。
创建活动记录时,系统执行以下逻辑:
首先,验证必填字段。活动日期不能为空且不能是未来日期之前,活动标题不能超过100个字符。
其次,初始化关联字段。参与人数初始化为0,羽毛球消耗初始化为0,各项费用初始化为0.00,状态初始化为"草稿"。
第三,创建空的参与者列表。用户可以在活动创建后手动添加参与者,也可以由签到操作自动填充。
第四,生成活动编号。编号格式为"HD+日期+序号",例如"HD20240115001"。
这是系统的核心业务逻辑之一,支持从多个批次核算羽毛球成本。一次活动可以从不同批次消耗羽毛球,满足不同品质羽毛球混用需求。
首先,获取可用批次列表。系统查询羽毛球批次库存表中状态为"有库存"(status=1)的批次记录,按采购日期升序排列。返回的批次信息包括:批次号、商品名称、单价、剩余数量、状态。
其次,用户选择批次(支持多选)。前端页面展示所有可用批次,用户可以多选多个批次进行羽毛球消耗登记。每个批次卡片包含:批次号、商品名称、单价、剩余数量、消耗数量输入框、选择复选框。
第三,输入各批次消耗数量并计算费用。用户为每个选中的批次输入消耗数量,系统自动计算各批次的费用:单批次费用等于消耗数量乘以该批次单价。羽毛球总费用等于各批次费用之和。计算结果四舍五入到分,保留两位小数。
第四,库存检查与预警。系统检查每个选中批次的剩余数量是否充足:如果某批次消耗数量大于剩余数量,该批次数量输入框边框变红显示警告,用户需调整数量或取消选择该批次。
第五,批次扣减逻辑。保存活动记录时,系统自动执行以下操作:
第六,费用汇总。更新活动记录的总费用字段,总费用等于各批次羽毛球费用之和加上其他费用。更新人均费用字段,人均费用等于总费用除以参与人数(参与人数大于0时)。
在新建或编辑活动记录时,用户选择羽毛球批次的交互流程如下:
第一步,展开批次选择区域。用户点击"选择羽毛球批次(可多选)"区域,显示可用批次列表。
第二步,显示批次列表。系统调用接口GET /badminton/procurement/batch/list获取所有有库存的羽毛球批次,前端以卡片列表形式展示,每个卡片显示:复选框、批次号、商品名称、单价、剩余数量、状态标签、消耗数量输入框。
第三步,选择批次(多选)。用户点击批次卡片选中该批次(复选框变为勾选状态),可以同时选中多个批次。再次点击可取消选中。
第四步,输入各批次消耗数量。用户在每个选中批次的数量输入框中输入本次活动从该批次消耗的羽毛球数量。数量输入框仅在选中批次后可用。
第五步,实时预览费用和汇总。前端实时计算并显示:
第六步,库存检查。前端实时检查各批次的剩余数量:
第七步,保存活动。用户点击保存按钮时,系统执行以下操作:
活动保存时,羽毛球消耗数据以数组形式提交:
{
"activityId": 501,
"shuttlecockUsage": [
{
"batchId": 1,
"batchNo": "PC-20240110-001",
"usageQuantity": 4,
"unitPrice": 15.00,
"totalCost": 60.00
},
{
"batchId": 2,
"batchNo": "PC-20240110-002",
"usageQuantity": 2,
"unitPrice": 16.00,
"totalCost": 32.00
}
],
"totalShuttlecockCost": 92.00
}
后端接收数据后,遍历shuttlecockUsage数组,为每条记录执行库存扣减和明细创建操作。
参与人员管理支持以下操作:
添加参与者时,需要验证用户ID的有效性和用户状态。添加后更新活动的参与人数字段。如果用户当日已签到,自动关联签到记录。
移除参与者时,需要确认操作。如果该用户已付款,需要先处理退款或标记为特殊处理。移除后更新活动的参与人数字段和费用分摊。
标记付款时,更新参与人员的is_paid字段为true,记录pay_time。同时更新账单的已付金额和待付金额。如果待付金额为0,更新账单状态为"已结清"。
创建采购记录时,系统执行以下逻辑:
首先,验证必填字段。采购日期、商品名称、数量、单价均为必填。数量必须大于0,单价必须大于等于0。
其次,自动生成批次号。批次号格式为"PC+YYYYMMDD+序号",例如"PC-20240110-001"。序号为当日采购序号,从001开始递增。
第三,计算采购金额。金额等于数量乘以单价,保留两位小数。
第四,生成采购记录。保存采购信息到采购记录表,初始状态为"未入库"。
入库确认是采购流程的关键步骤,执行以下逻辑:
首先,验证采购记录状态。只有状态为"未入库"的记录可以执行入库确认。
其次,创建羽毛球批次记录。入库确认时,系统自动在羽毛球批次库存表中创建一条批次记录:
第三,更新采购记录状态。将状态更新为"已入库",记录入库时间。
第四,生成库存变动日志。记录库存变动的详细信息,包括变动前数量、变动数量、变动后数量、操作人、操作时间。
批次库存管理包括以下功能:
首先,批次列表查询。查询羽毛球批次库存表,返回所有批次的库存信息,支持按分类、状态筛选,支持按采购日期排序或按剩余数量排序。
其次,批次消耗扣减。当活动记录消耗羽毛球时,系统自动扣减对应批次的剩余数量:更新羽毛球批次库存表中的remaining_quantity字段;更新used_quantity字段等于total_quantity减去remaining_quantity;根据剩余数量更新批次状态。
第三,批次状态自动更新。系统定时检查各批次的剩余数量,自动更新状态:当remaining_quantity等于0时,status更新为0(已用完);当remaining_quantity小于预警阈值时,status更新为2(预警)。
第四,批次消耗历史追溯。支持查询每个批次的消耗明细,记录每次消耗的活动信息、消耗数量、消耗金额。消耗历史用于成本分析和审计追溯。
系统定时检查库存数量,触发预警的逻辑如下:
首先,设置预警阈值。每个耗材类别可以设置独立的预警阈值,羽毛球类默认阈值为20个。
其次,每日定时任务检查库存。查询库存数量低于预警阈值的耗材记录。
第三,生成预警通知。向管理员发送系统通知或邮件通知,告知需要采购的耗材明细。
第四,前端实时显示预警。库存管理页面侧边栏实时显示预警列表,红色高亮标识。
账单生成是系统自动化的核心功能,执行以下逻辑:
首先,确定账单周期。账单周期为自然月,例如2024年1月的账单周期为2024-01-01至2024-01-31。
其次,汇总活动记录。查询账单周期内的所有活动记录,筛选状态为"已完成"的记录。
第三,计算用户分摊金额。对于每个用户,汇总其参与的所有活动的分摊金额。分摊金额等于活动总费用除以参与人数。如果用户中途加入或离开,按实际参与天数比例分摊。
第四,创建账单记录。为每个有消费的用户创建账单记录,设置账单月份、用户ID、总金额、已付金额(默认为0)、待付金额(等于总金额)、状态(默认为"未付款")、付款截止日期(账单生成后15天)。
第五,创建账单明细。为每条账单创建明细记录,关联具体活动、金额和付款状态。
费用分摊支持多种模式:
平均分摊模式下,总费用除以参与人数,结果四舍五入到分。由于四舍五入可能导致分摊总额与总费用有微小差异,最后一个人的金额取总费用减去其他人的金额之和。
阶梯分摊模式下,根据参与时长分摊。活动中途加入的用户按实际参与时长占活动总时长的比例分摊费用。
混合分摊模式下,基本费用(如场地费)按人头平均分摊,耗材费用(如羽毛球)按实际消耗分摊。
系统默认使用平均分摊模式,支持在活动记录中手动调整每个用户的分摊金额。
用户付款时,执行以下逻辑:
首先,验证付款金额。付款金额不能超过待付金额。
其次,更新账单明细。找到对应的活动明细记录,更新is_paid字段和pay_time。
第三,更新账单汇总。累加已付金额,重新计算待付金额。如果待付金额为0,更新账单状态为"已结清";如果已付金额大于0且待付金额大于0,更新账单状态为"部分付款"。
第四,记录付款日志。记录付款详情,包括付款时间、付款金额、付款方式、操作人。
第五,发送通知。付款成功后向用户发送通知,告知付款成功和剩余待付金额。
对于逾期未付款的账单,系统执行以下催缴逻辑:
首先,设置付款截止日期。默认为账单生成后15天。
其次,账单生成时发送通知。用户收到账单生成通知,包含账单明细和付款链接。
第三,截止日期前3天发送提醒。提醒用户尽快付款。
第四,截止日期当天发送最后提醒。
第五,逾期后每天发送催款通知。逾期超过7天,升级为管理员介入处理。
系统需要配置以下定时任务:
执行时间:每日23:59:00
任务内容:汇总当日的签到数据和活动数据,生成日报表。统计项目包括:签到人数、参与活动人数、羽毛球消耗量、总费用等。
执行时间:每日08:00:00
任务内容:检查所有耗材的库存数量,生成预警清单,发送通知给管理员。
执行时间:每月1日00:10:00
任务内容:自动生成上月的账单记录。具体步骤为:获取上月活动记录汇总,计算每个用户的分摊金额,创建账单主表和明细表记录。
执行时间:每日09:00:00
任务内容:检查逾期未付款的账单,发送催款通知。规则为:截止日期前3天发送提醒,截止日期当天发送最后提醒,逾期后每天发送催款通知。
系统定义以下角色和权限:
普通用户角色可以执行的操作包括:签到、查看自己的活动记录、查看自己的账单、付款、编辑活动记录(所有用户均可编辑活动记录,不限制权限)。
管理员角色在普通用户权限基础上,额外可以执行的操作包括:采购管理、入库确认、库存管理、生成账单、编辑所有活动记录、删除活动记录、查看所有账单、调整账单。
超级管理员角色在管理员权限基础上,额外可以执行的操作包括:用户管理、角色管理、系统配置、查看操作日志。
数据权限控制规则如下:
普通用户只能查看自己的签到记录、账单和活动记录。管理员可以查看所有数据。数据权限通过MyBatis的SQL过滤实现,在查询时动态添加用户ID条件。
前端使用自定义指令v-permission控制按钮的显示和隐藏。例如:<el-button v-permission="['procurement:add']">采购</el-button>。
权限定义采用"模块:操作"的格式,如"procurement:add"表示采购模块的添加权限。
系统采用JWT(JSON Web Token)进行身份认证。用户登录成功后,服务端生成JWT令牌返回给客户端。客户端在后续请求中通过Authorization请求头携带令牌。服务端验证令牌的有效性,确认用户身份后处理业务请求。
JWT令牌包含用户ID、角色信息、令牌过期时间等字段。令牌有效期设置为8小时,与工作日时长匹配。过期后用户需要重新登录。
服务端在处理请求前,首先校验用户是否具有该接口的访问权限。权限校验流程如下:
第一步,从请求头中获取JWT令牌,解析出用户信息。
第二步,查询用户的角色和权限列表。
第三步,检查用户权限是否包含请求接口所需的权限。
第四步,如果权限校验通过,继续处理业务逻辑;否则返回403无权限错误。
服务端对所有输入数据进行严格校验:
参数校验使用JSR-303注解实现,如@NotNull、@Size、@Min等。校验失败时返回400错误,附带详细的错误信息。
SQL注入防护使用参数化查询,禁止拼接SQL语句。
XSS防护对用户输入的富文本内容进行过滤,转义特殊字符。
系统记录完整的操作日志,包括:操作时间、操作人、操作类型、操作内容、请求参数、响应结果。日志保存期限为180天,支持按时间范围和操作人查询。
前端构建命令如下:
# 安装依赖
yarn install --registry=https://registry.npmmirror.com
# 开发环境启动
yarn dev
# 构建生产环境
yarn build:prod
# 构建测试环境
yarn build:stage
生产环境构建产物位于dist目录,部署到Nginx或Apache服务器。Nginx配置示例:
server {
listen 80;
server_name badminton.example.com;
root /var/www/badminton/dist;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /dev-api/ {
proxy_pass http://localhost:8081/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
后端Spring Boot应用打包为JAR文件,部署命令如下:
# 打包
mvn clean package -DskipTests
# 运行
java -jar badminton-admin.jar --spring.profiles.active=prod
MySQL数据库部署要求:版本8.0以上,字符集设置为utf8mb4,排序规则设置为utf8mb4_unicode_ci。创建数据库和用户后,执行初始化SQL脚本创建数据表和初始数据。
| 分类编码 | 分类名称 | 默认预警阈值 |
|---|---|---|
| YQ | 羽毛球 | 20个 |
| QP | 球拍 | 0 |
| SG | 手胶 | 50个 |
| YP | 饮料 | 10瓶 |
| ZC | 场地 | 0 |
| QT | 其他 | 10 |
签到状态码:0表示缺席,1表示已签到,2表示请假。
采购状态码:0表示未入库,1表示已入库,2表示已用完。
账单状态码:0表示未付款,1表示部分付款,2表示已结清。
活动状态码:0表示草稿,1表示已完成。
创建羽毛球平均单价视图:
CREATE VIEW v_shuttlecock_avg_price AS
SELECT
AVG(unit_price) AS avg_price,
SUM(quantity) AS total_quantity
FROM badminton_procurement
WHERE category_id = (SELECT id FROM badminton_consumable_category WHERE category_name = '羽毛球')
AND status = 1
ORDER BY procurement_date DESC
LIMIT 10;
账单生成存储过程:
DELIMITER $
CREATE PROCEDURE sp_generate_bill(IN bill_month VARCHAR(7))
BEGIN
-- 存储过程实现逻辑
END$
DELIMITER ;
| 版本 | 日期 | 修订人 | 修订内容 |
|---|---|---|---|
| 1.0 | 2024-01-15 | - | 初稿完成 |