diff --git a/README.md b/README.md index cf1df38433b2972fa5dcc5757bf050ef19221a2e..f17ee25bff409583c34dd7e5463ef49224fc4dcf 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,354 @@ -# 24计算机-分组项目 +# 需求分析说明书 -#### 介绍 -24计算机-分组项目 -第一组:学生成绩管理系统 -第二组:学生考勤系统 -第三组:模拟贷款系统 -第四组:c语言图书管理系统 -第五组:通讯录管理系统 -第六组:校园图书管理系统设计与实现 -第七组:大学生信息管理系统 -第八组:仓库管理系统 -第九组:基于分块链表的通讯录管理系统 -第十组:校园导游系统 -第十一组:简单版的个人账务管理系统 +**系统名称**:高校宿舍管理系统 -#### 软件架构 -软件架构说明 +**姓  名**:__________ +**学  号**:__________ -#### 安装教程 +--- -1. xxxx -2. xxxx -3. xxxx +## 一、总体需求描述 -#### 使用说明 +### 1.1 文字说明 -1. xxxx -2. xxxx -3. xxxx +高校宿舍管理是学校后勤保障的重要环节,涉及学生入住、退宿、换宿、宿舍楼与房间维护、报修处理、水电费缴费、宿管人员调度等多类业务。传统的人工登记方式面临以下问题: -#### 参与贡献 +- **床位状态滞后**:纸质或 Excel 登记无法实时反映床位空闲、占用、维修等状态,导致新生入住安排混乱。 +- **报修流程不透明**:学生提交报修后缺少统一记录,宿管无法跟踪维修进度,后勤难以统计未完成任务。 +- **缴费记录分散**:各宿舍每月水电费数据单独记录,缺乏统一的查询与统计渠道,容易出现漏缴或数据错误。 +- **信息查询效率低**:查找某学生住在哪里、某宿舍有多少空位,需要翻阅多份表格,耗时且易出错。 -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request +为此,设计一套**高校宿舍管理系统**,以数据库为核心,统一存储和管理学生信息、宿舍资源(楼栋、房间、床位)、入住记录、报修记录和缴费记录,支撑日常宿舍管理业务的数字化运行。 +### 1.2 系统目标 -#### 特技 +1. 统一管理学生基本信息(学号、姓名、学院、专业、班级、联系方式)。 +2. 维护宿舍资源信息(楼栋、房间、床位及床位状态)。 +3. 支撑入住、退宿和换宿等核心住宿业务,保证床位状态与入住记录的一致性。 +4. 提供报修申请与处理跟踪功能。 +5. 按宿舍和月份记录水电费缴费情况,自动计算费用总额。 +6. 提供面向宿管、学生和后勤管理人员的常用查询与统计功能。 +7. 通过视图、索引和存储过程等数据库对象提升查询效率和可维护性。 -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) +### 1.3 用户角色 + +| 角色 | 主要职责 | +|---|---| +| 宿舍管理员 | 管理楼栋、宿舍、床位信息;办理入住、退宿、换宿;维护报修和缴费记录 | +| 学生 | 查询本人住宿信息;提交报修申请;查看缴费情况 | +| 后勤管理人员 | 查看报修统计、缴费统计;掌握宿舍整体使用情况 | + +### 1.4 业务流程概述 + +``` +学生入学 → 宿管分配床位 → 创建入住记录 → 床位状态更新为"已入住" + ↓ +学生入住期间 → 提交报修(可选)→ 后勤处理维修 → 更新报修状态 + ↓ +每月 → 生成宿舍缴费记录 → 学生缴费 → 更新缴费状态 + ↓ +学生毕业/换宿 → 办理退宿 → 释放床位 → 入住记录归档 +``` + +--- + +## 二、功能需求 + +### 2.1 功能模块图 + +```mermaid +graph TD + A[高校宿舍管理系统] --> B[学生信息管理] + A --> C[宿舍资源管理] + A --> D[入住管理] + A --> E[报修管理] + A --> F[缴费管理] + A --> G[查询统计] + + B --> B1[录入学生信息] + B --> B2[修改学生信息] + B --> B3[查询学生信息] + + C --> C1[维护楼栋信息] + C --> C2[维护宿舍房间] + C --> C3[维护床位状态] + + D --> D1[办理入住] + D --> D2[办理退宿] + D --> D3[办理换宿] + + E --> E1[提交报修申请] + E --> E2[更新报修状态] + E --> E3[查看报修记录] + + F --> F1[录入缴费记录] + F --> F2[更新缴费状态] + F --> F3[查询缴费明细] + + G --> G1[空床位查询] + G --> G2[住宿信息查询] + G --> G3[报修统计] + G --> G4[缴费统计] +``` + +### 2.2 功能详细说明 + +| 功能模块 | 功能项 | 功能描述 | +|---|---|---| +| 学生信息管理 | 录入/修改/查询 | 维护学号、姓名、性别、学院、专业、班级、联系电话等基本信息 | +| 宿舍资源管理 | 楼栋维护 | 管理宿舍楼名称、楼层数、性别限制、地址等 | +| 宿舍资源管理 | 房间维护 | 管理每个楼栋下的宿舍房间号、楼层、容量 | +| 宿舍资源管理 | 床位维护 | 管理每个房间的床位编号及状态(空闲/已入住/维修中) | +| 入住管理 | 办理入住 | 为学生分配空闲床位,创建入住记录,更新床位状态 | +| 入住管理 | 办理退宿 | 结束入住记录,释放床位,恢复床位为空闲 | +| 入住管理 | 办理换宿 | 同时完成退宿原床位和入住新床位的操作(事务性操作) | +| 报修管理 | 提交报修 | 学生提交维修内容,记录报修时间和宿舍 | +| 报修管理 | 处理报修 | 更新报修状态(待处理→处理中→已完成),记录处理人和完成时间 | +| 缴费管理 | 费用录入 | 按宿舍和月份录入水费、电费 | +| 缴费管理 | 缴费确认 | 更新缴费状态为已缴费,记录缴费日期 | +| 查询统计 | 空床位查询 | 按楼栋查询空闲床位,用于安排新生入住 | +| 查询统计 | 住宿查询 | 按学生姓名查询其所在楼栋、房间和床位 | +| 查询统计 | 入住统计 | 按宿舍统计当前入住人数和剩余床位数 | +| 查询统计 | 报修统计 | 按报修状态统计各类报修数量 | +| 查询统计 | 缴费统计 | 按月份查询未缴费宿舍清单 | + +--- + +## 三、数据流图 + +### 3.1 第 0 层数据流图(顶层图 / 上下文图) + +第 0 层将整个系统抽象为一个整体处理过程,展示系统与外部实体之间的数据交互。 + +```mermaid +graph TD + %% 外部实体 + S[/学生\] + M[/宿舍管理员\] + L[/后勤管理人员\] + + %% 主处理 + P[高校宿舍管理系统
P0] + + %% 学生 → 系统 + S -->|入住申请、退宿申请、换宿申请| P + S -->|报修申请、报修内容| P + S -->|缴费查询请求| P + + %% 系统 → 学生 + P -->|住宿信息、分配床位| S + P -->|报修处理结果| S + P -->|缴费明细、缴费状态| S + + %% 宿管 → 系统 + M -->|学生信息、楼栋信息、房间信息| P + M -->|床位分配指令| P + M -->|缴费记录录入| P + + %% 系统 → 宿管 + P -->|空床位列表、入住统计| M + P -->|缴费状态汇总| M + P -->|学生住宿清单| M + + %% 后勤 → 系统 + L -->|报修处理结果、维修记录| P + + %% 系统 → 后勤 + P -->|报修统计报表| L + P -->|缴费统计报表| L + P -->|宿舍使用率统计| L +``` + +### 3.2 第 1 层数据流图(详细层) + +第 1 层将顶层的主处理拆分为 6 个子处理过程,展示各子过程之间及其与数据存储之间的数据流。 + +```mermaid +graph TD + %% 外部实体 + S[/学生\] + M[/宿舍管理员\] + L[/后勤管理人员\] + + %% 子处理过程 + P1[P1
学生信息管理] + P2[P2
宿舍资源管理] + P3[P3
入住管理] + P4[P4
报修管理] + P5[P5
缴费管理] + P6[P6
查询统计] + + %% 数据存储 + D1[(D1 学生表
Student)] + D2[(D2 宿舍楼表
Building)] + D3[(D3 宿舍表
Dormitory)] + D4[(D4 床位表
Bed)] + D5[(D5 入住记录表
CheckIn)] + D6[(D6 报修记录表
Repair)] + D7[(D7 缴费记录表
Payment)] + D8[(D8 宿管表
Manager)] + + %% 宿管 → P1 + M -->|学生基本信息| P1 + P1 -->|写入/更新| D1 + + %% 宿管 → P2 + M -->|楼栋/宿舍/床位信息| P2 + P2 -->|写入/更新| D2 + P2 -->|写入/更新| D3 + P2 -->|写入/更新| D4 + M -->|宿管信息| P2 + P2 -->|写入/更新| D8 + + %% 学生 → P3 & P4 + S -->|入住/退宿/换宿申请| P3 + P3 -->|读取空闲床位| D4 + P3 -->|写入/更新入住记录| D5 + P3 -->|更新床位状态| D4 + P3 -->|读取学生信息| D1 + + S -->|报修申请| P4 + P4 -->|写入报修记录| D6 + P4 -->|读取学生信息| D1 + P4 -->|读取宿舍信息| D3 + + %% 后勤 → P4 + L -->|报修处理信息| P4 + P4 -->|更新报修状态| D6 + + %% 宿管 → P5 + M -->|缴费数据录入| P5 + P5 -->|写入/更新缴费记录| D7 + P5 -->|读取宿舍信息| D3 + + %% 查询 P6 + S -->|查询请求| P6 + M -->|统计请求| P6 + L -->|统计请求| P6 + + P6 -->|读取| D1 + P6 -->|读取| D2 + P6 -->|读取| D3 + P6 -->|读取| D4 + P6 -->|读取| D5 + P6 -->|读取| D6 + P6 -->|读取| D7 + + P6 -->|查询/统计结果| S + P6 -->|查询/统计结果| M + P6 -->|查询/统计结果| L +``` + +--- + +## 四、数据字典 + +### 4.1 数据项 + +数据项是系统中不可再分的最小数据单位。 + +| 数据项编号 | 数据项名称 | 别名 | 类型与长度 | 取值范围 | 说明 | +|---|---|---|---|---|---| +| DI-001 | student_id | 学生编号 | INT | 自增序列 | 主键,系统内部唯一标识 | +| DI-002 | student_no | 学号 | VARCHAR(20) | 如 "202401001" | 唯一,学生业务标识 | +| DI-003 | name | 学生姓名 | VARCHAR(20) | 中文字符 | 不能为空 | +| DI-004 | gender | 性别 | ENUM | '男'、'女' | 不能为空 | +| DI-005 | college | 学院 | VARCHAR(50) | 如 "计算机学院" | 不能为空 | +| DI-006 | major | 专业 | VARCHAR(50) | 如 "软件工程" | 不能为空 | +| DI-007 | class_name | 班级 | VARCHAR(30) | 如 "软工2401" | 不能为空 | +| DI-008 | phone | 联系电话 | VARCHAR(20) | 11位手机号 | 可为空 | +| DI-009 | building_id | 楼栋编号 | INT | 自增序列 | 主键 | +| DI-010 | building_name | 楼栋名称 | VARCHAR(30) | 如 "一号楼" | 唯一,不能为空 | +| DI-011 | floors | 楼层数 | INT | ≥1 | 检查约束 | +| DI-012 | gender_limit | 性别限制 | ENUM | '男'、'女' | 该楼栋允许住宿的性别 | +| DI-013 | address | 地址 | VARCHAR(100) | 如 "东区生活园A区" | 可为空 | +| DI-014 | dormitory_id | 宿舍编号 | INT | 自增序列 | 主键 | +| DI-015 | room_no | 房间号 | VARCHAR(10) | 如 "101" | 同一楼栋内唯一 | +| DI-016 | floor_no | 楼层 | INT | ≥1 | 检查约束 | +| DI-017 | capacity | 容量 | INT | ≥1 | 该宿舍最大床位数 | +| DI-018 | bed_id | 床位编号 | INT | 自增序列 | 主键 | +| DI-019 | bed_no | 床位号 | VARCHAR(10) | 如 "1号床" | 同一宿舍内唯一 | +| DI-020 | bed_status | 床位状态 | ENUM | '空闲'、'已入住'、'维修中' | 默认'空闲' | +| DI-021 | checkin_id | 入住记录编号 | INT | 自增序列 | 主键 | +| DI-022 | checkin_date | 入住日期 | DATE | 如 "2025-09-01" | 不能为空 | +| DI-023 | checkout_date | 退宿日期 | DATE | 如 "2026-07-01" | 在住时为空 | +| DI-024 | checkin_status | 入住状态 | ENUM | '在住'、'已退宿' | 默认'在住' | +| DI-025 | repair_id | 报修编号 | INT | 自增序列 | 主键 | +| DI-026 | repair_content | 报修内容 | VARCHAR(200) | 描述故障情况 | 不能为空 | +| DI-027 | repair_status | 报修状态 | ENUM | '待处理'、'处理中'、'已完成' | 默认'待处理' | +| DI-028 | report_time | 报修时间 | DATETIME | 如 "2026-05-08 09:30:00" | 默认当前时间 | +| DI-029 | finish_time | 完成时间 | DATETIME | 完成时填写 | 可为空 | +| DI-030 | handler | 处理人 | VARCHAR(20) | 如 "维修员A" | 可为空 | +| DI-031 | payment_id | 缴费编号 | INT | 自增序列 | 主键 | +| DI-032 | fee_month | 费用月份 | CHAR(7) | 如 "2026-05" | 同一宿舍每月唯一 | +| DI-033 | water_fee | 水费 | DECIMAL(8,2) | ≥0 | 默认 0 | +| DI-034 | electricity_fee | 电费 | DECIMAL(8,2) | ≥0 | 默认 0 | +| DI-035 | total_amount | 总金额 | DECIMAL(8,2) | = 水费 + 电费 | 自动计算(生成列) | +| DI-036 | payment_status | 缴费状态 | ENUM | '未缴费'、'已缴费' | 默认'未缴费' | +| DI-037 | pay_date | 缴费日期 | DATE | 如 "2026-05-03" | 未缴费时为空 | +| DI-038 | manager_id | 宿管编号 | INT | 自增序列 | 主键 | +| DI-039 | manager_name | 宿管姓名 | VARCHAR(20) | 中文字符 | 不能为空 | + +### 4.2 数据结构 + +数据结构描述了数据项之间的组合关系,对应数据库中的表结构。 + +| 数据结构编号 | 数据结构名称 | 组成数据项 | 对应表名 | +|---|---|---|---| +| DS-01 | 学生信息 | student_id + student_no + name + gender + college + major + class_name + phone | Student | +| DS-02 | 宿管信息 | manager_id + manager_name + phone | Manager | +| DS-03 | 宿舍楼信息 | building_id + building_name + floors + gender_limit + address + manager_id | Building | +| DS-04 | 宿舍信息 | dormitory_id + building_id + room_no + floor_no + capacity | Dormitory | +| DS-05 | 床位信息 | bed_id + dormitory_id + bed_no + bed_status | Bed | +| DS-06 | 入住记录 | checkin_id + student_id + bed_id + checkin_date + checkout_date + status | CheckIn | +| DS-07 | 报修记录 | repair_id + student_id + dormitory_id + repair_content + repair_status + report_time + finish_time + handler | Repair | +| DS-08 | 缴费记录 | payment_id + dormitory_id + fee_month + water_fee + electricity_fee + total_amount + payment_status + pay_date | Payment | + +### 4.3 数据处理 + +数据处理描述系统中对数据的各类操作。 + +| 处理编号 | 处理名称 | 输入数据 | 输出数据 | 处理逻辑 | 涉及数据存储 | +|---|---|---|---|---|---| +| DP-01 | 录入学生信息 | 学号、姓名、性别、学院、专业、班级、电话 | 新增学生记录 | INSERT INTO student | Student | +| DP-02 | 修改学生信息 | student_id + 需修改的字段 | 更新后的学生记录 | UPDATE student SET ... | Student | +| DP-03 | 维护楼栋信息 | 楼栋名称、楼层数、性别限制、地址、宿管ID | 新增/更新楼栋记录 | INSERT/UPDATE building | Building | +| DP-04 | 维护宿舍信息 | 楼栋ID、房间号、楼层、容量 | 新增/更新宿舍记录 | INSERT/UPDATE dormitory | Dormitory | +| DP-05 | 维护床位信息 | 宿舍ID、床位号 | 新增/更新床位记录 | INSERT/UPDATE bed | Bed | +| DP-06 | 办理入住 | student_id + bed_id + checkin_date | 新增入住记录 + 更新床位状态为"已入住" | INSERT check_in + UPDATE bed SET bed_status='已入住' | CheckIn, Bed | +| DP-07 | 办理退宿 | student_id | 更新入住记录状态 + 更新床位状态为"空闲" | UPDATE check_in SET status='已退宿', checkout_date=今天 + UPDATE bed SET bed_status='空闲' | CheckIn, Bed | +| DP-08 | 办理换宿 | student_no + new_bed_id | 退宿旧床位 + 入住新床位 | 事务操作:退宿→释放旧床→入住新床→占用新床 | CheckIn, Bed | +| DP-09 | 提交报修 | student_id + dormitory_id + repair_content | 新增报修记录 | INSERT INTO repair (status='待处理') | Repair | +| DP-10 | 处理报修 | repair_id + handler | 更新报修状态 | UPDATE repair SET status='已完成', finish_time=NOW() | Repair | +| DP-11 | 录入缴费 | dormitory_id + fee_month + water_fee + electricity_fee | 新增缴费记录(total_amount 自动计算) | INSERT INTO payment | Payment | +| DP-12 | 确认缴费 | payment_id + pay_date | 更新缴费状态 | UPDATE payment SET status='已缴费', pay_date=日期 | Payment | +| DP-13 | 删除错误缴费 | payment_id | 删除缴费记录(仅限未缴费) | DELETE FROM payment WHERE status='未缴费' | Payment | +| DP-14 | 查询空床位 | building_name | 空床位列表 | SELECT ... JOIN ... WHERE bed_status='空闲' | Bed, Dormitory, Building | +| DP-15 | 按姓名查住宿 | student_name | 住宿信息(楼栋、房间、床位) | SELECT ... JOIN 5张表 WHERE name=姓名 AND status='在住' | Student, CheckIn, Bed, Dormitory, Building | +| DP-16 | 宿舍入住统计 | 无(视图) | 各宿舍入住人数与空床位数 | CREATE VIEW v_dormitory_occupancy | Dormitory, Bed, CheckIn | +| DP-17 | 查未缴费宿舍 | fee_month | 未缴费宿舍清单 | SELECT ... WHERE fee_month=月份 AND status='未缴费' | Payment, Dormitory, Building | +| DP-18 | 报修状态统计 | 无 | 各状态报修数量 | SELECT ... GROUP BY repair_status | Repair | +| DP-19 | 按学号查缴费 | student_no | 该学生所在宿舍缴费历史 | 存储过程 sp_query_student_payment | Student, CheckIn, Bed, Dormitory, Payment | + +### 4.4 数据存储总览 + +| 存储编号 | 存储名称 | 对应表名 | 主键 | 记录数(初始) | 增长频率 | +|---|---|---|---|---|---| +| S-01 | 学生信息 | Student | student_id | 6条 | 每年新生入学时批量新增 | +| S-02 | 宿管信息 | Manager | manager_id | 2条 | 偶有新增或变更 | +| S-03 | 宿舍楼信息 | Building | building_id | 2条 | 新建楼栋时新增 | +| S-04 | 宿舍信息 | Dormitory | dormitory_id | 6条 | 新建宿舍时新增 | +| S-05 | 床位信息 | Bed | bed_id | 24条 | 新增宿舍时批量新增 | +| S-06 | 入住记录 | CheckIn | checkin_id | 6条 | 每学期初集中新增 | +| S-07 | 报修记录 | Repair | repair_id | 3条 | 按报修频率日常新增 | +| S-08 | 缴费记录 | Payment | payment_id | 6条 | 每月每宿舍新增一条 | + +--- + +## 附录:数据库实施参考 + +详见同目录下文件: +- `高校宿舍管理系统_mysql.sql` — 完整建库、建表、初始化数据、视图、索引、存储过程脚本 +- `高校宿舍管理系统_大作业说明书.md` — 概念结构设计(ER 图)与逻辑结构设计 +- `高校宿舍管理系统_汇报PPT.pptx` — 12 页汇报演示文稿 diff --git "a/\345\255\246\347\224\237\350\200\203\345\213\244\347\263\273\347\273\237/.keep" "b/\345\255\246\347\224\237\350\200\203\345\213\244\347\263\273\347\273\237/.keep" deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git "a/\345\255\246\347\224\237\350\200\203\345\213\244\347\263\273\347\273\237/attendance.c" "b/\345\255\246\347\224\237\350\200\203\345\213\244\347\263\273\347\273\237/attendance.c" deleted file mode 100644 index 44095a1e749876b31b5f538a0fe5140d566b2725..0000000000000000000000000000000000000000 --- "a/\345\255\246\347\224\237\350\200\203\345\213\244\347\263\273\347\273\237/attendance.c" +++ /dev/null @@ -1,155 +0,0 @@ -#include //printfscanfȺ -#include //mallocڴ亯 -#include //ַ -#include // ڿ̨ɫ - -#include "attendance.h" - -// ========== ȫֱ ========== -Student *studentHead = NULL; //ѧͷָ룬ڹѧϢ -ClassRecord *recordHead = NULL; //忼ڼ¼ͷָ룬ڹڼ¼ - -// ========== ̨ɫ ========== -void setColor(int color) { //̨ɫIJ - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color); //Windows APIÿ̨ıɫ -} - -// ========== ѧ ========== -void addStudent() { - Student *p = (Student*)malloc(sizeof(Student)); //ѧڵ - printf(" ѧ ༶"); //ʾûѧѧţ༶Ϣ - scanf("%s %s %s", p->id, p->name, p->className); //ȡûѧš༶Ϣѧڵ - p->next = studentHead; //½ڵnextָָԭͷʵͷ巨 - studentHead = p; //ͷָָ½ڵ㣬½ڵ - setColor(10); - printf(" ѧӳɹ\n"); //ӡѧӳɹʾϢ - setColor(7); -} - -void showStudents() { - Student *p = studentHead; //ѧָ룬ָѧͷ - setColor(11); - printf("\n%-10s %-10s %-10s\n", "ѧ", "", "༶"); //ӡѧϢͷָʽ - setColor(7); - while (p) { - printf("%-10s %-10s %-10s\n", p->id, p->name, p->className); //ӡǰѧѧš༶Ϣʽ - p = p->next; //ָָһѧڵ - } //ѭ -} // - -//ɾѧϢ -void deleteStudent(char *id) { - Student *p = studentHead, *pre = NULL; //Ҫɾѧ - while (p) { - if (strcmp(p->id, id) == 0) { //ҵƥѧ - if (pre) pre->next = p->next; //ǰڵָǰڵĺ - else studentHead = p->next; - free(p); //ͷڴ - setColor(12); - printf(" ɾѧ %s\n", id); - setColor(7); - return; - } - pre = p; //ǰڵ - p = p->next; //ƶһڵ - } - printf("δҵѧ\n"); -} - -//ʼڼ¼ -// ========== ڹ ========== -void startAttendance(int lessonNo) { - ClassRecord *cr = (ClassRecord*)malloc(sizeof(ClassRecord)); //γ̼¼ڴ - cr->lessonNo = lessonNo; - cr->attList = NULL; //ʼΪ - cr->next = recordHead; //¼¼ͷ - recordHead = cr; //¼¼ͷ - - Student *p = studentHead; //ѧп - printf("\n %dڿοڣ\n", lessonNo); - while (p) { - int st; - printf("ѧ:%s :%s 1 ȱ0: ", p->id, p->name); - scanf("%d", &st); - Attendance *a = (Attendance*)malloc(sizeof(Attendance)); //俼ڼ¼ڴ - strcpy(a->studentId, p->id); //ѧ - a->status = st; //ÿ״̬ - a->next = cr->attList; //뿼ͷ - cr->attList = a; //¿ͷ - p = p->next; - } - setColor(10);//ɫΪɫʾ - printf(" ڼ¼ɣ\n"); - setColor(7); -} -//ʾijογ̿ĺlessonNoǿγ̱ -void showLessonAttendance(int lessonNo) {//ָcrָڼ¼ͷڲĿγ - ClassRecord *cr = recordHead;//ڼ¼ָγ̱ŵļ¼//crΪNULLҿγ̱Ųƥʱ - while (cr && cr->lessonNo != lessonNo) cr = cr->next;// ƶһγ̼¼ - if (!cr) {// ǷҵĿγ̼¼ - printf(" δҵ%dڿμ¼\n", lessonNo); // ûҵʾϢ - return; // ˳ - } - Attendance *a = cr->attList; // ָaָǰγ̵Ŀͷ - setColor(14); // ɫΪɫ14ɫͷ - printf("\n%dڿο\n", lessonNo); - printf("%-10s %-10s %-10s\n", "ѧ", "", "״̬"); - setColor(7); - while (a) { // ǰγ̵пڼ¼ - Student *p = studentHead; // ָpָѧͷڲҶӦѧŵѧϢ - while (p && strcmp(p->id, a->studentId) != 0) p = p->next; // ѧв뵱ǰڼ¼ѧƥѧ - if (p) { // ƶһѧ // ҵӦѧϢ - printf("%-10s %-10s ", p->id, p->name); // ӡѧѧź - if (a->status) { setColor(10); printf("\n"); setColor(7); } // ݿ״̬ʾͬɫϢ - else { setColor(12); printf("ȱ\n"); setColor(7); } // ״̬Ϊ1ڣɫʾ"" - }// ״̬Ϊ0ȱڣúɫʾ"ȱ" - a = a->next; // ƶһڼ¼ - } -} -// ʾָѧпڼ¼ĺidѧѧ -void showStudentAttendance(char *id) { - ClassRecord *cr = recordHead; // ָcrָڼ¼ͷ - printf("\nѧ %s Ŀ\n", id); // ѯʾ - while (cr) {//пγ̼¼ - Attendance *a = cr->attList; // ָaָǰγ̵Ŀͷ - while (a) { // ǰγ̵пڼ¼ - if (strcmp(a->studentId, id) == 0) { // 鵱ǰڼ¼Ƿָѧƥ - printf("%dڿ: %s\n", cr->lessonNo, a->status ? "" : "ȱ"); // ƥ䣬ÿγ̵Ŀ - } // ʹԪstatusʾ"""ȱ" - a = a->next; // ƶһڼ¼ - } - cr = cr->next; // ƶһγ̼¼ - } -} - -// ========== ͳ ========== -void countLessonRate(int lessonNo) { - ClassRecord *cr = recordHead;//ӿδμ¼ͷʼ - while (cr && cr->lessonNo != lessonNo) cr = cr->next;//ƥƶһδμ¼ - if (!cr) {//crΪNULLʾûҵƥĿδ - printf("δҵÿδΣ\n"); - return;//ǰִ - } - int total = 0, present = 0;//ѧѧ - Attendance *a = cr->attList; - while (a) { - total++;//ÿһڼ¼1 - if (a->status) present++;//鿼״̬a->statusΪ棨0ʾڣ1 - a = a->next;//ƶһڼ¼ - } - printf("%dڿγ: %.2f%%\n", lessonNo, (double)present / total * 100); -} - -void countStudentAbsence(char *id) { - ClassRecord *cr = recordHead;//ȡδμ¼ͷ - int cnt = 0;//ʼȱڴ - while (cr) {//ѭѧÿһڿεĿ - Attendance *a = cr->attList;//ȡǰδεĿڼ¼ͷ - while (a) { - if (strcmp(a->studentId, id) == 0 && a->status == 0) cnt++;//ȱڴ1 - a = a->next;//ƶǰδεһڼ¼ - } - cr = cr->next;//ƶһδμ¼ - } - printf("ѧ %s ȱڴ: %d\n", id, cnt); -} diff --git "a/\345\255\246\347\224\237\350\200\203\345\213\244\347\263\273\347\273\237/attendance.h" "b/\345\255\246\347\224\237\350\200\203\345\213\244\347\263\273\347\273\237/attendance.h" deleted file mode 100644 index 6a32f49c58c2c0ef246e1b289c3aae35b0a1630a..0000000000000000000000000000000000000000 --- "a/\345\255\246\347\224\237\350\200\203\345\213\244\347\263\273\347\273\237/attendance.h" +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef ATTENDANCE_H -#define ATTENDANCE_H - -// ѧϢṹ -typedef struct Student { - char id[20]; - char name[20]; - char className[20];//ѧڰ༶ - struct Student *next;//ָһѧڵָ룬 -} Student; - -// ڼ¼ṹ壺ڴ洢οڵϢṹ -typedef struct Attendance { - char studentId[20];//ѧѧ - int status; // ״̬1=, 0=ȱ - struct Attendance *next;//ָһڼ¼ָ룬 -} Attendance; - -// γ̼¼ṹ壺ڴ洢ijһογ̵Ŀڼϣṹ -typedef struct ClassRecord { - int lessonNo;//γ̱ - Attendance *attList;//ָôογ̿ڼ¼ͷָ - struct ClassRecord *next;//ָһογ̼¼ָ룬 -} ClassRecord; - -// ========== ========== - -// ѧ -void addStudent(); //ѧϢ -void showStudents();//ʾѧϢ -void deleteStudent(char *id);//ѧɾѧϢ - -// ڹ -void startAttendance(int lessonNo);// ʼijһογ̵Ŀ -void showLessonAttendance(int lessonNo);// ʾijһογ̵Ŀڼ¼ -void showStudentAttendance(char *id);// ʾijѧпڼ¼ - -// ͳ -void countLessonRate(int lessonNo);// ͳijһογ̵ij -void countStudentAbsence(char *id);// ͳijѧȱڴ - -// -void setColor(int color);// ÿ̨ɫcolorΪɫ룩 - -#endif diff --git "a/\345\255\246\347\224\237\350\200\203\345\213\244\347\263\273\347\273\237/main.c" "b/\345\255\246\347\224\237\350\200\203\345\213\244\347\263\273\347\273\237/main.c" deleted file mode 100644 index 835577e9568dee653bf9bfedc3840ff3114d6ee0..0000000000000000000000000000000000000000 --- "a/\345\255\246\347\224\237\350\200\203\345\213\244\347\263\273\347\273\237/main.c" +++ /dev/null @@ -1,82 +0,0 @@ -#include -#include -#include "attendance.h" - -int main() { - int choice; // ûѡ - - // ѭ - while (1) { - // ʾϵͳ˵ - setColor(11); // ɫ - printf("\n========= ѧϵͳ =========\n"); - setColor(7); // ָĬɫ - - // ˵ѡ - printf("1. ѧ\n"); - printf("2. ɾѧ\n"); - printf("3. ʾѧб\n"); - printf("4. ʼ\n"); - printf("5. 鿴ijڿο\n"); - printf("6. 鿴ijѧ\n"); - printf("7. ͳijڿγ\n"); - printf("8. ͳijѧȱڴ\n"); - printf("0. ˳ϵͳ\n"); - printf("ѡ"); - - // ȡû - scanf("%d", &choice); - if (choice == 0) break; // ˳ϵͳ - - char id[20]; // ѧŻ - int lesson; // δα - - // ûѡִӦ - switch(choice) { - case 1: - addStudent(); // ѧ - break; - case 2: - printf("ѧ: "); - scanf("%s", id); - deleteStudent(id); // ɾѧ - break; - case 3: - showStudents(); // ʾѧб - break; - case 4: - printf("δ: "); - scanf("%d", &lesson); - startAttendance(lesson); // ʼ - break; - case 5: - printf("δ: "); - scanf("%d", &lesson); - showLessonAttendance(lesson); // 鿴δο - break; - case 6: - printf("ѧ: "); - scanf("%s", id); - showStudentAttendance(id); // 鿴ѧ - break; - case 7: - printf("δ: "); - scanf("%d", &lesson); - countLessonRate(lesson); // ͳƳ - break; - case 8: - printf("ѧ: "); - scanf("%s", id); - countStudentAbsence(id); // ͳȱڴ - break; - default: - printf("Ч룡\n"); // 봦 - } - } - - // ˳ϵͳ - setColor(10); // ɫ˳Ϣ - printf(" лʹãټ\n"); - setColor(7); // ָĬɫ - return 0; -} diff --git "a/\346\240\221\347\273\223\346\236\204.docx" "b/\346\240\221\347\273\223\346\236\204.docx" new file mode 100644 index 0000000000000000000000000000000000000000..758dd446640c361e9a19ee46043119f2880c492a Binary files /dev/null and "b/\346\240\221\347\273\223\346\236\204.docx" differ diff --git "a/\346\272\220\344\273\243\347\240\201.c" "b/\346\272\220\344\273\243\347\240\201.c" new file mode 100644 index 0000000000000000000000000000000000000000..ef5d12374fa1826a69e5cd2d767df79aa7671218 --- /dev/null +++ "b/\346\272\220\344\273\243\347\240\201.c" @@ -0,0 +1,539 @@ +#include +#include +#include +#include + +#define MAX_NAME_LEN 50 +#define MAX_PHONE_LEN 20 +#define MAX_EMAIL_LEN 50 +#define MAX_ADDRESS_LEN 100 +#define MAX_GROUP_LEN 20 +#define MAX_CONTACTS 1000 + +// ϵ˽ṹ +typedef struct { + char name[MAX_NAME_LEN]; + char phone[MAX_PHONE_LEN]; + char email[MAX_EMAIL_LEN]; + char address[MAX_ADDRESS_LEN]; + char group[MAX_GROUP_LEN]; +} Contact; + +// ͨѶ¼ṹ壨˳ +typedef struct { + Contact contacts[MAX_CONTACTS]; + int count; +} AddressBook; + +// ߺ +void trim_newline(char *str) { + int len = strlen(str); + if (len > 0 && str[len - 1] == '\n') { + str[len - 1] = '\0'; + } +} + +int validate_phone(const char *phone) { + // 򵥵ĵ绰֤: ֻ֡ߺͿո + for (int i = 0; phone[i] != '\0'; i++) { + if (!isdigit(phone[i]) && phone[i] != '-' && phone[i] != ' ') { + return 0; + } + } + return strlen(phone) >= 7; // 7λ +} + +int validate_email(const char *email) { + if (strlen(email) == 0) return 1; // + + int at_count = 0; + int dot_after_at = 0; + + for (int i = 0; email[i] != '\0'; i++) { + if (email[i] == '@') { + at_count++; + if (at_count > 1) return 0; + } + if (at_count > 0 && email[i] == '.') { + dot_after_at = 1; + } + } + return at_count == 1 && dot_after_at; +} + +int is_contact_valid(const Contact *contact) { + if (strlen(contact->name) == 0 || strlen(contact->phone) == 0) { + return 0; + } + + if (!validate_phone(contact->phone)) { + return 0; + } + + if (strlen(contact->email) > 0 && !validate_email(contact->email)) { + return 0; + } + + return 1; +} + +void clear_input_buffer() { + int c; + while ((c = getchar()) != '\n' && c != EOF) { + // ѭ壬ȡֱзļ + } +} + +// ʼʾϵ +void initialize_sample_contacts(AddressBook *book) { + // ʾϵ1 + Contact contact1 = { + "", + "13812345678", + "zhangsan@example.com", + "к", + "" + }; + + // ʾϵ2 + Contact contact2 = { + "", + "13987654321", + "lisi@company.com", + "Ϻֶ", + "ͬ" + }; + + // ʾϵ3 + Contact contact3 = { + "", + "13611112222", + "wangwu@gmail.com", + "", + "" + }; + + // ʾϵ4 + Contact contact4 = { + "", + "13733334444", + "zhaoliu@qq.com", + "ɽ", + "ͬѧ" + }; + + // ʾϵ˵ͨѶ¼ + if (book->count < MAX_CONTACTS) { + book->contacts[book->count++] = contact1; + } + if (book->count < MAX_CONTACTS) { + book->contacts[book->count++] = contact2; + } + if (book->count < MAX_CONTACTS) { + book->contacts[book->count++] = contact3; + } + if (book->count < MAX_CONTACTS) { + book->contacts[book->count++] = contact4; + } +} + +// û溯 +void display_main_menu() { + printf("\n========== ͨѶ¼ϵͳ ==========\n"); + printf("1. ϵ\n"); + printf("2. ɾϵ\n"); + printf("3. ޸ϵ\n"); + printf("4. ѯϵ\n"); + printf("5. ʾϵ\n"); + printf("6. \n"); + printf("7. \n"); + printf("8. \n"); + printf("0. ˳ϵͳ\n"); + printf("====================================\n"); + printf("ѡ: "); +} + +int get_user_choice() { + char input[10]; + int choice; + + while (1) { + fgets(input, sizeof(input), stdin); + if (sscanf(input, "%d", &choice) == 1 && choice >= 0 && choice <= 8) { + return choice; + } + printf("Чѡ (0-8): "); + } +} + +void display_contact(const Contact *contact) { + printf("\n--- ϵϢ ---\n"); + printf(": %s\n", contact->name); + printf("绰: %s\n", contact->phone); + printf(": %s\n", contact->email); + printf("ַ: %s\n", contact->address); + printf(": %s\n", contact->group); +} + +void display_all_contacts(const AddressBook *book) { + if (book->count == 0) { + printf("\nͨѶ¼Ϊ!\n\n"); + return; + } + + printf("\n=== ϵ (%d) ===\n", book->count); + printf("%-15s %-15s %-20s %-15s %-10s\n", + "", "绰", "", "ַ", ""); + printf("---------------------------------------------------------------------------\n"); + + for (int i = 0; i < book->count; i++) { + const Contact *c = &book->contacts[i]; + printf("%-15s %-15s %-20s %-15s %-10s\n", + c->name, c->phone, c->email, c->address,c->group); + } + printf("===========================================================================\n"); + } + + void input_contact_info(Contact *contact) { + printf("\nϵϢ:\n"); + + printf(": "); + fgets(contact->name, MAX_NAME_LEN, stdin); + trim_newline(contact->name); + + printf("绰: "); + fgets(contact->phone, MAX_PHONE_LEN, stdin); + trim_newline(contact->phone); + + printf(": "); + fgets(contact->email, MAX_EMAIL_LEN, stdin); + trim_newline(contact->email); + + printf("ַ: "); + fgets(contact->address, MAX_ADDRESS_LEN, stdin); + trim_newline(contact->address); + + printf(": "); + fgets(contact->group, MAX_GROUP_LEN, stdin); + trim_newline(contact->group); + } + + void input_search_keyword(char *keyword) { + printf("ؼ: "); + fgets(keyword, MAX_NAME_LEN, stdin); + trim_newline(keyword); + } + + // ϵ˹ + int add_contact(AddressBook *book, const Contact *contact) { + if (book->count >= MAX_CONTACTS) { + printf("ͨѶ¼޷ϵ!\n"); + return 0; + } + + // ǷѴ + for (int i = 0; i < book->count; i++) { + if (strcasecmp(book->contacts[i].name, contact->name) == 0) { + printf("ϵ %s Ѵ!\n", contact->name); + return 0; + } + } + + // ϵ + book->contacts[book->count] = *contact; + book->count++; + return 1; + } + + int delete_contact(AddressBook *book, const char *name) { + for (int i = 0; i < book->count; i++) { + if (strcasecmp(book->contacts[i].name, name) == 0) { + // ҵϵˣɾƶԪ + for (int j = i; j < book->count - 1; j++) { + book->contacts[j] = book->contacts[j + 1]; + } + book->count--; + return 1; + } + } + return 0; + } + + int modify_contact(AddressBook *book, const char *name, const Contact *new_contact) { + for (int i = 0; i < book->count; i++) { + if (strcasecmp(book->contacts[i].name, name) == 0) { + // ϵϢ + book->contacts[i] = *new_contact; + return 1; + } + } + return 0; + } + + Contact* search_contact(const AddressBook *book, const char *keyword) { + static Contact results[MAX_CONTACTS]; + int result_count = 0; + + for (int i = 0; i < book->count; i++) { + const Contact *c = &book->contacts[i]; + + // 绰 + if (strstr(c->name, keyword) != NULL || + strstr(c->phone, keyword) != NULL || + strstr(c->group, keyword) != NULL) { + results[result_count++] = *c; + } + } + + results[result_count].name[0] = '\0'; // + return results; + } + + void sort_contacts_by_name(AddressBook *book) { + for (int i = 0; i < book->count - 1; i++) { + for (int j = 0; j count - i - 1; j++) { + if (strcasecmp(book->contacts[j].name, book->contacts[j + 1].name) > 0) { + // + Contact temp = book->contacts[j]; + book->contacts[j] = book->contacts[j + 1]; + book->contacts[j + 1] = temp; + } + } + } + } + + // ļ洢 + int save_to_file(const AddressBook *book, const char *filename) { + FILE *file = fopen(filename, "wb"); + if (file == NULL) { + return 0; + } + + fwrite(&book->count, sizeof(int), 1, file); + fwrite(book->contacts, sizeof(Contact), book->count, file); + fclose(file); + return 1; + } + + int load_from_file(AddressBook *book, const char *filename) { + FILE *file = fopen(filename, "rb"); + if (file == NULL) { + return 0; + } + + fread(&book->count, sizeof(int), 1, file); + fread(book->contacts, sizeof(Contact), book->count, file); + fclose(file); + return 1; + } + + int export_to_text(const AddressBook *book, const char *filename) { + FILE *file = fopen(filename, "w"); + if (file == NULL) { + return 0; + } + + fprintf(file, ",绰,,ַ,\n"); + for (int i = 0; i < book->count; i++) { + const Contact *c = &book->contacts[i]; + fprintf(file, "%s,%s,%s,%s,%s\n", + c->name, c->phone, c->email, c->address, c->group); + } + + fclose(file); + return 1; + } + + int import_from_text(AddressBook *book, const char *filename) { + FILE *file = fopen(filename, "r"); + if (file == NULL) { + return 0; + } + + char line[256]; + fgets(line, sizeof(line), file); // + + int imported = 0; + + while (fgets(line, sizeof(line), file)) { + Contact contact; + char *token = strtok(line, ","); + + if (token) strncpy(contact.name, token, MAX_NAME_LEN); + token = strtok(NULL, ","); + + if (token) strncpy(contact.phone, token, MAX_PHONE_LEN); + token = strtok(NULL, ","); + + if (token) strncpy(contact.email, token, MAX_EMAIL_LEN); + token = strtok(NULL, ","); + + if (token) strncpy(contact.address, token, MAX_ADDRESS_LEN); + token = strtok(NULL, "\n"); + + if (token) strncpy(contact.group, token, MAX_GROUP_LEN); + + if (add_contact(book, &contact)) { + imported++; + } + } + + fclose(file); + return imported; + } + + // ϵͳʼ + void initialize_system(AddressBook *book) { + book->count = 0; + if (load_from_file(book, "contacts.dat")) { + printf("ϵͳʼɣ %d ϵ\n", book->count); + } else { + printf("ϵͳʼɣµͨѶ¼\n"); + // ʾϵ + initialize_sample_contacts(book); + printf(" %d ʾϵ\n", book->count); + } + } + + void cleanup_system(AddressBook *book) { + if (save_to_file(book, "contacts.dat")) { + printf("ѱ浽ļ\n"); + } else { + printf("ݱʧ\n"); + } + } + + // ѭ + void main_loop(AddressBook *book) { + int choice; + char keyword[MAX_NAME_LEN]; + + while (1) { + display_main_menu(); + choice = get_user_choice(); + + switch (choice) { + case 0: // ˳ + return; + + case 1: { // ϵ + Contact new_contact; + input_contact_info(&new_contact); + + if (is_contact_valid(&new_contact)) { + if (add_contact(book, &new_contact)) { + printf("ϵӳɹ!\n"); + } else { + printf("ϵʧ!\n"); + } + } else { + printf("ϵϢЧ!\n"); + } + break; + } + + case 2: { // ɾϵ + printf("Ҫɾϵ: "); + input_search_keyword(keyword); + + if (delete_contact(book, keyword)) { + printf("ϵɾɹ!\n"); + } else { + printf("ϵ˲!\n"); + } + break; + } + + case 3: { // ޸ϵ + printf("Ҫ޸ĵϵ: "); + input_search_keyword(keyword); + + Contact *found = search_contact(book, keyword); + if (found->name[0] != '\0') { + Contact new_contact; + input_contact_info(&new_contact); + + if (modify_contact(book, keyword, &new_contact)) { + printf("ϵ޸ijɹ!\n"); + } + } else { + printf("ϵ˲!\n"); + } + break; + } + + case 4: { // ѯϵ + printf("ؼ: "); + input_search_keyword(keyword); + + Contact *results = search_contact(book, keyword); + if (results->name[0] != '\0') { + printf("\n=== ===\n"); + int i = 0; + while (results[i].name[0] != '\0' && i < MAX_CONTACTS) { + display_contact(&results[i]); + i++; + } + } else { + printf("δҵƥϵ!\n"); + } + break; + } + + case 5: // ʾϵ + display_all_contacts(book); + break; + + case 6: // + sort_contacts_by_name(book); + printf("ϵѰ!\n"); + break; + + case 7: { // + printf("Ҫļ: "); + char filename[100]; + fgets(filename, sizeof(filename), stdin); + trim_newline(filename); + + int count = import_from_text(book, filename); + printf("ɹ %d ϵ!\n", count); + break; + } + + case 8: { // + printf("뵼ļ: "); + char filename[100]; + fgets(filename, sizeof(filename), stdin); + trim_newline(filename); + if (export_to_text(book, filename)) { + printf("ݵɹ!\n"); + } else { + printf("ʧ!\n"); + } + break; + } + } + + printf("\nس..."); + clear_input_buffer(); + getchar(); + } + } + + // + int main() { + AddressBook addressBook; + + printf("=== CͨѶ¼ϵͳ ===\n"); + + // ʼϵͳss initialize_system(&addressBook); + + // ѭ + main_loop(&addressBook); + + // ϵͳ + cleanup_system(&addressBook); + + printf("лʹͨѶ¼ϵͳ!\n"); + return 0; + } diff --git "a/\351\200\232\350\256\257\345\275\225\347\256\241\347\220\206\347\263\273\347\273\237\351\241\271\347\233\256\344\271\246.docx" "b/\351\200\232\350\256\257\345\275\225\347\256\241\347\220\206\347\263\273\347\273\237\351\241\271\347\233\256\344\271\246.docx" new file mode 100644 index 0000000000000000000000000000000000000000..ad760038a0bd69ccfbf1e779c8d99c75f7ee54f1 Binary files /dev/null and "b/\351\200\232\350\256\257\345\275\225\347\256\241\347\220\206\347\263\273\347\273\237\351\241\271\347\233\256\344\271\246.docx" differ