银行管理系统
开源注释详解:Github
功能
- 添加账户
- 储蓄账户
- 信用卡账户
- 存款
- 取款
- 当前账户余额
- 指定时间内的账目信息
- 利息
- 本地化存储,二次启动同步
- 所有账户总金额
- 退出
数据存储思路
将每次运行的代码存储到本地txt文件当中,每次启动可以首先初始化文件内容,已达到文件储存的目的
缺点
- 数据量过大数据初始化难
- 业务逻辑越复杂也越难以保存
- 用户交互需要按照文件保存书写指令,十分不便
或许可以使用MySQL数据库存储数据,用C++与MySQL进行交互,达到相关查询,存储的功能。
类图呈现
各文件功能
- date.h 日期类头文件
- date.cpp 日期类实现文件
- account.h 账户类定义头文件
- account.cpp 账户类实现文件以及记录
- accumulator.h 对数据进行累加的相关计算
- main.cpp 主函数文件
- Array.h 数组模版的头文件
- Controller类保存账户列表,当前日期和处理指定命令
详解源代码
date.h
#pragma once
/*日期类头文件,对日期类文件进行处理*/
#ifndef _DATE_H_
#define _DATE_H_
#include <iostream>
class Date //日期类
{
private:
int year; //年
int month; //月
int day; //日
int totalDays; //该日期是从公元元年1月1日开始的第几天
public:
Date(int year=1, int month=1, int day=1); //用年,月,日构造日期
//专门从cin读入一个日期,赋值查询历史账目功能
//static Date read();
//获取Date日期年份
int getYear() const
{
return year;
}
//获取Date日期月份
int getMonth() const
{
return month;
}
//获取Date日期天
int getDay() const
{
return day;
}
//获取Date当月的天数
int getMaxDay() const;
// 判断Date日期是否是闰年
bool isLeapYear() const //判断是否为闰年
{
return year % 4 == 0 && year * 100 != 0 || year % 400 == 0;
}
//输出Date日期
void show() const;
//输出Data从公元元年1月1日到目前date的天数
int distance(const Date& date) const
{
return totalDays - date.totalDays;
}
//重载Date之前的“-”号,用公元元年到目前的日期相减
int operator-(const Date& date) const
{
return totalDays - date.totalDays;
}
//重载Date之间的“<"号,用永远元年到目前date的日期计算
bool operator<(const Date& date) const
{
return totalDays < date.totalDays;
}
};
//重载Date类输入,格式化日期格式:year-month-day
std::istream& operator >>(std::istream& in, Date& date);
//重载Date类输出,格式化日期格式:year-month-day
std::ostream& operator<<(std::ostream& out, const Date& date);
#endif // !_DATE_H_
date.cpp
/*日期类实现函数*/
#include "date.h"
#include <iostream>
#include <cstdlib>
#include <stdexcept> //标准库表头,此头文件是错误处理库的一部分
using namespace std;
namespace //使用namespace使下面的定义只能在当前文件中有效
{
//存储平年中每个月1日之前有多少天,为便于getMaxDay函数的实现,
const int DAYS_BEFORE_MONTH[] = { 0,31,59,90,120,151,181,212,143,273,304,334,365 };
}
Date::Date(int year, int month, int day) :year(year), month(month), day(day)
{
if (day <= 0 || day > getMaxDay())
{
throw runtime_error("Invalid date"); //Date发生异常,直接使用标准程序库构造异常抛出
}
//减一是为了计算总天数,会加上月份和天数,年份日要减一
int years = year - 1;
//计算总天数
totalDays = years * 365 + years / 4 - years / 100 + years / 400 + DAYS_BEFORE_MONTH[month - 1] + day;
//如果是瑞年并且月份大约2,总天数+1
if (isLeapYear() && month > 2)
totalDays++;
}
//返回这月最大天数
int Date::getMaxDay() const
{
if (isLeapYear() && month == 2)
{
return 29;
}
else
{
return DAYS_BEFORE_MONTH[month] - DAYS_BEFORE_MONTH[month - 1];
}
}
//输出Date的年-月-日
void Date::show() const
{
cout << getYear() << "-" << getMonth() << "-" << getDay();
}
istream& operator>>(istream& in, Date& date) //istream通用输入流
{
int year, month, day;
char c1, c2;
cout << "类似输入格式为:2019-01-01" << endl;
in >> year >> c1 >> month >> c2 >> day;
//对输入进行判断,若没有按照格式,返回错误
if (c1 != '-' || c2 != '-')
//可用于运行时检测的异常类
throw runtime_error("Bad time format");
//将所得的正确日期返回给date,指针
date = Date(year, month, day);
//返回用户的输入 此处
return in;
}
//虫重载输出流,使date按照标准格式输出
ostream& operator << (ostream& out, const Date& date) //ostream通用输出流
{
out << date.getYear() << "-" << date.getMonth() << "-" << date.getDay();
return out;
}
account.h
#pragma once
/*银行账户类头文件*/
#ifndef _ACCOUNT_H_
#define _ACCOUNT_H_
#include "date.h"
#include "accumulator.h"
#include <string> //字符串头文件
#include <istream>
#include <map> //引出关联容器
#include <stdexcept>
//前置声明
class Account;
//账目记录类
class AccountRecord
{
private:
//日期
Date date;
//账户
const Account* account;
// 账户金额
double amount;
// 余额
double balance;
//描述
std::string desc;
public:
//构造函数
AccountRecord(const Date& date, const Account* account, double amount, double balance, const std::string& desc);
//输出当前记录
void show() const;
};
// 创建关联容器,建立日期和账户记录类的已排序列表
typedef std::multimap<Date, AccountRecord>RecordMap;
//账户父类
class Account
{
public:
std::string id; //账户Id字符串
double balance; //账户余额
static double total; //所有账户的总金额静态变量
static RecordMap recordMap; //账目记录
protected:
//提供派生类调用的构造函数:日期,id为账户
Account(const Date& date, const std::string& id);
//记录一笔账,date为日期,amount为金额,desc为说明
void record(const Date& date, double amount, const std::string& desc);
//报告错误信息
void error(const std::string& msg) const;
public:
//获取账户id
const std::string& getId() const
{
return id;
}
//获取账户余额
double getBalance() const
{
return balance;
}
//获取账户总金额
static double getTotal()
{
return total;
}
//纯虚函数,没有具体的操作内容,要求各派生类根据需要定义自己的版本
//存入现金,date为日期,amount为金额,desc为款项说明
virtual void deposit(const Date& date, double amount, const std::string& desc) = 0;
//纯虚函数,没有具体的操作内容,要求各派生类根据需要定义自己的版本
//取出现金,date为日期,amount为金额,desc为款项说明
virtual void withdraw(const Date& date, double amount, const std::string& desc) = 0;
//结算(计算利息、年费等),每月结算一次,date为结算日期
virtual void settle(const Date& date) = 0;
//显示账户信息
virtual void show(std::ostream& out) const;
//查询指定时间内的账目记录,开始Date,结束Date
static void query(const Date& begin, const Date& end);
};
//内联重载: 将Account输出调用Account.show()
inline std::ostream& operator<<(std::ostream& out, const Account& account)
{
account.show(out);
return out;
}
class SavingsAccount : public Account //存储账户类
{
private:
Accumulator acc; //辅助计算利息的累加器
double rate; //存款的年利率
public:
//储蓄账户,日期Date,账户Account的id,存款利率
SavingsAccount(const Date &date, const std::string &id, double rate);
//获取储蓄账户的存款利率
double getRate() const
{
return rate;
}
// 存钱: 参数: 日期Date,金额,描述信息
void deposit(const Date &date, double amount,const std::string &desc);// 存入现金
//取款:参数: 日期Date,金额,描述信息
void withdraw(const Date &date, double amount,const std::string &desc);//取出现金
//结算利息:参数:Date日期
void settle(const Date &date);
};
class CreditAccount : public Account //信用账户类
{
private:
Accumulator acc; //辅助计算利息的累加器
//信用额度
double credit;
double rate; //欠款的日利率
double fee; //信用卡年费
//获取欠款额
double getDebt()
{
//获取账户Account的余额,
double balance = getBalance();
//如果余额大于0不欠钱,返回0,否则返回balance
return (balance < 0 ? balance : 0);
}
public: //构造函数
//信用卡类:参数:日期Date,账户Account:id,信用额度,欠款日利率,信用卡年费
CreditAccount(const Date& date, const std::string& id, double credit, double rate, double fee);
//获取信用卡额度
double getCredit() const
{
return credit;
}
//获取信用卡日利率
double getRate() const
{
return rate;
}
//获取信用卡年费
double getFee() const
{
return fee;
}
//返回信用卡还可用额度,若账户余额为负,用信用卡补上
double getAvailableCredit() const
{
if (getBalance() < 0)
return credit + getBalance();
else
return credit;
}
//存入现金:参数:日期Date,账户Account金额,存款描述
void deposit(const Date& date, double amount, const std::string& desc);
//取出现金,参数:取款日期Date,取款金额,取款描述
void withdraw(const Date& date, double amount, const std::string& desc);
//结算利息和年费,参数Date日期
void settle(const Date& date);//结算利息和年费,每月1号调用一次该函数
// 输出账户id,账户金额,账户余额
void show() const;
//虚函数输出
virtual void show(std::ostream& out) const;
};
//账户异常处理类
class AccountException :public std::runtime_error
{
private:
// 账户
const Account* account;
public:
//传递运行中的异常信息,以及账户Account类
AccountException(const Account* account, const std::string& msg)
:runtime_error(msg), account(account)
{
}
//获得异常账户类
const Account* getAccount() const
{
return account;
}
};
#endif //_ACCOUNT_H_
account.cpp
/*
project :Account账户类实现文件cpp
*/
#include "account.h"
#include <iostream>
#include <cmath>
#include <utility>
using namespace std;
using namespace std::rel_ops;
//AccountRecord类的实现:参数日期Date,账户Account类,金额,余额,描述信息
AccountRecord::AccountRecord(const Date& date, const Account* account, double amount, double balance, const std::string& desc)
:date(date), account(account), amount(amount), balance(balance), desc(desc)
{
}
// 展现出账户记录类的账户时间,Id,存款金额,余额(算上信用卡),描述信息
void AccountRecord::show() const
{
//date.show();
cout <<date<< "\t#" << account->getId() << "\t" << amount << "\t" << balance << "\t" << desc << endl;
}
//存款总金额全局变量(所有账户)
double Account::total = 0;
// Account类中的账户记录
RecordMap Account::recordMap;
//Account账户类构造函数
Account::Account(const Date& date, const string& id)
:id(id), balance(0)
{
//date.show();
cout << date<<"\t#" << id << " created" << endl;
}
//记账:参数:Date时间,amount金额,记账描述
void Account::record(const Date& date, double amount, const string& desc)
{
amount = floor(amount * 100 + 0.5) / 100; //保留小数点后两位,
balance += amount; //余额增加
total += amount; //账户总额增加
//date.show(); //输出时间
//将添加的数据存入账目记录类
AccountRecord record(date, this, amount, balance, desc);
//将数据插入到关联容器内部,,date为主键
recordMap.insert(make_pair(date, record));
record.show();
//cout << "\t#" << id << "\t" << amount << "\t" << balance << "\t" << desc << endl;
}
//显示账户信息
void Account::show(ostream &out) const
{
cout << "#" << id << "\tBalance:" << balance;
}
//SavingsAccount类的实现,参数:Date时间,Account的ID,利率
SavingsAccount::SavingsAccount(const Date& date, const string& id, double rate) //时间,账户,利率
:Account(date, id), rate(rate), acc(date, 0)
{
}
//存钱:参数:时间,金额,描述信息
void SavingsAccount::deposit(const Date& date, double amount,const string &desc)
{
record(date, amount,desc); //记录一笔账
//调佣accumulator函数,更新值,变更当前的时间,还有值
acc.change(date, getBalance());
}
//取钱
void SavingsAccount::withdraw(const Date& date, double amount,const string& desc)
{
if (amount > getBalance())
{
cout << "Error: not enough money" << endl;
}
else
{
record(date, -amount, desc);
acc.change(date, getBalance());
}
}
// 计算利息:传递时间Date
void SavingsAccount::settle(const Date& date)
{
double interest = acc.getSum(date) * rate / date.distance(Date(date.getYear()-1,1,1)); //计算年息
if (interest != 0)
{
//将利息记账
record(date, interest,"interest");
}
//初始化,日期,数值,累加器清零
acc.reset(date, getBalance());
}
//CreditAccount 类默认构造函数的实现
CreditAccount::CreditAccount(const Date& date, const string& id, double credit, double rate, double fee)
:Account(date,id),credit(credit),rate(rate),fee(fee),acc(date,0)
{
}
//存入信用卡
void CreditAccount::deposit(const Date& date, double amount, const string& desc)
{
record(date, amount, desc);
acc.change(date, getDebt());
}
//取出信用卡金额
void CreditAccount::withdraw(const Date& date, double amount, const string& desc)
{
if (amount - getBalance() > credit)
{
error("nt enough credit");
}
else
{
//使用负数 ,减
record(date, -amount, desc);
acc.change(date, getDebt());
}
}
//结算信用卡的利息
void CreditAccount::settle(const Date& date)
{
double interest = acc.getSum(date) * rate;
if (interest != 0)
record(date, interest, "interest");
if (date.getMonth() == 1)
record(date, -fee, "annual fee");
acc.reset(date, getDebt());
}
//输出,包括信用卡额度
void CreditAccount::show(ostream& out) const
{
Account::show(out);
cout << "\tAvailable credit:" << getAvailableCredit();
}
//异常
void Account::error(const string& msg) const
{
throw AccountException(this, msg);
}
//账户记录查询实现,参数:开始Date,结束Date
void Account::query(const Date& begin, const Date& end)
{
//从开始到结尾遍历输出
if (begin <= end)
{
//lower_bound返回指向首个不小于给定键的元素的迭代器
RecordMap::iterator iter1 = recordMap.lower_bound(begin);
//upper_bound返回指向首个大于给定键的元素的迭代器
RecordMap::iterator iter2 = recordMap.upper_bound(end);
for (RecordMap::iterator iter = iter1; iter != iter2; ++iter)
{
iter->second.show();
}
}
}
accumulator.h
/*将accumulation.h按日将数字累加的Accumulatior类的头文件*/
#ifndef _ACCUMULATOR_H_
#define _ACCUMULATOR_H_
#include "date.h"
class Accumulator //将某个数值按日累加
{
private:
Date lastDate; //上次变更数值的日期
double value; //数值的当前值
double sum; //数值按日累加之和
public:
//构造函数,date为 开始累加的日期,value为初始值
Accumulator(const Date& date, double value)
:lastDate(date), value(value), sum(0)
{
}
//获取日期date的累加结果
double getSum(const Date& date) const
{
return sum + value * date.distance(lastDate);
}
//在date将数值表更为value
void change(const Date& date, double value)
{
sum = getSum(date);
lastDate = date;
this->value = value;
}
//初始化,将日期变为date,数值变为value,累加器清零
void reset(const Date& date, double value)
{
lastDate = date;
this->value = value;
sum = 0;
}
};
#endif // !_ACCUMULATOR_H_
main.cpp
/*
Project:存款类主函数文件
environment:Visual studio 2022
*/
#include "account.h"
#include "Array.h"
#include <iostream>
#include <vector>
#include <algorithm>
#include <fstream>
#include <sstream>
#include <string>
using namespace std;
//释放空间结构体,用于析构函数
struct deleter
{
template<class T>void operator() (T* p)
{
delete p;
}
};
//建立类,用来存储账户列表和处理命令
class Controller
{
private:
Date date; //当前日期
vector<Account*>accounts; //账户列表
bool end; //用户是够输入了退出命令
public:
//存储用户数据,参数:Date时间
Controller(const Date& date) :date(date), end(false)
{
}
//析构函数
~Controller();
//返回当前的时间
const Date& getDate() const
{
return date;
}
//bool值,返回是否结束
bool isEnd() const
{
return end;
}
//执行一条命令,返回该命令是否改变了当前状态(即是否需要保存该命令)
bool runCommand(const string& cmdLine);
};
//释放从账户开始到结束的所有内存
Controller::~Controller()
{
//for_each遍历:开始,结束,执行
//按照顺序应用给定的函数对象f到接引到范围[first,last]中每个迭代器的结果
for_each(accounts.begin(), accounts.end(), deleter());
}
bool Controller::runCommand(const string& cmdLine)
{
/*
* 指令内容
* a s S3755217 0.015
* a s 02342342 0.015
* a c C5392394 10000 0.0005 50
* c 5
*/
//读取是有顺序要求的,根据指针向后,异常赋值
//第一个赋值给cmd判断进入那个case开面
//后面根据不同case依次赋值
istringstream str(cmdLine);
char cmd, type;
int index, day;
double amount, credit, rate, fee;
string id, desc;
Account* account;
Date date1, date2;
str >> cmd;
switch (cmd)
{
case 'a': //增加账户
str >> type >> id;
if (type == 's')
{
str >> rate;
account = new SavingsAccount(date, id, rate);
}
else
{
str >> credit >> rate >> fee;
account = new CreditAccount(date, id, credit, rate, fee);
}
accounts.push_back(account);
return true;
case 'd': //存入现金
str >> index >> amount;
getline(str, desc);
accounts[index]->deposit(date, amount, desc);
return true;
case 'w': //取出现金
str >> index >> amount;
getline(str, desc);
accounts[index]->withdraw(date, amount, desc);
return true;
case 's': //查询各账户信息
for (size_t i = 0; i < accounts.size(); i++)
cout << "[" << i << "] " << *accounts[i] << endl;
return false;
case 'c': //改变日期
str >> day;
if (day < date.getDay())
cout << "You cannot specify a previous day";
else if (day > date.getMaxDay())
cout << "Invalid day";
else
date = Date(date.getYear(), date.getMonth(), day);
return true;
case 'n': //进入下个月
if (date.getMonth() == 12)
date = Date(date.getYear() + 1, 1, 1);
else
date = Date(date.getYear(), date.getMonth() + 1, 1);
for (vector<Account*>::iterator iter = accounts.begin(); iter != accounts.end(); ++iter)
(*iter)->settle(date);
return true;
case 'q': //查询一段时间内的账目
str >> date1 >> date2;
Account::query(date1, date2);
return false;
case 'e': //退出
end = true;
return false;
}
cout << "Inavlid command: " << cmdLine << endl;
return false;
}
int main(void)
{
Date date(2022,11,1); //起始日期
cout << "输出化本地化数据库数据" << endl;
// 初始化命令
Controller controller(date);
string cmdLine;
//打开文件名称
const char* FILE_NAME = "commands.txt";
ifstream fileIn(FILE_NAME); //以读模式打开文件
if (fileIn) //直到执行到没有指令
{ //如果正常打开,就执行文件中的每一条命令
while (getline(fileIn, cmdLine))
{
try
{
controller.runCommand(cmdLine);
}
catch (exception& e)
{
cout << "Bad line in" << FILE_NAME << ":" << cmdLine << endl;
cout << "Error:" << e.what() << endl;
return 1;
}
}
fileIn.close(); //关闭文件
}
//初始化指令结束
//追加模式写入
ofstream fileOut(FILE_NAME, ios_base::app); //以追加模式
//选择框
cout << "\n\n\t\t***由于银行信息系统使用命令行存储数据形式,请严格按照输入格式进行输入:***" << endl;
cout << "\t\t\t\t******命令选项*******"
<<"\n\t\t\t\t\t(a)add account: a s S3755217 0.015 //a代表添加用户,s代表储蓄卡,c代表信用卡,S3755217为账户id,0.015为利率 "
<<"\n\t\t\t\t\t(d)deposit :d 0 5000 salary //d代表存钱,0代表第0个账户,5000代表金额,salary为存储说明"
<< "\n\t\t\t\t\t(w)withdraw : w 2 2000 buy a cell //w为取钱,2代表第二个账户,2000金额,buy a cell说明"
<<"\n\t\t\t\t\t(s)show : s //查看账户"
<< "\n\t\t\t\t\t(c)change day :c 15 //c为改变日期,15为日 "
<< "\n\t\t\t\t\t(n)next month :n //进入下一个月"
<<"\n\t\t\t\t\t(q)query :q 2022-11-01 2022-12-01 //遍历从11月1号到12月1号的账目数据"
<<"\n\t\t\t\t\t(e)exit :e //退出"
<< endl;
while (!controller.isEnd())
{ //从标准输入读入命令并执行,直到退出
cout << controller.getDate() << "\tTotal: " << Account::getTotal() << "\t\t\t\t\ncommand> ";
cout << "命令输入规范:" << endl;
string cmdLine;
//读入命令,和文件存储格式相同
getline(cin, cmdLine);
try
{
//执行命令
if (controller.runCommand(cmdLine))
//并记录文件内容
fileOut << cmdLine << endl; //将命令写入文件
}
catch (AccountException& e)
{
cout << "Erroe(#" << e.getAccount()->getId() << "):" << e.what() << endl;
}
catch (exception& e)
{
cout << "Error: " << e.what() << endl;
}
}
return 0;
}