From bb21a0477160e3e25a3899962031a07802b73bcd Mon Sep 17 00:00:00 2001 From: Drunk-Youth Date: Mon, 15 Jun 2026 16:36:36 +0800 Subject: [PATCH] graph --- .gitignore | 2 + AGENTS.md | 40 ++++++ README.md | 20 ++- bank.c | 60 ++++++++- bank.h | 15 ++- file_io.c | 3 - graph.c | 354 ++++++++++++++++++++++++++++++++++++++++++++++++++ log.c | 25 ++-- transaction.c | 59 ++++++++- 9 files changed, 558 insertions(+), 20 deletions(-) create mode 100644 AGENTS.md create mode 100644 graph.c diff --git a/.gitignore b/.gitignore index 1f51ab1..d727dd6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ bank.dat .vscode/launch.json .vscode/tasks.json +a.out +bank_system diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..c93ce62 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,40 @@ +# BankManager — C 语言银行账户管理系统 + +## 构建 & 运行 + +```sh +gcc bank.c account.c transaction.c file_io.c utils.c log.c graph.c -o bank_system +./bank_system +``` + +`bank.h` 包含所有结构体定义、函数声明、全局常量。所有 `.c` 的编译顺序无要求。 + +## 项目布局 + +所有源文件在仓库根目录,无 `src/` 子目录。无包管理器、无构建系统、无测试框架。 + +| 文件 | 职责 | +|------|------| +| `bank.c` | 主循环、菜单、权限路由 | +| `account.c` | 开户、销户、登录、查询全部账户 | +| `transaction.c` | 存款、取款、查余额、转账 | +| `file_io.c` | 二进制文件读写 (bank.dat) | +| `utils.c` | 输入校验工具 | +| `log.c` | 环形队列日志,支持按关键词搜索 | +| `graph.c` | 从转账日志建图,分析客户间资金来往关系 | +| `bank.h` | 唯一头文件,声明所有公有函数和类型 | + +## 重要约束 + +- **Max 50 账户** (`MAX_ACCOUNTS`),全局数组 `g_astAccounts`,不使用 `malloc`。 +- **默认管理员**: ID=1000, name="admin", password=123456, permission=1。系统首次运行时自动创建。 +- **密码**: 纯 6 位数字。 +- **权限**: 1=管理员,2=普通用户。管理员多出显示全部、删账户、创管理员、看日志功能。 +- **数据持久化**: 二进制文件 `bank.dat`(已加入 `.gitignore`)。 +- **全局日志队列**: 环形队列 `g_logQueue`,最多 100 条 (`MAX_LOGS`),溢出覆盖旧记录。 + +## 代码约定 + +- 中文注释和中文菜单字符串。 +- 全局变量命名前缀 `g_`(如 `g_iAccCount`, `g_astAccounts`)。 +- 无测试;曾有测试文件但已被删除(commit `ce8abe3`)。 diff --git a/README.md b/README.md index 0411f9d..9f7e089 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,13 @@ - 存款 - 取款 - 查询余额 + - 转账(账户间资金转移) - **系统功能** - 用户登录验证 - 权限分级(管理员/普通用户) - 数据持久化(自动保存到文件) - 交易流水查询(管理员) + - 资金关系图分析(管理员):从转账日志建图,分析客户间资金来往关系 - 自动初始化默认管理员账户 ## 系统架构 @@ -38,6 +40,7 @@ - 存款功能 - 取款功能 - 余额查询 + - 转账功能(账户间资金转移) 4. **数据持久化 (file_io.c)** - 数据保存到文件(bank.dat) @@ -54,7 +57,14 @@ - 函数声明 7. **日志模块(log.c)** - - 日志记录和存储(循环链表) + - 日志记录和存储(环形队列) + +8. **资金关系图分析(graph.c)** + - 从转账日志构建邻接矩阵 + - 显示全部转账关系图 + - 查看指定账户关联关系 + - 最活跃账户排名(度中心性) + - BFS最短转账路径查找 ### 数据结构 ```c @@ -71,7 +81,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 -o bank_system ``` ### 运行 @@ -89,6 +99,7 @@ gcc bank.c account.c transaction.c file_io.c utils.c -o bank_system - 选项1:存款 - 选项2:取款 - 选项3:查询余额 + - 选项4:转账 - 选项0:退出账户 3. **管理员界面**(登录后) @@ -99,6 +110,9 @@ gcc bank.c account.c transaction.c file_io.c utils.c -o bank_system - 选项5:删除账户 - 选项6:创建管理员账户 - 选项7:查看交易流水 + - 选项8:搜索交易日志 + - 选项9:转账 + - 选项10:资金关系图分析 - 选项0:退出账户 ### 默认账户 @@ -121,6 +135,8 @@ 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` - 资金关系图分析模块 - `bank.h` - 头文件,包含结构体定义和函数声明 - `bank.dat` - 数据存储文件(运行时自动生成) diff --git a/bank.c b/bank.c index c2b541a..bd146df 100644 --- a/bank.c +++ b/bank.c @@ -59,7 +59,7 @@ int main(void) } break; case 1: - printf("1. 存款\n2. 取款\n3. 查询\n4. 显示所有账户\n5. 删除账户\n6. 创建管理员账户\n7.查看交易日志\n8.搜索交易日志\n0. 退出账户"); + printf("1. 存款\n2. 取款\n3. 查询\n4. 显示所有账户\n5. 删除账户\n6. 创建管理员账户\n7.查看交易日志\n8.搜索交易日志\n9.转账\n10.资金关系图分析\n0. 退出账户"); pChoice=-1; scanf("%d", &pChoice); @@ -90,9 +90,62 @@ int main(void) 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按任意键退出"); @@ -107,7 +160,7 @@ int main(void) } break; case 2: - printf("1. 存款\n2. 取款\n3. 查询\n0.退出账户"); + printf("1. 存款\n2. 取款\n3. 查询\n4.转账\n0.退出账户"); pChoice=-1; scanf("%d", &pChoice); switch(pChoice) { @@ -120,6 +173,9 @@ int main(void) case 3: QueryBalance(user_id); break; + case 4: + Transfer(user_id); + break; case 0://EXIT printf("谢谢使用!\n按任意键退出"); getchar(); diff --git a/bank.h b/bank.h index 805ca03..dd92b4f 100644 --- a/bank.h +++ b/bank.h @@ -54,7 +54,8 @@ void InitAdminAccount(); // 日志条目结构 typedef struct { int account_id; // 涉及的账户ID - char type[10]; // 操作类型: "Deposit" 或 "Withdraw" + 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) @@ -75,9 +76,19 @@ extern LogQueue g_logQueue; void InitLogQueue(); // 初始化队列 int IsLogQueueFull(); // 检查队列是否满 int IsLogQueueEmpty(); // 检查队列是否空 -int EnqueueLog(int id, const char* type, double amt, double bal); // 入队 +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 diff --git a/file_io.c b/file_io.c index ba986d2..1158f7c 100644 --- a/file_io.c +++ b/file_io.c @@ -1,9 +1,6 @@ #include #include #include "bank.h" -#include - -#define FILE_NAME bank.bat //要操作的全局变量,但不在本模块定义需要声明为外部引入 extern STACCOUNT g_astAccounts[]; diff --git a/graph.c b/graph.c new file mode 100644 index 0000000..a0b81dd --- /dev/null +++ b/graph.c @@ -0,0 +1,354 @@ +#include +#include +#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"); +} diff --git a/log.c b/log.c index 70c7a45..dc2c537 100644 --- a/log.c +++ b/log.c @@ -31,7 +31,7 @@ void GetCurrentTimeStr(char* buf) { // 入队操作:记录交易 // 参数: 账户ID, 类型("Deposit"/"Withdraw"), 交易金额, 交易后余额 -int EnqueueLog(int id, const char* type, double amt, double bal) { +int EnqueueLog(int id, const char* type, double amt, double bal, int target) { // 如果队列满,通过移动 front 指针覆盖最旧的日志 (环形队列特性) if (IsLogQueueFull()) { // 覆盖旧数据,队头自动出队 @@ -44,6 +44,7 @@ int EnqueueLog(int id, const char* type, double amt, double bal) { 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, "宇宙总行"); // 根据你的需求固定地点 @@ -61,14 +62,18 @@ void ShowTransactionLogs() { } printf("\n=== 交易流水日志 ===\n"); - printf("%-5s %-8s %-10s %-10s %-15s %-20s\n", "ID", "操作", "金额", "余额", "时间", "地点"); - printf("--------------------------------------------------------------\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 %-15s %-20s\n", - e.account_id, e.type, e.amount, e.balance, e.timestamp, e.location); + 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; } } @@ -157,17 +162,19 @@ void SearchLogs(const char* pattern) { LogEntry e = g_logQueue.logs[i]; // 将日志条目格式化为字符串(模拟一行文本) - char logLine[200]; - sprintf(logLine, "ID:%d Type:%s Amount:%.2f Balance:%.2f Time:%s", - e.account_id, e.type, e.amount, e.balance, e.timestamp); + 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\n", + 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; diff --git a/transaction.c b/transaction.c index d3836e3..2914dbc 100644 --- a/transaction.c +++ b/transaction.c @@ -31,7 +31,7 @@ void Deposit(int id) g_astAccounts[FindAccount(id)].balance+=amount; printf("success"); QueryBalance(id); - EnqueueLog(id, "Deposit", amount, g_astAccounts[FindAccount(id)].balance); + EnqueueLog(id, "Deposit", amount, g_astAccounts[FindAccount(id)].balance, -1); SaveData(); break; } @@ -69,7 +69,7 @@ void Withdraw(int id) 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); + EnqueueLog(id, "Withdraw", amount, g_astAccounts[FindAccount(id)].balance, -1); printf("success"); QueryBalance(id); SaveData(); @@ -100,3 +100,58 @@ void QueryBalance(int id) 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("密码错误,请重新输入: "); + } + } +}