【C 语言项目实战】基于链表与文件操作的标准化彩票管理系统设计与实现

一、项目背景与技术架构
在职坐标平台的C语言的学习旅程中,我们往往止步于语法基础和简单的算法练习。然而,如何将链表、文件操作、模块化设计结合,构建一个真实的业务系统,是很多初学者面临的瓶颈。

本系统基于 C 语言(C99 标准) 编写,采用单向链表作为核心数据结构,并结合二进制文件进行数据持久化。系统严格遵循模块化设计原则,实现了用户管理、彩票发行、投注购买、开奖兑奖等完整业务闭环。

核心亮点:

多角色权限控制:支持彩民、管理员、公证员三类角色的差异化操作。
双色球中奖算法:实现复杂的奖项等级判定逻辑(6 红球 +1 蓝球)。
动态奖池机制:支持奖池累加、滚存及按比例分配。
数据安全:采用二进制文件存储,确保掉电后数据不丢失。
二、系统架构与需求概览
根据需求文档,本系统面向彩民、管理员、公证员三类角色,严格遵循四层架构(表示层、业务逻辑层、数据访问层、基础设施层)。

2.1 核心业务流程
系统的核心在于“发行-购买-开奖-兑奖”的闭环。

管理员:负责添加期号并发行彩票。
彩民:登录购买,支持多注、多倍投注。
公证员:独立执行开奖(设置号码)和兑奖(资金发放),确保公平性。
2.2 开发环境与工具链
本系统主要在 Windows 环境下开发,使用了轻量级的 GCC 编译器。

维度 选型 说明
开发语言 C 语言 遵循 C99 标准
编译器 GCC (MinGW-w64) 高效稳定的编译环境
构建工具 Make 通过 Makefile 管理编译流程
数据结构 单向链表 内存中高效管理用户、彩票及投注记录
存储方式 二进制文件 (.bin) 保证数据安全性与读写效率
三、系统架构设计
系统采用了清晰的四层分层架构,确保了代码的可维护性和扩展性。

表示层 (UI):负责所有界面渲染与菜单显示。
业务逻辑层:核心处理层,包含用户、彩票、公证等模块。
数据访问层 (File):负责链表与二进制文件之间的读写交互。
基础设施层 (Utils/Common):提供工具函数、宏定义及枚举类型。
模块依赖关系:

main.c 作为程序入口,调度 system 模块。
system 模块根据角色分发至 user、admin 或 notary 模块。
各业务模块通过 file 模块与磁盘数据交互,并依赖 utils 进行内存管理和输入验证。
四、核心数据结构设计
数据结构是系统的骨架。根据SRS文档,我们设计了三个核心结构体,完美映射业务实体。

4.1 彩民信息 (User)
支持多角色(彩民/管理员/公证员)和余额管理。

typedef struct User {
int userId; // 全局自增ID
char account[MAX_ACCOUNT_LEN]; // 账号
char password[MAX_PASSWORD_LEN]; // 密码
double balance; // 余额
int role; // 角色枚举
struct User *next;
} User;

4.2 彩票期号 (Lottery)
管理每一期的状态与奖池。特别注意,奖池基础值为100万(DEFAULT_PRIZE_POOL)。

typedef struct Lottery {
int issueNumber; // 期号
int issueStatus; // 发行状态
int drawStatus; // 开奖状态
int winningNumbers[NUMBERS_PER_BET]; // 开奖号码 (7个)
int totalSold; // 售出总注数
double prizePool; // 奖池总额
struct Lottery *next;
} Lottery;

4.3 购买记录 (Ticket)
这是最复杂的结构,需要记录多注号码、倍数及中奖详情。

typedef struct Ticket {
long long ticketId; // 彩票唯一ID
int issueNumber; // 所属期号
int betCount; // 注数 (1-5)
// 二维数组存储: 5注 x 8位 (6红+1蓝+1倍数)
int selectedNumbers[MAX_BET_COUNT][NUMBERS_PER_BET + 1];
char buyerAccount[MAX_ACCOUNT_LEN]; // 买家账号
double price; // 总价格
int winStatus; // 中奖状态
int prizeLevel[MAX_BET_COUNT]; // 每注的奖项等级
double winAmount; // 中奖金额
struct Ticket *next;
} Ticket;

五、核心业务逻辑实现
5.1 多角色权限控制
系统启动后,根据登录账号的角色(role字段),分发至不同的操作菜单。这是通过main函数中的switch-case实现的:

// 伪代码示例
User* currentUser = userLogin(userList);
switch(currentUser->role) {
case ROLE_ADMIN:
adminMenu();
break;
case ROLE_NOTARY:
notaryMenu();
break;
default:
userMenu(); // 彩民
}

5.2 彩票发行与奖池规则
发行流程:管理员需先“添加期号”(奖池初始化为 100 万),再“发行彩票”。
奖池累加:若上一期未发行,其奖池将自动累加到本期。
销售规则:单价硬编码为 2 元/倍,支持 1~10 倍投注。
5.3 双色球中奖算法 (重点)
这是本系统的难点之一。系统采用双色球模式(6 红球 +1 蓝球)进行判定。

奖项等级 判定条件 (红球 + 蓝球) 奖金分配比例
一等奖 6 + 1 奖池的 50%
二等奖 6 + 0 或 5 + 1 奖池的 25%
三等奖 5 + 0 或 4 + 1 奖池的 15%
四等奖 4 + 0 或 3 + 1 奖池的 10%
算法实现逻辑 (isWinningLottery):

统计每注红球匹配数量(红球已自动排序)。
判断蓝球是否匹配。
根据红球匹配数和蓝球状态,查表确定奖项等级。
代码实现逻辑:

// notary.c - 中奖判定核心逻辑
int isWinningLottery(User *userHead, Ticket *ticket, Lottery *lottery) {
// 1. 防御性检查:用户是否已注销
if (userIsDeleted(userHead, ticket->buyerAccount)) {
ticket->winStatus = WIN_STATUS_INVALID;
return 0;
}

// 2. 遍历每一注
for (int i = 0; i < ticket->betCount; i++) {
int redMatch = 0;
// 红球匹配 (前6个)
for (int j = 0; j < 6; j++) {
for (int k = 0; k < 6; k++) {
if (ticket->selectedNumbers[i][j] == lottery->winningNumbers[k]) {
redMatch++;
break;
}
}
}
// 蓝球匹配 (第7个)
int blueMatch = (ticket->selectedNumbers[i][6] == lottery->winningNumbers[6]);

// 3. 判定等级
if (redMatch == 6 && blueMatch) {
ticket->prizeLevel[i] = PRIZE_LEVEL_FIRST;
} else if ((redMatch == 6 && !blueMatch) || (redMatch == 5 && blueMatch)) {
ticket->prizeLevel[i] = PRIZE_LEVEL_SECOND;
}
// ... 其他等级判定
}
return 1;
}

5.4 兑奖流程 (Cash Prizes)
兑奖与开奖分离,确保流程严谨:

累加奖池:将当期所有彩票的 price 累加至奖池。
统计倍数:遍历所有彩票,统计每个奖项等级的总倍数。
计算奖金:每倍奖金 = 奖池 × 比例 ÷ 总倍数。
发放奖金:遍历中奖彩票,中奖金额 = 每倍奖金 × 该注倍数,并更新用户余额。
六、数据持久化与链表管理
系统采用二进制文件进行存储,确保断电数据不丢失。

6.1 文件操作封装
在file.c中,我们封装了通用的读写逻辑。以保存用户为例:

int fileSaveUsers(User *head) {
FILE *fp = fopen(USERS_FILE_PATH, "wb");
if (!fp) return 0;

User *current = head->next; // 跳过头节点
while (current != NULL) {
fwrite(current, sizeof(User), 1, fp);
current = current->next;
}
fclose(fp);
return 1;
}
6.2 内存安全
严格遵守C语言内存管理规范:

初始化:所有链表节点next指针置为NULL。
释放:程序退出时,调用utilsFreeXxxList释放所有链表节点。
检查:malloc后立即检查NULL,防止野指针。
七、总结与展望
通过本次项目实战,我深入理解了 C 语言在大型项目中的模块化设计思想。该系统不仅实现了基本的 CRUD 操作,还解决了复杂的业务逻辑(如奖池分配、多维数组排序等)。

项目亮点:

高内聚低耦合:9个模块分工明确,main.c仅作为调度中心。
业务逻辑严谨:严格区分了“开奖”与“兑奖”两个步骤,符合实际业务场景。
健壮性:完善的输入校验和内存管理机制。
未来优化方向:

增加数据变更标记机制,仅在数据变更时保存,提升性能。
实现图形化界面(GUI)版本。

posted @ 2026-06-28 20:45  Solitude0420  阅读(57)  评论(6)    收藏  举报