12 Commits
Author SHA1 Message Date
Drunk-Youth 292af904d4 更新 AGENTS.md:补充已知陷阱和约束细节 2026-06-24 19:13:17 +08:00
Drunk-Youth 9f381d91a8 增加哈希表模块,加速账户ID查找,更新文档 2026-06-24 18:55:35 +08:00
Drunk-Youth bb21a04771 graph 2026-06-15 16:36:36 +08:00
Idiot ce8abe3c18 删除 test 2026-05-21 23:07:50 +08:00
Idiot-awa 9fb190ad5d Merge branch 'main' of http://idiot.asia/Idiot/BankManager 2026-05-21 23:01:12 +08:00
Idiot-awa 627201c739 增加交易日志搜索功能 2026-05-21 23:01:08 +08:00
Idiot 772b417cb2 添加 test 2026-05-08 17:08:33 +08:00
Idiot f4737c82f6 更新 README.md 2026-05-08 16:30:32 +08:00
Idiot 93e0214536 更新 README.md 2026-05-08 16:28:23 +08:00
Idiot-awa 74e467e111 增加日志 2026-05-08 12:39:58 +08:00
Idiot-awa 7d623c1993 增加日志 2026-05-08 11:46:09 +08:00
idiot ae99d48eda 更改编码为 utf8 2026-04-22 17:32:22 +08:00
12 changed files with 1227 additions and 281 deletions
+7
View File
@@ -0,0 +1,7 @@
*.o
*.exe
bank.dat
.vscode/launch.json
.vscode/tasks.json
a.out
bank_system
+53
View File
@@ -0,0 +1,53 @@
# BankManager — C 语言银行账户管理系统
## 构建 & 运行
```sh
gcc bank.c account.c transaction.c file_io.c utils.c log.c graph.c hash.c -o bank_system
./bank_system
```
`bank.h` 是唯一头文件,所有 `.c` 编译顺序无要求。
## 项目布局
所有源文件在根目录,无 `src/` 子目录。无包管理器、无构建系统、无测试框架。
| 文件 | 职责 |
|------|------|
| `bank.c` | 主循环、菜单、权限路由 |
| `account.c` | 开户、销户、登录、修改密码、查询全部账户 |
| `transaction.c` | 存款、取款、查余额、转账 |
| `file_io.c` | 二进制文件读写 (bank.dat) |
| `utils.c` | 输入校验工具(`GetInputInt`/`GetInputDouble` 已定义但未被使用) |
| `log.c` | 环形队列日志,支持 KMP 关键词搜索 |
| `graph.c` | 从转账日志建邻接矩阵,分析客户间资金来往关系 |
| `hash.c` | 哈希表,映射账户ID到数组下标,线性探测+墓碑标记 |
| `bank.h` | 唯一头文件,声明所有公有函数和类型 |
## 重要数据/约束
- **Max 50 账户** (`MAX_ACCOUNTS`),全局数组 `g_astAccounts`,不使用 `malloc`
- **`g_iAccCount` 初始值=1**(非0),槽位 0 预留给管理员。
- **默认管理员**: ID=1000, name="admin", password=123456, quanxian=1。首次运行时自动创建。
- **密码**: 纯 6 位数字(100000-999999)。
- **权限**: 1=管理员,2=普通用户。
- **数据持久化**: 二进制文件 `bank.dat`(已加入 `.gitignore`)。`SaveData()` 总是写入恰好 50 条记录。
- **全局日志队列**: 环形队列 `g_logQueue`,最多 100 条 (`MAX_LOGS`),溢出时队头出队覆盖旧记录。
- **哈希表**: `HASH_SIZE=101`(质数),开放寻址法+线性探测,删除用墓碑标记(id=-2)。
## 代码约定
- 中文注释和中文菜单字符串。
- 全局变量命名前缀 `g_`(如 `g_iAccCount`, `g_astAccounts`)。
- 无测试。
## 已知陷阱
- **`InitAdminAccount()` 双重调用导致密码重置** — `LoadData()` 内无文件时调用一次,`main()` 第 21 行无条件再调用一次。管理员密码修改在重启后必然丢失。
- **`SaveData()` 内部调用 `LoadData()`**`file_io.c:22`),写盘后立即重读以重新计数 `g_iAccCount`。注意这会导致刚保存的数组被覆盖读回。
- **`GenerateDummyData()`(graph.c)会清空全部日志并修改账户余额**,仅用于测试演示,非生产操作。
- **`DeleteAccount()` 不递减 `g_iAccCount`** — 递减由调用方 `bank.c:84` 负责。函数内 `for (; i < g_iAccCount-i; i++)` 边界有误(应为 `g_iAccCount-1`)。
- **`exit(0)` 短路** — `bank.c:53` 直接 `exit(0)`,不会执行 `main()` 末尾的清理代码(当前无实质性清理,但未来加逻辑时需注意)。
- **`ChangePassword()` 已定义但未接入菜单**`account.c:159`),无法从界面调用。
- **`utils.c``GetInputInt`/`GetInputDouble` 已定义但未被任何调用方使用**,全部输入仍用裸 `scanf`
+32 -4
View File
@@ -13,10 +13,13 @@
- 存款
- 取款
- 查询余额
- 转账(账户间资金转移)
- **系统功能**
- 用户登录验证
- 权限分级(管理员/普通用户)
- 数据持久化(自动保存到文件)
- 交易流水查询(管理员)
- 资金关系图分析(管理员):从转账日志建图,分析客户间资金来往关系
- 自动初始化默认管理员账户
## 系统架构
@@ -37,6 +40,7 @@
- 存款功能
- 取款功能
- 余额查询
- 转账功能(账户间资金转移)
4. **数据持久化 (file_io.c)**
- 数据保存到文件(bank.dat
@@ -52,6 +56,22 @@
- 账户结构体定义
- 函数声明
7. **日志模块(log.c)**
- 日志记录和存储(环形队列)
8. **资金关系图分析(graph.c)**
- 从转账日志构建邻接矩阵
- 显示全部转账关系图
- 查看指定账户关联关系
- 最活跃账户排名(度中心性)
- BFS最短转账路径查找
9. **哈希表模块(hash.c)**
- 映射账户ID到数组下标,O(1)快速查找
- 开放寻址法,线性探测解决冲突
- 墓碑标记处理删除操作
- 支持从数组重建哈希表
### 数据结构
```c
typedef struct strAccount
@@ -67,7 +87,7 @@ typedef struct strAccount
## 使用说明
### 编译
```bash
gcc bank.c account.c transaction.c file_io.c utils.c -o bank_system
gcc bank.c account.c transaction.c file_io.c utils.c log.c graph.c hash.c -o bank_system
```
### 运行
@@ -85,6 +105,7 @@ gcc bank.c account.c transaction.c file_io.c utils.c -o bank_system
- 选项1:存款
- 选项2:取款
- 选项3:查询余额
- 选项4:转账
- 选项0:退出账户
3. **管理员界面**(登录后)
@@ -94,6 +115,10 @@ gcc bank.c account.c transaction.c file_io.c utils.c -o bank_system
- 选项4:显示所有账户
- 选项5:删除账户
- 选项6:创建管理员账户
- 选项7:查看交易流水
- 选项8:搜索交易日志
- 选项9:转账
- 选项10:资金关系图分析
- 选项0:退出账户
### 默认账户
@@ -108,7 +133,7 @@ gcc bank.c account.c transaction.c file_io.c utils.c -o bank_system
- **密码规则**: 6位数字(100000-999999
- **数据存储**: 二进制文件(bank.dat
- **输入验证**: 所有数值输入都包含错误处理
- **内存管理**: 使用全局数组存储账户数据
- **内存管理**: 使用全局数组存储账户数据,哈希表加速ID查找
## 文件说明
- `bank.c` - 主程序文件
@@ -116,6 +141,9 @@ gcc bank.c account.c transaction.c file_io.c utils.c -o bank_system
- `transaction.c` - 交易处理功能实现
- `file_io.c` - 文件读写功能实现
- `utils.c` - 工具函数实现
- `log.c` - 日志记录和存储(环形队列)
- `graph.c` - 资金关系图分析模块
- `hash.c` - 哈希表模块,映射账户ID到数组下标
- `bank.h` - 头文件,包含结构体定义和函数声明
- `bank.dat` - 数据存储文件(运行时自动生成)
@@ -127,8 +155,8 @@ gcc bank.c account.c transaction.c file_io.c utils.c -o bank_system
5. 系统会自动保存所有操作到文件,确保数据持久化
## 扩展建议
- 添加交易记录功能
- 实现账户排序和搜索
- ~~添加交易记录功能~~
- ~~实现账户快速搜索~~(已通过哈希表实现)
- 增加更多权限级别
- 改进用户界面(如使用图形界面)
- 添加数据加密功能
+60 -63
View File
@@ -6,15 +6,15 @@
extern STACCOUNT g_astAccounts[];
extern int g_iAccCount;
/* 函数功能:处理用户的创建账户操作
思考处理步骤,写到函数体内注释
* 参数:无
* 存款信息要写入全局变量g_astAccounts中
* 返回值:无
* 存款失败怎么处理?
/* 函数功能:处理用户的创建账户操作
思考处理步骤,写到函数体内注释
* 参数:无
* 存款信息要写入全局变量g_astAccounts中
* 返回值:无
* 存款失败怎么处理?
*/
void InitAdminAccount() {
printf("初始化默认管理员账户");
printf("初始化默认管理员账户");
g_astAccounts[0].id = 1000;
strcpy(g_astAccounts[0].name, "admin");
g_astAccounts[0].balance = 0.0;
@@ -25,73 +25,74 @@ void InitAdminAccount() {
void CreateAccount(int quanxian)
{
// (1)合法性检查
// (1)合法性检查
if(MAX_ACCOUNTS < g_iAccCount)
{
printf("账户数量已达上限!\n");
printf("账户数量已达上限!\n");
return;
}
// (2)定义局部变量(函数内使用的变量)
STACCOUNT stNewAcc; //账户变量,结构体变量定义
//char cInput[NAME_LEN]; //接输入的名字字符串
// (2)定义局部变量(函数内使用的变量)
STACCOUNT stNewAcc; //账户变量,结构体变量定义
//char cInput[NAME_LEN]; //接输入的名字字符串
// (3)功能实现
printf("请输入姓名(最多30个字符): ");
// (3)功能实现
printf("请输入姓名(最多30个字符): ");
//这里有风险,怎么改进?
//这里有风险,怎么改进?
scanf("%30s", stNewAcc.name);
//stNewAcc.name[NAME_LEN - 1] = '\0'; // 确保null终止
//输密码
//stNewAcc.name[NAME_LEN - 1] = '\0'; // 确保null终止
//输密码
int temp_password;
while (1) {
printf("请输入密码\n");
printf("请输入密码\n");
scanf("%d",&temp_password);
if (99999<temp_password&&temp_password<999999) {
printf("设置成功\n");
printf("设置成功\n");
stNewAcc.password=temp_password;
SaveData();
break;
}
printf("密码不符合规范!请重新输入\n");
printf("密码不符合规范!请重新输入\n");
}
//需求中要求账户ID从1000开始,初始化账户余额
//需求中要求账户ID从1000开始,初始化账户余额
stNewAcc.id = 1000 + g_iAccCount;
stNewAcc.balance = 0.0;
stNewAcc.quanxian = quanxian;
g_astAccounts[g_iAccCount] = stNewAcc;
//把记录账户数量的全局变量自增。加过之后自增,所以下标0占用,数量是1个
//把记录账户数量的全局变量自增。加过之后自增,所以下标0占用,数量是1个
HashInsert(stNewAcc.id, g_iAccCount);
g_iAccCount++;
printf("开户成功! 账户ID: %d\n", stNewAcc.id);
printf("开户成功! 账户ID: %d\n", stNewAcc.id);
SaveData();
}
/* 函数功能:处理用户的显示所有用户信息的操作
思考处理步骤,写到函数体内注释
* 参数:无
* 从全局变量g_astAccounts中取信息,并显示
* 返回值:无
* 存款失败怎么处理?
/* 函数功能:处理用户的显示所有用户信息的操作
思考处理步骤,写到函数体内注释
* 参数:无
* 从全局变量g_astAccounts中取信息,并显示
* 返回值:无
* 存款失败怎么处理?
*/
void ShowAccounts()
{
//(1)输出用户内容提示
printf("\n所有账户信息:\n");
//(1)输出用户内容提示
printf("\n所有账户信息:\n");
//(2)检查是否有账户了?异常情况无账户的处理
//(2)检查是否有账户了?异常情况无账户的处理
if (0 == g_iAccCount)
{
printf("暂无账户信息\n");
printf("暂无账户信息\n");
return;
}
//(3)打印表头
printf("%-8s %-30s %-10s %-3s\n", "ID", "姓名", "余额","权限");
//(3)打印表头
printf("%-8s %-30s %-10s %-3s\n", "ID", "姓名", "余额","权限");
printf("------------------------------------------------------\n");
//(4)循环打印每一行
//(4)循环打印每一行
for(int i = 0; i < g_iAccCount; i++)
{
printf("%-8d %-30s %-10.2f %-10d\n",
@@ -99,58 +100,54 @@ void ShowAccounts()
}
}
//登录账户,返回权限
//登录账户,返回权限
int LoginAccount(int temp_id) {
int temp_password;
if (FindAccount(temp_id)==-1) {
printf("没有找到账户\n");
printf("没有找到账户\n");
return 0;
}
else {
while (1) {
printf("请输入密码");
printf("请输入密码");
scanf("%d", &temp_password);
if (g_astAccounts[FindAccount(temp_id)].password==temp_password) {
printf("欢迎用户%s \n",g_astAccounts[FindAccount(temp_id)].name);
printf("欢迎用户%s \n",g_astAccounts[FindAccount(temp_id)].name);
if (g_astAccounts[FindAccount(temp_id)].quanxian==1) {
printf("您目前以管理员身份登录");
printf("您目前以管理员身份登录");
return 1;
}
else {return 2;}
}
else {
printf("密码错误,请重新输入\n");
printf("密码错误,请重新输入\n");
}
}
}
}
/* 函数功能:根据输入的用户ID找到
思考处理步骤,写到函数体内注释
* 参数:无
* 用户ID跟数组ID
* 返回值:无
* 存款失败怎么处理?
/* 函数功能:根据输入的用户ID找到
思考处理步骤,写到函数体内注释
* 参数:无
* 用户ID跟数组ID
* 返回值:无
* 存款失败怎么处理?
*/
int FindAccount(int id)
{
for(int i = 0; i < g_iAccCount; i++)
{
if(g_astAccounts[i].id == id) return i;
}
return -1;
return HashSearch(id);
}
//删除账户
//删除账户
void DeleteAccount(int id) {
printf("正在删除\n");
printf("正在删除\n");
int i=FindAccount(id);
if (i==-1) {
printf("有这个账户吗你就删");
printf("有这个账户吗你就删");
}
else {
printf("正在删除一下账户...");
printf("正在删除一下账户...");
printf("%-8d %-30s %-10.2f %-3d\n",
g_astAccounts[i].id, g_astAccounts[i].name, g_astAccounts[i].balance,g_astAccounts[i].quanxian);
HashDelete(id);
for (; i < g_iAccCount-i; i++)
{
g_astAccounts[i]=g_astAccounts[i+1];
@@ -162,24 +159,24 @@ void DeleteAccount(int id) {
void ChangePassword(int id) {
int temp_password;
while (1) {
printf("请输入原密码");
printf("请输入原密码");
scanf("%d",&temp_password);
if (g_astAccounts[FindAccount(id)].password==temp_password) {
while (1) {
printf("请输入新密码\n");
printf("请输入新密码\n");
scanf("%d",&temp_password);
if (99999<temp_password&&temp_password<999999) {
printf("修改成功\n");
printf("修改成功\n");
g_astAccounts[FindAccount(id)].password=temp_password;
SaveData();
break;
}
printf("密码不符合规范!请重新输入\n");
printf("密码不符合规范!请重新输入\n");
}
}
else {
printf("密码错误,请重新输入");
printf("密码错误,请重新输入");
}
}
}
+105 -37
View File
@@ -2,7 +2,7 @@
#include <stdlib.h>
#include <string.h>
#include "bank.h"
LogQueue g_logQueue; // <-- 新增:定义全局日志队列
STACCOUNT g_astAccounts[MAX_ACCOUNTS];
int g_iAccCount = 1;
@@ -12,53 +12,55 @@ int d_id;
int main(void)
{
LoadData();
//(1)导入数据
//(1)导入数据
// which fuction?
//应该先导入数据再欢迎?还是欢迎了再导入数据?
printf("=== 简易银行账户管理系统 ===\n");
printf("欢迎使用! 系统已加载%d个账户\n", g_iAccCount);
InitAdminAccount();//初始化管理员账号
int iChoice=-1; //选择标志
//应该先导入数据再欢迎?还是欢迎了再导入数据?
printf("=== 简易银行账户管理系统 ===\n");
printf("欢迎使用! 系统已加载%d个账户\n", g_iAccCount);
InitAdminAccount();//初始化管理员账号
RebuildHashTable();
InitLogQueue();
int iChoice=-1; //选择标志
int pChoice=-1;
while (1) {
//持续显示菜单的秘诀
//(2)显示主菜单,并且处理用户的选择
//持续显示菜单的秘诀
//(2)显示主菜单,并且处理用户的选择
if (quanxian==-2){break;}
printf("\n=== 简易银行账户管理系统 ===\n");
printf("\n=== 简易银行账户管理系统 ===\n");
switch(quanxian) {
case 0:
printf("1. 开户\n2. 登录 \n0. 退出\n");
//printf("1. 开户\n2. 存款\n3. 取款\n4. 查询\n5. 显示所有账户\n6. 退出\n");
printf("请选择: ");
printf("1. 开户\n2. 登录 \n0. 退出\n");
//printf("1. 开户\n2. 存款\n3. 取款\n4. 查询\n5. 显示所有账户\n6. 退出\n");
printf("请选择: ");
iChoice=-1;
scanf("%d", &iChoice);
switch(iChoice) {
case 1://CREATE
//调用创建账号的函数
//调用创建账号的函数
CreateAccount(2);
quanxian=0;
break;
case 2://LOGIN
printf("请输入账户id");
printf("请输入账户id");
scanf("%d", &user_id);
quanxian=LoginAccount(user_id);
break;
case 0://EXIT
printf("谢谢使用!\n按任意键退出\n");
printf("谢谢使用!\n按任意键退出\n");
getchar();
quanxian=-2;
exit(0);
//还要实现哪些菜单吗?
//如果要做排序展示,要做什么?
default: //好的编码习惯,对无效值做处理
printf("无效选择!\n");
break; //好的编码加上break,万一default挪了个位置呢?
//还要实现哪些菜单吗?
//如果要做排序展示,要做什么?
default: //好的编码习惯,对无效值做处理
printf("无效选择!\n");
break; //好的编码加上break,万一default挪了个位置呢?
}
break;
case 1:
printf("1. 存款\n2. 取款\n3. 查询\n4. 显示所有账户\n5. 删除账户\n6. 创建管理员账户\n0. 退出账户");
printf("1. 存款\n2. 取款\n3. 查询\n4. 显示所有账户\n5. 删除账户\n6. 创建管理员账户\n7.查看交易日志\n8.搜索交易日志\n9.转账\n10.资金关系图分析\n0. 退出账户");
pChoice=-1;
scanf("%d", &pChoice);
@@ -76,28 +78,91 @@ int main(void)
ShowAccounts();
break;
case 5:
printf("请输入要删除的id");
printf("请输入要删除的id");
scanf("%d", &d_id);
DeleteAccount(d_id);
g_iAccCount--;
RebuildHashTable();
break;
case 6:
CreateAccount(1);
break;
case 7:
ShowTransactionLogs();
break;
case 8:
printf("请输入搜索内容");
{
char pattern[50];
scanf("%s", pattern);
SearchLogs(pattern);
}
break;
case 9:
Transfer(user_id);
break;
case 10:
{
int g_choice;
do {
printf("\n=== 资金关系图分析 ===\n");
printf("1. 显示全部转账关系图\n");
printf("2. 查看指定账户关联关系\n");
printf("3. 查看最活跃账户排名\n");
printf("4. 查找转账路径\n");
printf("5. 生成虚拟交易记录\n");
printf("0. 返回\n");
printf("请选择: ");
scanf("%d", &g_choice);
switch(g_choice) {
case 1:
ShowTransferGraph();
break;
case 2: {
int search_id;
printf("请输入账户ID: ");
scanf("%d", &search_id);
ShowAccountRelations(search_id);
break;
}
case 3:
ShowMostActiveNodes();
break;
case 4: {
int from, to;
printf("请输入起始账户ID: ");
scanf("%d", &from);
printf("请输入目标账户ID: ");
scanf("%d", &to);
FindTransferPaths(from, to);
break;
}
case 5:
GenerateDummyData();
break;
case 0:
break;
default:
printf("无效选择!\n");
break;
}
} while (g_choice != 0);
}
break;
case 0://EXIT
printf("谢谢使用!\n按任意键退出");
printf("谢谢使用!\n按任意键退出");
getchar();
quanxian=-2;
//还要实现哪些菜单吗?
//如果要做排序展示,要做什么?
default: //好的编码习惯,对无效值做处理
printf("无效选择!\n");
break; //好的编码加上break,万一default挪了个位置呢?
//还要实现哪些菜单吗?
//如果要做排序展示,要做什么?
default: //好的编码习惯,对无效值做处理
printf("无效选择!\n");
break; //好的编码加上break,万一default挪了个位置呢?
}
break;
case 2:
printf("1. 存款\n2. 取款\n3. 查询\n0.退出账户");
printf("1. 存款\n2. 取款\n3. 查询\n4.转账\n0.退出账户");
pChoice=-1;
scanf("%d", &pChoice);
switch(pChoice) {
@@ -110,23 +175,26 @@ int main(void)
case 3:
QueryBalance(user_id);
break;
case 4:
Transfer(user_id);
break;
case 0://EXIT
printf("谢谢使用!\n按任意键退出");
printf("谢谢使用!\n按任意键退出");
getchar();
quanxian=-2;
break;
//还要实现哪些菜单吗?
//如果要做排序展示,要做什么?
default: //好的编码习惯,对无效值做处理
printf("无效选择!\n");
break; //好的编码加上break,万一default挪了个位置呢?
//还要实现哪些菜单吗?
//如果要做排序展示,要做什么?
default: //好的编码习惯,对无效值做处理
printf("无效选择!\n");
break; //好的编码加上break,万一default挪了个位置呢?
}
break;
}
}
//(3)调用保存数据的函数
//(3)调用保存数据的函数
// which fuction?
return 0;
+72 -16
View File
@@ -1,10 +1,11 @@
#ifndef BANK_H
#define BANK_H
#define MAX_LOGS 100 // 日志队列最大容量,防止无限增长
#define MAX_ACCOUNTS 50 //系统规格为50个账户
#define NAME_LEN 31 //用户名字最长30字符,因此需要多1个字符串结束符
#define HASH_SIZE 101 //哈希表大小,取质数以减少冲突
#define MAX_ACCOUNTS 50 //系统规格为50个账户
#define NAME_LEN 31 //用户名字最长30字符,因此需要多1个字符串结束符
// 账户信息,每个账户id对应了名字、账户余额等信息,是不是用数据结构定义比较合适?
// 账户信息,每个账户id对应了名字、账户余额等信息,是不是用数据结构定义比较合适?
typedef struct strAccount
{
int id;
@@ -16,39 +17,94 @@ typedef struct strAccount
// strAccount STACCOUNT;
// 函数声明,main函数中调用的各模块函数需要在程序公用的.h中声明
// 公开出来被其他模块调用的函数,也应该在公用的.h中声明
// 哈希表条目,映射账户ID到g_astAccounts数组下标
typedef struct {
int id; // 账户ID-1表示空槽位
int index; // g_astAccounts中的下标
} HashEntry;
//创建账户的函数
extern HashEntry g_hashTable[HASH_SIZE];
// 哈希表操作
void InitHashTable();
int HashInsert(int id, int index);
int HashSearch(int id);
void HashDelete(int id);
void RebuildHashTable();
// 函数声明,main函数中调用的各模块函数需要在程序公用的.h中声明
// 公开出来被其他模块调用的函数,也应该在公用的.h中声明
//创建账户的函数
void CreateAccount(int quanxian);
//实现存款的函数
//实现存款的函数
void Deposit(int id);
//实现取款的函数
//实现取款的函数
void Withdraw(int id);
//查询余额的函数
//查询余额的函数
void QueryBalance(int id);
//显示全部账户的函数
//显示全部账户的函数
void ShowAccounts();
//数据持久化函数
//数据持久化函数
void SaveData();
//加载数据的函数
//加载数据的函数
void LoadData();
//根据ID查找账户数组id的函数
//根据ID查找账户数组id的函数
int FindAccount(int iD);
//登录函数,返回用户权限
//登录函数,返回用户权限
int LoginAccount(int temp_id);
//销户
//销户
void DeleteAccount(int id);
void InitAdminAccount();
// 日志条目结构
typedef struct {
int account_id; // 涉及的账户ID
int target_id; // 转账目标账户ID,非转账为-1
char type[10]; // 操作类型: "Deposit","Withdraw","Transfer","Receive"
double amount; // 交易金额
double balance; // 交易后的余额
char timestamp[20]; // 时间戳 (格式: YYYY-MM-DD HH:MM:SS)
char location[50]; // 地点信息
} LogEntry;
// 环形队列结构
typedef struct {
LogEntry logs[MAX_LOGS];
int front; // 队头指针
int rear; // 队尾指针
} LogQueue;
// 全局日志队列变量 (在 bank.c 中定义,在其他文件中 extern 引用)
extern LogQueue g_logQueue;
// 日志函数声明
void InitLogQueue(); // 初始化队列
int IsLogQueueFull(); // 检查队列是否满
int IsLogQueueEmpty(); // 检查队列是否空
int EnqueueLog(int id, const char* type, double amt, double bal, int target); // 入队
void ShowTransactionLogs(); // 显示所有日志
void SearchLogs(const char* pattern);//日志搜索
// 转账功能
void Transfer(int from_id);
// 资金关系图分析
void ShowTransferGraph();
void ShowAccountRelations(int id);
void ShowMostActiveNodes();
void FindTransferPaths(int from_id, int to_id);
void GenerateDummyData();
#endif
+52
View File
@@ -0,0 +1,52 @@
#include <stdio.h>
#include <stdlib.h>
#include "bank.h"
//要操作的全局变量,但不在本模块定义需要声明为外部引入
extern STACCOUNT g_astAccounts[];
extern int g_iAccCount;
/* 函数功能:数据持久化,将内存中的账户数据保存到文件中
保存方式为二进制
保存到文件bank.bat中,该文件名只在本模块用使用
* 参数:无
* 保存信息来自全局变量g_astAccounts
* 返回值:无
* 保存是失败怎么处理?
*/
void SaveData()
{
FILE *file = fopen("bank.dat", "wb");
fwrite(g_astAccounts, sizeof(STACCOUNT), 50, file);
fclose(file);
LoadData();
}
/* 函数功能:从文件bank.bat中把账户数据读取到内存
操作的文件名仅在本模块中使用
* 参数:无
* 信息读取后放入全局变量g_astAccounts
* 返回值:无
* 读取失败怎么处理?
*/
void LoadData()
{
int i;
int result=0;
FILE *file = fopen("bank.dat", "rb");
if (file == NULL) {
InitAdminAccount();
g_iAccCount= 1;
}
else {
fread(g_astAccounts, sizeof(STACCOUNT), 50, file);
}
fclose( file);
for (i = 0; i < 50; i++) {
if (g_astAccounts[i].id != 0) {
result++;
}
}
g_iAccCount= result;
}
+354
View File
@@ -0,0 +1,354 @@
#include <stdio.h>
#include <string.h>
#include "bank.h"
extern LogQueue g_logQueue;
extern STACCOUNT g_astAccounts[];
extern int g_iAccCount;
// 从转账日志构建邻接矩阵,matrix[i][j] = 账户i向账户j的累计转账金额
static void BuildAdjMatrix(int matrix[MAX_ACCOUNTS][MAX_ACCOUNTS])
{
int i, j;
for (i = 0; i < g_iAccCount; i++)
for (j = 0; j < g_iAccCount; j++)
matrix[i][j] = 0;
if (IsLogQueueEmpty()) return;
int idx = g_logQueue.front;
while (idx != g_logQueue.rear) {
LogEntry e = g_logQueue.logs[idx];
if (strcmp(e.type, "Transfer") == 0 && e.target_id > 0) {
int from_idx = FindAccount(e.account_id);
int to_idx = FindAccount(e.target_id);
if (from_idx != -1 && to_idx != -1) {
matrix[from_idx][to_idx] += (int)e.amount;
}
}
idx = (idx + 1) % MAX_LOGS;
}
}
// 显示全部转账关系图
void ShowTransferGraph()
{
int matrix[MAX_ACCOUNTS][MAX_ACCOUNTS];
BuildAdjMatrix(matrix);
printf("\n=== 客户资金关系图 ===\n");
printf("(连线表示转账关系,数字为累计转账金额)\n\n");
int has_transfer = 0;
int i, j;
for (i = 0; i < g_iAccCount; i++) {
for (j = 0; j < g_iAccCount; j++) {
if (matrix[i][j] > 0) {
has_transfer = 1;
printf("%s(%d) --[%d元]--> %s(%d)\n",
g_astAccounts[i].name, g_astAccounts[i].id,
matrix[i][j],
g_astAccounts[j].name, g_astAccounts[j].id);
}
}
}
if (!has_transfer) {
printf("暂无转账记录,无法构建关系图。\n");
}
}
// 查看指定账户的关联关系
void ShowAccountRelations(int id)
{
int idx = FindAccount(id);
if (idx == -1) {
printf("账户不存在!\n");
return;
}
int matrix[MAX_ACCOUNTS][MAX_ACCOUNTS];
BuildAdjMatrix(matrix);
printf("\n=== 账户关系分析: %s(%d) ===\n",
g_astAccounts[idx].name, g_astAccounts[idx].id);
int j;
int out_count = 0;
int out_total = 0;
printf("\n[转出记录]\n");
for (j = 0; j < g_iAccCount; j++) {
if (matrix[idx][j] > 0) {
printf(" -> %s(%d): %d元\n",
g_astAccounts[j].name, g_astAccounts[j].id, matrix[idx][j]);
out_total += matrix[idx][j];
out_count++;
}
}
if (out_count == 0) printf(" 无转出记录\n");
else printf(" 共转出 %d 笔, 合计 %d 元\n", out_count, out_total);
int i;
int in_count = 0;
int in_total = 0;
printf("\n[转入记录]\n");
for (i = 0; i < g_iAccCount; i++) {
if (matrix[i][idx] > 0) {
printf(" <- %s(%d): %d元\n",
g_astAccounts[i].name, g_astAccounts[i].id, matrix[i][idx]);
in_total += matrix[i][idx];
in_count++;
}
}
if (in_count == 0) printf(" 无转入记录\n");
else printf(" 共转入 %d 笔, 合计 %d 元\n", in_count, in_total);
printf("\n总结: 转出%d笔(共%d元), 转入%d笔(共%d元)\n",
out_count, out_total, in_count, in_total);
}
// 查看最活跃账户排名(按转账关系数量排序)
void ShowMostActiveNodes()
{
int matrix[MAX_ACCOUNTS][MAX_ACCOUNTS];
BuildAdjMatrix(matrix);
int degrees[MAX_ACCOUNTS];
int account_ids[MAX_ACCOUNTS];
int i, j;
int count = 0;
for (i = 0; i < g_iAccCount; i++) {
int deg = 0;
for (j = 0; j < g_iAccCount; j++) {
if (matrix[i][j] > 0) deg++;
if (matrix[j][i] > 0) deg++;
}
if (deg > 0) {
degrees[count] = deg;
account_ids[count] = i;
count++;
}
}
if (count == 0) {
printf("\n暂无转账关系数据。\n");
return;
}
// 冒泡排序(最多50个元素)
for (i = 0; i < count - 1; i++) {
for (j = 0; j < count - 1 - i; j++) {
if (degrees[j] < degrees[j + 1]) {
int tmp_d = degrees[j];
degrees[j] = degrees[j + 1];
degrees[j + 1] = tmp_d;
int tmp_id = account_ids[j];
account_ids[j] = account_ids[j + 1];
account_ids[j + 1] = tmp_id;
}
}
}
printf("\n=== 最活跃账户排名 ===\n");
printf("%-4s %-8s %-20s %-10s\n", "排名", "ID", "姓名", "关系数");
printf("-----------------------------------------\n");
int top = count < 10 ? count : 10;
for (i = 0; i < top; i++) {
int idx = account_ids[i];
printf("%-4d %-8d %-20s %-10d\n",
i + 1, g_astAccounts[idx].id,
g_astAccounts[idx].name, degrees[i]);
}
}
// 用BFS查找两个账户间的最短转账路径
void FindTransferPaths(int from_id, int to_id)
{
int from_idx = FindAccount(from_id);
int to_idx = FindAccount(to_id);
if (from_idx == -1) { printf("源账户不存在!\n"); return; }
if (to_idx == -1) { printf("目标账户不存在!\n"); return; }
if (from_id == to_id) { printf("同一账户无需查找路径。\n"); return; }
int matrix[MAX_ACCOUNTS][MAX_ACCOUNTS];
BuildAdjMatrix(matrix);
// BFS
int visited[MAX_ACCOUNTS] = {0};
int parent[MAX_ACCOUNTS];
int queue[MAX_ACCOUNTS];
int q_front = 0, q_rear = 0;
int i;
for (i = 0; i < MAX_ACCOUNTS; i++) parent[i] = -1;
queue[q_rear++] = from_idx;
visited[from_idx] = 1;
int found = 0;
while (q_front < q_rear) {
int curr = queue[q_front++];
if (curr == to_idx) { found = 1; break; }
int j;
for (j = 0; j < g_iAccCount; j++) {
if (matrix[curr][j] > 0 && !visited[j]) {
visited[j] = 1;
parent[j] = curr;
queue[q_rear++] = j;
}
}
}
if (!found) {
printf("未找到从 %d 到 %d 的转账路径。\n", from_id, to_id);
return;
}
// 回溯路径
int path[MAX_ACCOUNTS];
int path_len = 0;
int curr = to_idx;
while (curr != -1) {
path[path_len++] = curr;
curr = parent[curr];
}
printf("\n=== 转账路径: %s(%d) -> %s(%d) ===\n",
g_astAccounts[from_idx].name, from_id,
g_astAccounts[to_idx].name, to_id);
printf("最短路径长度: %d 步\n", path_len - 1);
for (i = path_len - 1; i >= 0; i--) {
printf("%s(%d)", g_astAccounts[path[i]].name, g_astAccounts[path[i]].id);
if (i > 0) {
printf(" --[%d元]--> ", matrix[path[i]][path[i - 1]]);
}
}
printf("\n");
}
// 生成虚拟交易记录,用于测试资金关系图分析
void GenerateDummyData()
{
int i;
int orig_count = g_iAccCount;
// 如果账户不足5个,先创建测试账户(密码统一123456)
if (g_iAccCount < 5 && g_iAccCount < MAX_ACCOUNTS) {
int target = 5;
if (target > MAX_ACCOUNTS) target = MAX_ACCOUNTS;
for (i = g_iAccCount; i < target; i++) {
g_astAccounts[i].id = 1000 + i;
g_astAccounts[i].balance = (i + 1) * 5000.0;
g_astAccounts[i].password = 123456;
g_astAccounts[i].quanxian = 2;
g_astAccounts[i].name[0] = '\0';
}
// 给账户起中文名
if (orig_count <= 1) strcpy(g_astAccounts[1].name, "张三");
if (orig_count <= 2) strcpy(g_astAccounts[2].name, "李四");
if (orig_count <= 3) strcpy(g_astAccounts[3].name, "王五");
if (orig_count <= 4) strcpy(g_astAccounts[4].name, "赵六");
g_iAccCount = target;
SaveData();
printf("已补充创建%d个测试账户\n", target - orig_count);
}
// 如果有第6个槽位,创建一个钱七
if (g_iAccCount < 6 && g_iAccCount < MAX_ACCOUNTS) {
i = g_iAccCount;
g_astAccounts[i].id = 1000 + i;
strcpy(g_astAccounts[i].name, "钱七");
g_astAccounts[i].balance = 20000.0;
g_astAccounts[i].password = 123456;
g_astAccounts[i].quanxian = 2;
g_iAccCount++;
SaveData();
printf("已创建测试账户: 钱七(1005)\n");
}
// 清空旧日志,生成新的虚拟交易流水
InitLogQueue();
int id[6];
for (i = 0; i < g_iAccCount && i < 6; i++)
id[i] = g_astAccounts[i].id;
// 为每个账户存入初始资金(留痕)
for (i = 1; i < g_iAccCount && i < 6; i++) {
double bal = g_astAccounts[i].balance;
EnqueueLog(id[i], "Deposit", bal, bal, -1);
}
// 构建一个互联的转账网络:
// 张三 -> 李四 3000
// 李四 -> 王五 2000
// 王五 -> 赵六 1500
// 赵六 -> 钱七 1000
// 钱七 -> 张三 800 (形成闭环,可测路径查找)
// 张三 -> 王五 1200 (增加多条边)
// 李四 -> 钱七 600
if (g_iAccCount >= 2) {
// 张三(1) -> 李四(2): 3000
g_astAccounts[1].balance -= 3000;
g_astAccounts[2].balance += 3000;
EnqueueLog(id[1], "Transfer", 3000, g_astAccounts[1].balance, id[2]);
EnqueueLog(id[2], "Receive", 3000, g_astAccounts[2].balance, id[1]);
}
if (g_iAccCount >= 3) {
// 李四(2) -> 王五(3): 2000
g_astAccounts[2].balance -= 2000;
g_astAccounts[3].balance += 2000;
EnqueueLog(id[2], "Transfer", 2000, g_astAccounts[2].balance, id[3]);
EnqueueLog(id[3], "Receive", 2000, g_astAccounts[3].balance, id[2]);
// 张三(1) -> 王五(3): 1200
g_astAccounts[1].balance -= 1200;
g_astAccounts[3].balance += 1200;
EnqueueLog(id[1], "Transfer", 1200, g_astAccounts[1].balance, id[3]);
EnqueueLog(id[3], "Receive", 1200, g_astAccounts[3].balance, id[1]);
}
if (g_iAccCount >= 4) {
// 王五(3) -> 赵六(4): 1500
g_astAccounts[3].balance -= 1500;
g_astAccounts[4].balance += 1500;
EnqueueLog(id[3], "Transfer", 1500, g_astAccounts[3].balance, id[4]);
EnqueueLog(id[4], "Receive", 1500, g_astAccounts[4].balance, id[3]);
}
if (g_iAccCount >= 5) {
// 赵六(4) -> 钱七(5): 1000
g_astAccounts[4].balance -= 1000;
g_astAccounts[5].balance += 1000;
EnqueueLog(id[4], "Transfer", 1000, g_astAccounts[4].balance, id[5]);
EnqueueLog(id[5], "Receive", 1000, g_astAccounts[5].balance, id[4]);
// 钱七(5) -> 张三(1): 800 (闭环)
g_astAccounts[5].balance -= 800;
g_astAccounts[1].balance += 800;
EnqueueLog(id[5], "Transfer", 800, g_astAccounts[5].balance, id[1]);
EnqueueLog(id[1], "Receive", 800, g_astAccounts[1].balance, id[5]);
// 李四(2) -> 钱七(5): 600
g_astAccounts[2].balance -= 600;
g_astAccounts[5].balance += 600;
EnqueueLog(id[2], "Transfer", 600, g_astAccounts[2].balance, id[5]);
EnqueueLog(id[5], "Receive", 600, g_astAccounts[5].balance, id[2]);
}
// 为部分账户生成取款记录
if (g_iAccCount >= 3) {
g_astAccounts[1].balance -= 500;
EnqueueLog(id[1], "Withdraw", 500, g_astAccounts[1].balance, -1);
g_astAccounts[3].balance -= 300;
EnqueueLog(id[3], "Withdraw", 300, g_astAccounts[3].balance, -1);
}
SaveData();
printf("已生成 %d 条虚拟交易记录\n",
(g_logQueue.rear - g_logQueue.front + MAX_LOGS) % MAX_LOGS);
printf("账户数量: %d\n", g_iAccCount);
printf("转账网络已构建: 张三->李四->王五->赵六->钱七->张三(闭环)\n");
}
+91
View File
@@ -0,0 +1,91 @@
#include <stdio.h>
#include "bank.h"
extern STACCOUNT g_astAccounts[];
extern int g_iAccCount;
HashEntry g_hashTable[HASH_SIZE];
static int HashFunc(int id)
{
return id % HASH_SIZE;
}
void InitHashTable()
{
int i;
for (i = 0; i < HASH_SIZE; i++) {
g_hashTable[i].id = -1;
g_hashTable[i].index = -1;
}
}
int HashInsert(int id, int index)
{
int pos = HashFunc(id);
int first_tombstone = -1;
int i;
for (i = 0; i < HASH_SIZE; i++) {
int probe = (pos + i) % HASH_SIZE;
if (g_hashTable[probe].id == id) {
g_hashTable[probe].index = index;
return 0;
}
if (g_hashTable[probe].id == -1) {
if (first_tombstone != -1) {
probe = first_tombstone;
}
g_hashTable[probe].id = id;
g_hashTable[probe].index = index;
return 0;
}
if (g_hashTable[probe].id == -2 && first_tombstone == -1) {
first_tombstone = probe;
}
}
return -1;
}
int HashSearch(int id)
{
int pos = HashFunc(id);
int i;
for (i = 0; i < HASH_SIZE; i++) {
int probe = (pos + i) % HASH_SIZE;
if (g_hashTable[probe].id == id) {
return g_hashTable[probe].index;
}
if (g_hashTable[probe].id == -1) {
return -1;
}
}
return -1;
}
void HashDelete(int id)
{
int pos = HashFunc(id);
int i;
for (i = 0; i < HASH_SIZE; i++) {
int probe = (pos + i) % HASH_SIZE;
if (g_hashTable[probe].id == -1) {
return;
}
if (g_hashTable[probe].id == id) {
g_hashTable[probe].id = -2;
g_hashTable[probe].index = -1;
return;
}
}
}
void RebuildHashTable()
{
int i;
InitHashTable();
for (i = 0; i < g_iAccCount; i++) {
if (g_astAccounts[i].id != 0) {
HashInsert(g_astAccounts[i].id, i);
}
}
}
+183
View File
@@ -0,0 +1,183 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "bank.h"
// 初始化日志队列
void InitLogQueue() {
g_logQueue.front = 0;
g_logQueue.rear = 0;
}
// 检查队列是否满
int IsLogQueueFull() {
return (g_logQueue.rear + 1) % MAX_LOGS == g_logQueue.front;
}
// 检查队列是否空
int IsLogQueueEmpty() {
return g_logQueue.front == g_logQueue.rear;
}
// 获取当前时间字符串
void GetCurrentTimeStr(char* buf) {
time_t t = time(NULL);
struct tm tm = *localtime(&t);
sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
}
// 入队操作:记录交易
// 参数: 账户ID, 类型("Deposit"/"Withdraw"), 交易金额, 交易后余额
int EnqueueLog(int id, const char* type, double amt, double bal, int target) {
// 如果队列满,通过移动 front 指针覆盖最旧的日志 (环形队列特性)
if (IsLogQueueFull()) {
// 覆盖旧数据,队头自动出队
g_logQueue.front = (g_logQueue.front + 1) % MAX_LOGS;
}
// 准备日志条目
int pos = g_logQueue.rear;
g_logQueue.logs[pos].account_id = id;
strcpy(g_logQueue.logs[pos].type, type);
g_logQueue.logs[pos].amount = amt;
g_logQueue.logs[pos].balance = bal;
g_logQueue.logs[pos].target_id = target;
GetCurrentTimeStr(g_logQueue.logs[pos].timestamp);
strcpy(g_logQueue.logs[pos].location, "宇宙总行"); // 根据你的需求固定地点
// 队尾指针后移
g_logQueue.rear = (g_logQueue.rear + 1) % MAX_LOGS;
return 1; // 成功
}
// 显示所有日志 (从队头到队尾)
void ShowTransactionLogs() {
if (IsLogQueueEmpty()) {
printf("暂无交易日志记录。\n");
return;
}
printf("\n=== 交易流水日志 ===\n");
printf("%-5s %-8s %-10s %-10s %-10s %-15s %-20s\n", "ID", "操作", "金额", "余额", "目标", "时间", "地点");
printf("-----------------------------------------------------------------------\n");
int i = g_logQueue.front;
while (i != g_logQueue.rear) {
LogEntry e = g_logQueue.logs[i];
printf("%-5d %-8s %-10.2f %-10.2f ", e.account_id, e.type, e.amount, e.balance);
if (e.target_id > 0)
printf("%-10d ", e.target_id);
else
printf("%-10s ", "-");
printf("%-15s %-20s\n", e.timestamp, e.location);
i = (i + 1) % MAX_LOGS;
}
}
// 1. 构建部分匹配表 (Next Array)
// 这是KMP算法的核心预处理步骤
void ComputeLPSArray(const char* pattern, int M, int* lps) {
int len = 0; // length of the previous longest prefix suffix
lps[0] = 0; // lps[0] is always 0
int i = 1;
while (i < M) {
if (pattern[i] == pattern[len]) {
len++;
lps[i] = len;
i++;
} else {
if (len != 0) {
len = lps[len - 1];
} else {
lps[i] = 0;
i++;
}
}
}
}
// 2. KMP 搜索函数
// 在文本 text 中查找模式 pattern,找到后调用回调函数处理匹配位置
void KMPSearch(const char* pattern, const char* text, void (*callback)(int)) {
int M = strlen(pattern);
int N = strlen(text);
if (M == 0) return;
// 创建并计算LPS数组
int* lps = (int*)malloc(M * sizeof(int));
ComputeLPSArray(pattern, M, lps);
int i = 0; // index for text
int j = 0; // index for pattern
while (i < N) {
if (pattern[j] == text[i]) {
j++;
i++;
}
if (j == M) {
// 找到匹配,调用回调函数输出该行或处理
callback(i - j);
j = lps[j - 1];
} else if (i < N && pattern[j] != text[i]) {
if (j != 0) {
j = lps[j - 1];
} else {
i++;
}
}
}
free(lps);
}
// 3. 回调函数:用于打印匹配到的日志行号
void PrintMatchLine(int pos) {
// 这里简化处理,实际应用中需要根据换行符计算行号
// 或者直接打印匹配位置附近的上下文
printf("找到匹配 (位置: %d)\n", pos);
}
// 4. 对外接口:搜索交易日志
void SearchLogs(const char* pattern) {
if (IsLogQueueEmpty()) {
printf("暂无日志可供搜索。\n");
return;
}
printf("\n=== 搜索日志: '%s' ===\n", pattern);
// 简化版:遍历每一条日志进行匹配
int i = g_logQueue.front;
int lineNum = 1;
while (i != g_logQueue.rear) {
LogEntry e = g_logQueue.logs[i];
// 将日志条目格式化为字符串(模拟一行文本)
char logLine[256];
sprintf(logLine, "ID:%d Type:%s Amount:%.2f Balance:%.2f Target:%d Time:%s",
e.account_id, e.type, e.amount, e.balance, e.target_id, e.timestamp);
// 使用KMP检查这一行是否包含模式
// 这里为了演示调用了标准 strstr,你可以将其替换为上面的 KMPSearch
// 但考虑到单行文本较短,KMP优势不大;如果是超长日志文件,KMP优势巨大
if (strstr(logLine, pattern) != NULL) {
printf("%-3d %-8d %-10s %-10.2f %-15s",
lineNum, e.account_id, e.type, e.amount, e.timestamp);
if (e.target_id > 0) printf(" -> %d", e.target_id);
printf("\n");
}
i = (i + 1) % MAX_LOGS;
lineNum++;
}
}
+85 -28
View File
@@ -5,96 +5,153 @@
extern STACCOUNT g_astAccounts[];
extern int g_iAccCount;
/* 函数功能:处理用户的存款操作
需要什么步骤?
* 参数:无
* 存款信息要写入全局变量g_astAccounts中
* 返回值:无
* 存款失败怎么处理?
/* 函数功能:处理用户的存款操作
需要什么步骤?
* 参数:无
* 存款信息要写入全局变量g_astAccounts中
* 返回值:无
* 存款失败怎么处理?
*/
void Deposit(int id)
{
int amount;
int temp_password;
while (1) {
printf("请输入存款金额");
printf("请输入存款金额");
scanf("%d",&amount);
if (amount>0) {
break;
}
printf("?你存给我存个负的?重新说存多少");
printf("?你存给我存个负的?重新说存多少");
}
printf("请输入密码");
printf("请输入密码");
while (1) {
scanf("%d",&temp_password);
if (g_astAccounts[FindAccount(id)].password==temp_password) {
g_astAccounts[FindAccount(id)].balance+=amount;
printf("success");
QueryBalance(id);
EnqueueLog(id, "Deposit", amount, g_astAccounts[FindAccount(id)].balance, -1);
SaveData();
break;
}
else
{
{
printf("密码错误,请重新输入");
printf("密码错误,请重新输入");
}
}
}
}
/* 函数功能:处理用户的取款操作
需要什么步骤?
* 参数:无
* 取款后,账户信息要写入全局变量g_astAccounts中
* 返回值:无
* 取款失败怎么处理?
/* 函数功能:处理用户的取款操作
需要什么步骤?
* 参数:无
* 取款后,账户信息要写入全局变量g_astAccounts中
* 返回值:无
* 取款失败怎么处理?
*/
void Withdraw(int id)
{
int amount;
int temp_password;
while (1) {
printf("请输入取款金额");
printf("请输入取款金额");
scanf("%d",&amount);
if (amount>0) {
break;
}
printf("?取款怎么能取负的呢");
printf("?取款怎么能取负的呢");
}
while (1) {
printf("请输入密码");
printf("请输入密码");
scanf("%d",&temp_password);
if (g_astAccounts[FindAccount(id)].password==temp_password) {
if (g_astAccounts[FindAccount(id)].balance>amount) {
g_astAccounts[FindAccount(id)].balance-=amount;
EnqueueLog(id, "Withdraw", amount, g_astAccounts[FindAccount(id)].balance, -1);
printf("success");
QueryBalance(id);
SaveData();
return;
}
else {
printf("有那么多钱吗你就取?\n");
printf("有那么多钱吗你就取?\n");
QueryBalance(id);
return;
}
}
else {
printf("密码错误,请重新输入");
printf("密码错误,请重新输入");
}
}
}
/* 函数功能:处理用户查询余额操作
需要什么步骤?
* 参数:无
* 查询后全局变量是否需要变化?
* 返回值:无
* 查询失败怎么处理?
/* 函数功能:处理用户查询余额操作
需要什么步骤?
* 参数:无
* 查询后全局变量是否需要变化?
* 返回值:无
* 查询失败怎么处理?
*/
void QueryBalance(int id)
{
printf("你现在还有%f块\n",g_astAccounts[FindAccount(id)].balance);
printf("你现在还有%f块\n",g_astAccounts[FindAccount(id)].balance);
return;
}
void Transfer(int from_id)
{
int to_id;
int amount;
int temp_password;
int from_idx = FindAccount(from_id);
int to_idx;
printf("请输入目标账户ID: ");
scanf("%d", &to_id);
to_idx = FindAccount(to_id);
if (to_idx == -1) {
printf("目标账户不存在!\n");
return;
}
if (from_id == to_id) {
printf("不能给自己转账!\n");
return;
}
while (1) {
printf("请输入转账金额: ");
scanf("%d", &amount);
if (amount > 0) break;
printf("金额必须为正数!\n");
}
printf("请输入密码验证身份: ");
while (1) {
scanf("%d", &temp_password);
if (g_astAccounts[from_idx].password == temp_password) {
if (g_astAccounts[from_idx].balance >= amount) {
g_astAccounts[from_idx].balance -= amount;
g_astAccounts[to_idx].balance += amount;
EnqueueLog(from_id, "Transfer", amount,
g_astAccounts[from_idx].balance, to_id);
EnqueueLog(to_id, "Receive", amount,
g_astAccounts[to_idx].balance, from_id);
printf("转账成功! %d -> %d, 金额: %d\n",
from_id, to_id, amount);
printf("当前余额: %.2f\n", g_astAccounts[from_idx].balance);
SaveData();
return;
} else {
printf("余额不足! 当前余额: %.2f\n",
g_astAccounts[from_idx].balance);
return;
}
} else {
printf("密码错误,请重新输入: ");
}
}
}
+2 -2
View File
@@ -13,7 +13,7 @@ int GetInputInt(const char *prompt)
printf("%s", prompt);
while(scanf("%d", &value) != 1) {
ClearInput();
printf("输入错误,请重新输入: ");
printf("输入错误,请重新输入: ");
}
return value;
}
@@ -24,7 +24,7 @@ double GetInputDouble(const char *prompt)
printf("%s", prompt);
while(scanf("%lf", &value) != 1) {
ClearInput();
printf("输入错误,请重新输入: ");
printf("输入错误,请重新输入: ");
}
return value;
}