Go项目-家庭收支记账软件

Go项目-家庭收支记账软件

项目开发流程

xHqQNd.md.png

项目需求说明

  1. 模拟实现基于文本界面的《家庭记账软件》
  2. 软件能够记录家庭的收入、支出,并能够打印收支明细表

项目代码编写

主菜单的设计

func main() {
	// 声明一个变量保存用户的输入
	key := ""

	// 声明一个变量,控制是否退出for循环
	loop := true

	// 显示主菜单
	for loop {
		fmt.Println("---------------------家庭收支记账软件---------------------")
		fmt.Println("                       1 收支明细")
		fmt.Println("                       2 登记收入")
		fmt.Println("                       3 登记输出")
		fmt.Println("                       4 退出软件")
		fmt.Print("请选择(1-4):")
		// 接收用户的输入
		fmt.Scanln(&key)
		// 对用户的输入进行判断
		switch key {
		case "1":
			fmt.Println("---------------------当前收支明细记录---------------------")
		case "2":
		case "3":
			fmt.Println("登记支出------")
		case "4":
			loop = false
		default:
			fmt.Println("请输入正确的选项------")
		}
	}
	fmt.Println("-------------------退出家庭收支记账软件-------------------")
}

没啥有意思的,基础编程,效果如下:

xHOuYd.png

显示明细与登记输入

case "1":
	fmt.Println("---------------------当前收支明细记录---------------------")
	fmt.Println(details)
case "2":
	fmt.Println("-------------------------登记收入-------------------------")
	fmt.Print("本次收入金额:")
	fmt.Scanln(&money)
	fmt.Print("本次收入说明:")
	fmt.Scanln(&note)
	balance += money
	details += fmt.Sprintf("收  入\t%v\t%v\t%v\n", balance, money, note)
	fmt.Println("收入登记成功!")

其中明细是用字符串拼接实现的,实际中应该是要操作数据库的

登记支出

case "3":
	fmt.Println("-------------------------登记支出-------------------------")
	fmt.Print("本次支出金额:")
	fmt.Scanln(&money)
	if money > balance {
		fmt.Println("余额的金额不足!")
		break
	} else if money <= 0 {
		fmt.Println("支出金额应为正数!")
	}
	fmt.Print("本次支出说明:")
	fmt.Scanln(&note)
	balance -= money
	details += fmt.Sprintf("收  入\t%v\t%v\t%v\t%v\n", balance+money, money, balance, note)
	fmt.Println("支出登记成功!")

注意支出的金额要小于账户余额,也要注意收入和支出的时候用户输入的数字需要为正数。

完善代码

退出时增加确认条件

case "4":
	var choice byte
	for {
		fmt.Print("确定退出?(y/n):")
		fmt.Scanf("%c\n", &choice)
		if choice == 'y' {
			loop = false
			break
		} else if choice == 'n' {
			break
		} else {
			fmt.Println("输入有误!!请重新输入")
		}
	}

注意scanf字符的时候与C语言是差不多的,需要考虑回车符号

没有记录时不输出收支详情字符串

// 判断当前是否有输入或者输出的记录
flag := false

也没啥好说的,加个标志位,有记录的时候将这个标志位改掉就可以了

面向对象

将上面的面向过程的代码修改成面向对象的代码

主要思想:将记账软件的功能封装到结构体中,然后调用这个结构体的方法完成功能。

定义结构体

// 定义结构体
type FamilyAccount struct {
	// 声明一个变量保存用户的输入
	key string
	// 声明一个变量,控制是否退出for循环
	loop bool
	// 定义账户的初始值
	balance float64
	// 定义每次收支的金额和说明
	money float64
	note  string
	// 收支的详情使用字符串来记录
	// 当有记录时对这个字符串进行拼接
	details string
	// 判断当前是否有输入或者输出的记录
	flag bool
}

注意定义结构体的时候不能进行初始化

工厂模式返回结构体的指针

// 编写一个工厂模式的构造方法,返回结构体的指针
func NewFamilyAcount() *FamilyAccount {
	return &FamilyAccount{
		key:     "",
		loop:    true,
		balance: 10000.0,
		money:   0.0,
		note:    "",
		details: "收  支\t收支前账户余额\t收支金额\t收支后账户余额\t说  明\n",
		flag:    false,
	}
}

注意如果结构体是私有的是一定要有的,公开的也可以有,以后就要记得一定要有这样的一个方法

编写各种方法

简单改造一下面向过程的代码即可完成面向对象的效果

将显示明细写成一个方法

func (fa *FamilyAccount) showDetails() {
	if !fa.flag {
		fmt.Println("当前没有任何收支记录!")
	} else {
		fmt.Println("---------------------当前收支明细记录---------------------")
		fmt.Println(fa.details)
	}
}

将登记收入写成一个方法

func (fa *FamilyAccount) income() {
	fmt.Println("-------------------------登记收入-------------------------")
	fmt.Print("本次收入金额:")
	fmt.Scanln(&fa.money)
	// 收入金额不能是负数
	if fa.money <= 0 {
		fmt.Println("收入金额应为正数!")
		return
	}
	fmt.Print("本次收入说明:")
	fmt.Scanln(&fa.note)
	fa.balance += fa.money
	fa.details += fmt.Sprintf("收  入\t%v\t%v\t%v\t%v\n", fa.balance-fa.money, fa.money, fa.balance, fa.note)
	fmt.Println("收入登记成功!")
	fa.flag = true
}

将登记支出写成一个方法

func (fa *FamilyAccount) pay() {
	fmt.Println("-------------------------登记支出-------------------------")
	fmt.Print("本次支出金额:")
	fmt.Scanln(&fa.money)
	if fa.money > fa.balance {
		fmt.Println("余额的金额不足!")
		return
	} else if fa.money <= 0 {
		fmt.Println("支出金额应为正数!")
	}
	fmt.Print("本次支出说明:")
	fmt.Scanln(&fa.note)
	fa.balance -= fa.money
	fa.details += fmt.Sprintf("收  入\t%v\t%v\t%v\t%v\n", fa.balance+fa.money, fa.money, fa.balance, fa.note)
	fmt.Println("支出登记成功!")
	fa.flag = true
}

将退出系统写成一个方法

func (fa *FamilyAccount) exit() {
	var choice byte
	for {
		fmt.Print("确定退出?(y/n):")
		fmt.Scanf("%c\n", &choice)
		if choice == 'y' {
			fa.loop = false
			break
		} else if choice == 'n' {
			break
		} else {
			fmt.Println("输入有误!!请重新输入")
		}
	}
}

显示主菜单

func (fa *FamilyAccount) MainMenu() {
	// 显示主菜单
	for fa.loop {
		fmt.Println("\n---------------------家庭收支记账软件---------------------")
		fmt.Println("                       1 收支明细")
		fmt.Println("                       2 登记收入")
		fmt.Println("                       3 登记输出")
		fmt.Println("                       4 退出软件")
		fmt.Print("请选择(1-4):")
		// 接收用户的输入
		fmt.Scanln(&fa.key)
		// 对用户的输入进行判断
		switch fa.key {
		case "1":
			fa.showDetails()
		case "2":
			fa.income()
		case "3":
			fa.pay()
		case "4":
			fa.exit()
		default:
			fmt.Println("请输入正确的选项------")
		}
	}
}

主函数中进行调用

func main() {
	utils.NewFamilyAcount().MainMenu()
}

增加扩展功能

添加一个用户登录的功能,即只有将用户名和密码输入正确后才能打开软件,否则无法看到主界面

实现:在显示菜单之前增加一个无限循环要求用户输入用户名和密码,只有输入正确才能退出循环

for {
	var username, password string
	fmt.Print("请输入用户名:")
	fmt.Scanln(&username)
	fmt.Print("请输入密码:")
	fmt.Scanln(&password)
	if fa.login(username, password) {
		break
	} else {
		fmt.Println("用户名或密码错误!")
	}
}

用户登录的方法

func (fa *FamilyAccount) login(username string, password string) bool {
	if (username == fa.username) && (password == fa.password) {
		return true
	}
	return false
}

完整源代码

面向过程的代码

package main

import "fmt"

func main() {
	// 声明一个变量保存用户的输入
	key := ""

	// 声明一个变量,控制是否退出for循环
	loop := true

	// 定义账户的初始值
	balance := 10000.0

	// 定义每次收支的金额和说明
	var money float64
	var note string

	// 收支的详情使用字符串来记录
	// 当有记录时对这个字符串进行拼接
	details := "收  支\t收支前账户余额\t收支金额\t收支后账户余额\t说  明\n"
	// 判断当前是否有输入或者输出的记录
	flag := false

	// 显示主菜单
	for loop {
		fmt.Println("\n---------------------家庭收支记账软件---------------------")
		fmt.Println("                       1 收支明细")
		fmt.Println("                       2 登记收入")
		fmt.Println("                       3 登记输出")
		fmt.Println("                       4 退出软件")
		fmt.Print("请选择(1-4):")
		// 接收用户的输入
		fmt.Scanln(&key)
		// 对用户的输入进行判断
		switch key {
		case "1":
			if !flag {
				fmt.Println("当前没有任何收支记录!")
			} else {
				fmt.Println("---------------------当前收支明细记录---------------------")
				fmt.Println(details)
			}
		case "2":
			fmt.Println("-------------------------登记收入-------------------------")
			fmt.Print("本次收入金额:")
			fmt.Scanln(&money)
			// 收入金额不能是负数
			if money <= 0 {
				fmt.Println("收入金额应为正数!")
				break
			}
			fmt.Print("本次收入说明:")
			fmt.Scanln(&note)
			balance += money
			details += fmt.Sprintf("收  入\t%v\t%v\t%v\t%v\n", balance-money, money, balance, note)
			fmt.Println("收入登记成功!")
			flag = true
		case "3":
			fmt.Println("-------------------------登记支出-------------------------")
			fmt.Print("本次支出金额:")
			fmt.Scanln(&money)
			if money > balance {
				fmt.Println("余额的金额不足!")
				break
			} else if money <= 0 {
				fmt.Println("支出金额应为正数!")
			}
			fmt.Print("本次支出说明:")
			fmt.Scanln(&note)
			balance -= money
			details += fmt.Sprintf("收  入\t%v\t%v\t%v\t%v\n", balance+money, money, balance, note)
			fmt.Println("支出登记成功!")
			flag = true
		case "4":
			var choice byte
			for {
				fmt.Print("确定退出?(y/n):")
				fmt.Scanf("%c\n", &choice)
				if choice == 'y' {
					loop = false
					break
				} else if choice == 'n' {
					break
				} else {
					fmt.Println("输入有误!!请重新输入")
				}
			}
		default:
			fmt.Println("请输入正确的选项------")
		}
	}
	fmt.Println("-------------------退出家庭收支记账软件-------------------")
}

面向对象的代码

.
├── Family-Ledger
│ ├── main
│ │ └── main.go
│ └── utils
│ └── familyAccount.go

main.go

package main

import (
	"Go-Projects/Family-Ledger/utils"
)

func main() {
	utils.NewFamilyAcount().MainMenu()
}

familyAccount.go

package utils

import "fmt"

// 定义结构体
type FamilyAccount struct {
	// 用户名和密码
	username string
	password string
	// 声明一个变量保存用户的输入
	key string
	// 声明一个变量,控制是否退出for循环
	loop bool
	// 定义账户的初始值
	balance float64
	// 定义每次收支的金额和说明
	money float64
	note  string
	// 收支的详情使用字符串来记录
	// 当有记录时对这个字符串进行拼接
	details string
	// 判断当前是否有输入或者输出的记录
	flag bool
}

// 编写一个工厂模式的构造方法,返回结构体的指针
func NewFamilyAcount() *FamilyAccount {
	return &FamilyAccount{
		username: "admin",
		password: "password",
		key:      "",
		loop:     true,
		balance:  10000.0,
		money:    0.0,
		note:     "",
		details:  "收  支\t收支前账户余额\t收支金额\t收支后账户余额\t说  明\n",
		flag:     false,
	}
}

// 给结构体绑定相应的方法

// 将显示明细写成一个方法
func (fa *FamilyAccount) showDetails() {
	if !fa.flag {
		fmt.Println("当前没有任何收支记录!")
	} else {
		fmt.Println("---------------------当前收支明细记录---------------------")
		fmt.Println(fa.details)
	}
}

// 将登记收入写成一个方法
func (fa *FamilyAccount) income() {
	fmt.Println("-------------------------登记收入-------------------------")
	fmt.Print("本次收入金额:")
	fmt.Scanln(&fa.money)
	// 收入金额不能是负数
	if fa.money <= 0 {
		fmt.Println("收入金额应为正数!")
		return
	}
	fmt.Print("本次收入说明:")
	fmt.Scanln(&fa.note)
	fa.balance += fa.money
	fa.details += fmt.Sprintf("收  入\t%v\t%v\t%v\t%v\n", fa.balance-fa.money, fa.money, fa.balance, fa.note)
	fmt.Println("收入登记成功!")
	fa.flag = true
}

// 将登记支出写成一个方法
func (fa *FamilyAccount) pay() {
	fmt.Println("-------------------------登记支出-------------------------")
	fmt.Print("本次支出金额:")
	fmt.Scanln(&fa.money)
	if fa.money > fa.balance {
		fmt.Println("余额的金额不足!")
		return
	} else if fa.money <= 0 {
		fmt.Println("支出金额应为正数!")
	}
	fmt.Print("本次支出说明:")
	fmt.Scanln(&fa.note)
	fa.balance -= fa.money
	fa.details += fmt.Sprintf("收  入\t%v\t%v\t%v\t%v\n", fa.balance+fa.money, fa.money, fa.balance, fa.note)
	fmt.Println("支出登记成功!")
	fa.flag = true
}

// 将退出系统写成一个方法
func (fa *FamilyAccount) exit() {
	var choice byte
	for {
		fmt.Print("确定退出?(y/n):")
		fmt.Scanf("%c\n", &choice)
		if choice == 'y' {
			fa.loop = false
			break
		} else if choice == 'n' {
			break
		} else {
			fmt.Println("输入有误!!请重新输入")
		}
	}
}

// 用户登录的功能
func (fa *FamilyAccount) login(username string, password string) bool {
	if (username == fa.username) && (password == fa.password) {
		return true
	}
	return false
}

// 显示主菜单
func (fa *FamilyAccount) MainMenu() {
	for {
		var username, password string
		fmt.Print("请输入用户名:")
		fmt.Scanln(&username)
		fmt.Print("请输入密码:")
		fmt.Scanln(&password)
		if fa.login(username, password) {
			break
		} else {
			fmt.Println("用户名或密码错误!")
		}
	}
	// 显示主菜单
	for fa.loop {
		fmt.Println("\n---------------------家庭收支记账软件---------------------")
		fmt.Println("                       1 收支明细")
		fmt.Println("                       2 登记收入")
		fmt.Println("                       3 登记输出")
		fmt.Println("                       4 退出软件")
		fmt.Print("请选择(1-4):")
		// 接收用户的输入
		fmt.Scanln(&fa.key)
		// 对用户的输入进行判断
		switch fa.key {
		case "1":
			fa.showDetails()
		case "2":
			fa.income()
		case "3":
			fa.pay()
		case "4":
			fa.exit()
		default:
			fmt.Println("请输入正确的选项------")
		}
	}
}

Go项目-家庭收支记账软件
https://zhangzhao219.github.io/2022/11/02/Go/Go-Project-Family-Ledger/
作者
Zhang Zhao
发布于
2022年11月2日
许可协议