GO语言-“欢乐记”-个人记账demo

基于Go语言的记账demo设计与实现


前言

        巩固新学的go语言语法基础,特此来练手回顾。由于是小demo,无GUI,数据只保留在本地,未接入数据库。

开发环境

        os:Windows10

        选用技术:Go

        软件集成开发环境(IDE):Goland 2022.3.4

需求分析

        完成个人日常的记账、查账,修改和删除等基本功能。要求写入本地实现数据的持久化

功能分析

新建账本 

        初始化切片充当数据容器,从本地读入或新建账本

记账

        向账本内写入数据

查看账本

        查看已记录的账本,并动态的展示余额。

修改账本

        根据id号来修改相应的记账记录

删除记账

        根据id号来删除相应的记账记录

退出

        将内存中数据写入本地保存,然后实现程序正常退出即可

代码实现

        使用结构体来表示每一条记账,内容包括:

type Transaction struct {
	Id          int
	Amount      float32 //金额
	Type        string  //类型(支出或收入)
	Description string  // 备注
	Date        string  //日期
}

        实现结构体的构造方法:

func NewTransaction(id int, amount float32, date string, Type, Description string) *Transaction {
	return &Transaction{id, amount, Type, Description, date}
}

       菜单

func menu(ts []basic.Transaction) ([]basic.Transaction, int) {

	fmt.Println("欢迎进入欢乐记账系统")
	fmt.Println("-----1.新建账本 -----")
	fmt.Println("-----2.  记账  -----")
	fmt.Println("-----3.查看账本 -----")
	fmt.Println("-----4.修改账本 -----")
	fmt.Println("-----5.删除账本 -----")
	fmt.Println("-----6.  退出  -----")
	fmt.Printf("请输入对应数字进入您所需要的功能:")
	var options int
	_, err := fmt.Scanf("%d\n", &options)
	if err != nil {
		fmt.Println("输入有误:", err)
	}
	path := "E:\\code\\go\\person_budget\\Ledger.txt"
	switch {
	case options == 1:
		if ts == nil {
			tmp, flag := utils.ReadFile(ts, path)
			if !flag {
				ts = newLedger()
				fmt.Println("新建账本成功!")
			} else {
				ts = tmp
				fmt.Println("读取账本成功!")
			}

		} else {
			fmt.Println("已存在账本")
		}
	case options == 2:
		ts = insertLedger(ts)
	case options == 3:
		printLedger(ts)
	case options == 4:
		ts = modifyLedger(ts)
	case options == 5:
		ts = deleteLedger(ts)
	case options == 6:
		utils.WriteFile(ts, path)
		return ts, 6
	}

	return ts, 0
}

新建账本/读取本地文件

        切片存储每一个记账结构体

// 初始化账单
func newLedger() []basic.Transaction {
	transaction := make([]basic.Transaction, 1)
	//fmt.Println(transaction, len(transaction))
	return transaction
}

        读取本地文件  

func ReadFile(ts []basic.Transaction, path string) ([]basic.Transaction, bool) {
	file, err := os.Open(path)
	if err != nil {
		fmt.Println("打开文件错误:", err)
		return nil, false
	}
	defer file.Close()

	ts = make([]basic.Transaction, 0)

	scanner := bufio.NewScanner(file)
	// 逐行读取文件
	for scanner.Scan() {
		line := scanner.Text() // 获取当前行的内容
		str_tmp := strings.Split(line, "--")
		// 使用 strconv.Atoi 解析字符串
		num, err := strconv.Atoi(str_tmp[0][5:])
		if err != nil {
			fmt.Println("转换错误:", err)
			return nil, false
		}
		//fmt.Println(str_tmp[1][9:])
		f64, _ := strconv.ParseFloat(str_tmp[1][9:], 32) // 解析为 float64
		if err != nil {
			fmt.Println("转换错误:", err)
			return nil, false
		}
		tmp := basic.NewTransaction(num, float32(f64), str_tmp[4][9:], str_tmp[2][9:], str_tmp[3][9:])

		ts = append(ts, *tmp)
	}

	// 检查读取过程中是否发生错误
	if err := scanner.Err(); err != nil {
		fmt.Println("读取文件错误:", err)
	}
	return ts, len(ts) != 0
}

记账

func insertLedger(ts []basic.Transaction) []basic.Transaction {
	//fmt.Println(cap(ts))
	if cap(ts) != 0 {
		var amount float32
		var Type string
		var description string
		fmt.Printf("金额:")
		fmt.Scanf("%f\n", &amount)
		fmt.Printf("类型:")
		fmt.Scanf("%s\n", &Type)
		fmt.Printf("描述:")
		fmt.Scanf("%s\n", &description)
		var id int
		if ts[0].Id == 0 {
			id = 1
		} else {
			id = len(ts) + 1
		}
		t := basic.NewTransaction(id, amount, time.Now().Format("2006-01-02 15:04:05"), Type, description)
		if ts[0].Id == 0 {
			ts[0] = *t
		} else {
			ts = append(ts, *t)
		}
	} else {
		fmt.Println("账本为空")
		return nil
	}
	return ts
}

查看账本

func printLedger(ts []basic.Transaction) {
	if cap(ts) != 0 && ts[0].Id != 0 {
		var sum float32
		for _, data := range ts {
			if data.Type == "支出" {
				sum -= data.Amount
			} else if data.Type == "收入" {
				sum += data.Amount
			}
			fmt.Printf("id :%d,金额:%.2f,类型:%s,描述:%s,时间:%s\n", data.Id, data.Amount, data.Type, data.Description, data.Date)
		}
		fmt.Println("余额:", sum)
	} else {
		fmt.Println("账本为空")
	}
}

修改账本

func modifyLedger(ts []basic.Transaction) []basic.Transaction {
	if cap(ts) != 0 && ts[0].Id != 0 {
		//打印账本
		printLedger(ts)
		//输入id修改
		var id int
		var amount float32
		var Type string
		var description string
		for {
			fmt.Printf("请输入要修改的账单id:")
			fmt.Scanf("%d\n", &id)
			fmt.Printf("修改后的金额:")
			fmt.Scanf("%f\n", &amount)
			fmt.Printf("修改后的类型:")
			fmt.Scanf("%s\n", &Type)
			fmt.Printf("修改后的描述:")
			fmt.Scanf("%s\n", &description)
			flag := false
			for i, data := range ts {
				if data.Id == id {
					x := basic.NewTransaction(data.Id, amount, time.Now().Format("2006-01-02 15:04:05"), Type, description)
					ts[i] = *x
					fmt.Println("修改后:", ts[i])
					flag = true
					break
				}
			}
			if !flag {
				fmt.Println("请输入正确的id")
			} else {
				break
			}
		}
		return ts
	} else {
		fmt.Println("账本为空")
		return nil
	}
}

删除记账

func deleteLedger(ts []basic.Transaction) []basic.Transaction {
	//打印账本
	printLedger(ts)
	//输入id修改
	var id int
	flag := false
	for {
		fmt.Printf("请输入要修改的账单id:")
		fmt.Scanf("%d\n", &id)
		for i, data := range ts {
			if data.Id == id {
				fmt.Println("是否删除:(Y/N)", data)
				var r string
				fmt.Scanf("%s\n", &r)
				if r == "Y" {
					ts = append(ts[:i], ts[i+1:]...)
				}
				flag = true
				break
			}
		}
		if !flag {
			fmt.Println("请输入正确的id")
		} else {
			break
		}
	}
	return ts
}

退出

        本模块逻辑十分简单,break跳出死循环即可。但退出前需将内存中的账本写入本地,实现数据的持久化。

        写入代码:

func WriteFile(ts []basic.Transaction, path string) {
	file, err := os.Create(path)
	if err != nil {
		fmt.Println("文件打开失败", err)
		return
	}
	defer file.Close()

	lines := make([]string, 0)
	for _, data := range ts {
		str := fmt.Sprintf("Id:%d--金额:%.2f--类型:%s--描述:%s--时间:%s", data.Id, data.Amount, data.Type, data.Description, data.Date)
		lines = append(lines, str)

	}
	writer := bufio.NewWriter(file)

	// 将切片内容写入文件
	for _, line := range lines {
		_, err := writer.WriteString(line + "\n") // 每行后添加换行符
		if err != nil {
			fmt.Println("写入文件错误:", err)
			return
		}
	}

	// 刷新缓冲区,将数据写入文件
	writer.Flush()

}

总结

        fmt.Scanf()。后加\n来吸收用户输入的回车,不然输入区里潜藏的回车将作用于下一个接收

        切片的append会返回一个新的切片,故需返回新的切片

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值