V1 使用函數來解決需求
需求(在之前的用戶管理的基礎上)
1.用戶數據持久化 gob csv 密碼也持久化 每次操作完持久化 User不能有全局的 每次需要用User 從文件反序列化
2.當文件不存在,獲取反序列化失敗,不能報錯
3.密碼文件不存在 讓用戶初始化密碼3.保存文件 加一個版本 數據文件每次保存一個版本 最多保留3個版本
4.修改密碼(輸入原密碼和新密碼 只有在原密碼正確的時候 新密碼持久化)
1.CSV
//main.go
package main
import (
"fmt"
"github.com/strive-after/users/lib"
)
func main() {
var (
user string
menu string
)
if !lib.Auth() {
fmt.Println("您輸入的密碼錯誤,系統退出")
return
}
for {
//界面打印
menu = `====================
歡迎使用用戶管理系統
====================
1.添加用戶
2.查找用戶
3.修改用戶
4.刪除用戶
5.修改密碼
6.退出程序
====================`
fmt.Println(menu)
//用戶選擇
user = lib.Inputstring("請輸入指令:")
fmt.Println("====================")
if user == "1" {
lib.Add(lib.GetID())
} else if user == "2" {
lib.Sel()
} else if user == "3" {
lib.Change()
} else if user == "4" {
lib.Del()
} else if user == "5" {
lib.SetPassword()
}else if user == "6"{
break
} else {
fmt.Println("輸入有誤請重新輸入")
continue
}
}
}
//load&cave.go
//持久化
package lib
import (
"encoding/csv"
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strconv"
"time"
)
const userpath string = "./user.csv"
//存儲用戶信息
func SaveUser(UserInfo map[int]User) {
//對文件重命名 加時間後綴
if _,err := os.Stat(userpath); err == nil{
// //os.Rename(userpath,fmt.Sprintf("%x",time.Now())+".user.csv")
os.Rename(userpath,strconv.FormatInt(time.Now().Unix(),10)+".user.csv")
}
//刪除文件 保留3個最新的
if filebak,err := filepath.Glob("*.user.csv");err == nil{
// //默認是升序 我們要保留最新的 就用倒敘的方式排序 刪除3個以後的ok
sort.Sort(sort.Reverse(sort.StringSlice(filebak)))
//fmt.Println(filebak)
if len(filebak) >= 3 {
for _,v := range filebak[3:]{
os.Remove(v)
}
}
}
file,err := os.Create(userpath)
if err == nil {
defer file.Close()
writer := csv.NewWriter(file)
for _,users := range UserInfo{
writer.Write([]string{
strconv.Itoa(users.ID),
users.Name,
users.Birthday.Format("2006-1-2"),
users.Tel,
users.Addr,
users.Desc,
})
writer.Flush()
}
}
}
//加載用戶信息
func LoadUser()map[int]User {
UserInfo := map[int]User{}
//判斷文件是否存在
if file,err := os.Open(userpath);err == nil {
defer file.Close()
reader := csv.NewReader(file)
for {
//這裏line 每次讀取一行 是[]string類型
line,err := reader.Read()
if err != nil {
if err != io.EOF {
fmt.Printf("[-]錯誤:%v",err)
}
break
}else {
id ,_:= strconv.Atoi(line[0])
bir,_ := time.Parse("2006-1-2",line[2])
UserInfo[id] = User{
ID: id,
Name: line[1],
Birthday: bir,
Tel: line[3],
Addr: line[4],
Desc: line[5],
}
}
}
}else { //這裏err != nil
if !os.IsNotExist(err) { //如果不是因爲文件不存在 打印報錯
fmt.Printf("[-]發生錯誤%v",err)
}
}
return UserInfo
}
//import.go
package lib
import (
"bufio"
"fmt"
"os"
"strings"
"time"
)
//因爲需要用戶輸入的比較多定義一個input函數
func Inputstring(input string) string {
//這裏使用帶緩衝的io
//創建scanner
scanner := bufio.NewScanner(os.Stdin)
fmt.Printf(input)
//獲取用戶信息
scanner.Scan()
//返回讀取的行信息 text每次讀一行
return strings.TrimSpace(scanner.Text())
}
func Inputuser(id int) {
UserInfo := LoadUser()
var user User
var err error
START:
user.ID = id
user.Name = Inputstring("請輸入名字:")
user.Birthday,err = time.Parse("2006-1-2",Inputstring("請輸入生日(如2006-1-2):"))
user.Tel = Inputstring("請輸入聯繫方式:")
user.Addr = Inputstring("請輸入住址:")
user.Desc = Inputstring("請輸入備註:")
if err == nil {
for k,v := range UserInfo {
if k == id {
break
}else if v.Name == user.Name {
fmt.Println("用戶已存在,請重新輸入")
fmt.Println(strings.Repeat("-", 20))
goto START
}else if user.Name == ""{
fmt.Println("用戶名不能爲空,請重新輸入")
goto START
}
}
UserInfo[id] = user
SaveUser(UserInfo)
}else {
fmt.Println("格式有誤,請重新輸入")
goto START
}
}
//auth.go
package lib
import (
"crypto/md5"
"fmt"
"io/ioutil"
"os"
"time"
"github.com/howeyc/gopass"
)
//定義密碼輸入次數
//定義用戶結構體
type User struct {
ID int
Name string
Birthday time.Time
Tel string
Addr string
Desc string
}
const (
passwordFile = "./passworld"
maxauth = 3
)
func GetID() int{
var id int
UserInfo := LoadUser()
for k,_ := range UserInfo {
if id < k {
id = k
}
}
return id + 1
}
//使用系統前輸入密碼 超過3次則退出系統
func Auth() bool {
//password, err := os.Open(passwordFile)
password ,err := ioutil.ReadFile(passwordFile)
//打開密碼文件如果err!=nil 就是說打開有問題
if err != nil || len(password) < 1 {
//如果是因爲不存在 那麼我們需要初始化密碼
if os.IsNotExist(err) || len(password) < 1 {
fmt.Print("請輸入初始化密碼:")
bytes, _ := gopass.GetPasswd()
//將用戶輸入的密碼md5加密 寫入文件
ioutil.WriteFile(passwordFile, []byte(fmt.Sprintf("%x",md5.Sum(bytes))), 0644)
return true
} else { //如果不是因爲不存在 那麼打印報錯信息
fmt.Println(err)
}
} else { //如果存在 那麼就需要做密碼驗證
//defer password.Close()
for i := 1; i <= maxauth; i++ {
// user := Inputstring("請輸入密碼:")
fmt.Printf("請輸入密碼:")
bytes, _ := gopass.GetPasswd()
//這裏對輸入的密碼進行md5加密並且與常量比較
//%x是小寫十六進制表示 Sprintf格式化返回一個字符串
passwd ,_:= ioutil.ReadFile(passwordFile)
if fmt.Sprintf("%x", md5.Sum(bytes)) == string(passwd) {
return true
} else {
fmt.Printf("輸入錯誤請重新輸入,您還有%d次機會\n", maxauth-i)
}
}
return false
}
return false
}
func SetPassword() {
fmt.Printf("請輸入舊密碼:")
bytes, _ := gopass.GetPasswd()
if passwd ,_:= ioutil.ReadFile(passwordFile);string(passwd) == fmt.Sprintf("%x",md5.Sum(bytes)){
fmt.Print("請輸入新密碼")
bytes, _ := gopass.GetPasswd()
ioutil.WriteFile(passwordFile, []byte(fmt.Sprintf("%x",md5.Sum(bytes))), 0644)
fmt.Println("密碼修改成功")
}else {
fmt.Println("密碼輸入錯誤")
}
}
//add.go
package lib
import "fmt"
//添加用戶
func Add(id int) {
Inputuser(id)
fmt.Println("[+]添加成功")
}
//查詢用戶
//sel.go
package lib
import (
"fmt"
"sort"
"strings"
)
func Sel() {
UserInfo := LoadUser()
user := Inputstring("請輸入要查詢的信息:")
list := make([]User,0)
fmt.Println(strings.Repeat("-", 20))
for _, v := range UserInfo {
if strings.Contains(string(v.ID), user)||strings.Contains(v.Name, user) || strings.Contains(v.Desc, user) || strings.Contains(v.Addr, user) || strings.Contains(v.Tel, user) || strings.Contains(v.Birthday.Format("2006-01-02"), user) {
list = append(list,v)
}
}
users := Inputstring("請輸入要排序的屬性(ID,Name,Desc,Addr,Tel,Birthday):")
sort.Slice(list, func(i, j int) bool {
switch users {
case "ID":
return list[i].ID < list[j].ID
case "Name":
return list[i].Name < list[j].Name
case "Desc":
return list[i].Desc < list[j].Desc
case "Addr":
return list[i].Addr < list[j].Addr
case "Tel":
return list[i].Tel < list[j].Tel
case "Birthday":
return list[i].Birthday.Format("2006-1-2") < list[j].Birthday.Format("2006-1-2")
default:
return list[i].ID < list[j].ID
}
})
for _,v := range list {
fmt.Printf("ID:%d\nname:%s\nbirthday:%v\ntel:%s\naddr:%s\ndesc:%s\n", v.ID,v.Name,v.Birthday.Format("2006-1-2"),v.Tel,v.Addr,v.Desc)
fmt.Println(strings.Repeat("-", 20))
}
}
//修改用戶
//change.go
package lib
import (
"fmt"
"strconv"
"strings"
)
func Change() {
UserInfo := LoadUser()
if id, err := strconv.Atoi(Inputstring("請輸入修改的用戶id:")); err == nil {
fmt.Println(strings.Repeat("-", 20))
if user, ok := UserInfo[id]; ok {
fmt.Printf("ID:%d\nname:%s\nbirthday:%v\ntel:%s\naddr:%s\ndesc:%s\n",user.ID, user.Name,user.Birthday.Format("2006-1-2"),user.Tel,user.Addr,user.Desc)
fmt.Println(strings.Repeat("-", 20))
affirm := Inputstring("確認是否修改(Y/N):")
if affirm == "Y" || affirm == "y" {
Inputuser(id)
}
} else {
fmt.Println("用戶id不存在")
}
} else {
fmt.Println("輸入錯誤")
}
}
//刪除用戶
//del.go
package lib
import (
"fmt"
"strconv"
"strings"
)
func Del() {
UserInfo := LoadUser()
if id, err := strconv.Atoi(Inputstring("請輸入刪除的用戶id:")); err == nil {
fmt.Println(strings.Repeat("-", 20))
if user, ok := UserInfo[id]; ok {
fmt.Printf("ID:%d\nname:%s\nbirthday:%v\ntel:%s\naddr:%s\ndesc:%s\n",user.ID ,user.Name,user.Birthday.Format("2006-1-2"),user.Tel,user.Addr,user.Desc)
fmt.Println(strings.Repeat("-", 20))
affirm := Inputstring("確認是否刪除(Y/N):")
if affirm == "Y" || affirm == "y" {
delete(UserInfo, id)
SaveUser(UserInfo)
fmt.Println("[-]刪除成功")
}
} else {
fmt.Println("用戶id不存在")
}
} else {
fmt.Println("輸入錯誤")
}
}
2.GOB
//main.go
package main
import (
"fmt"
"github.com/strive-after/users/lib"
)
func main() {
var (
user string
menu string
)
if !lib.Auth() {
fmt.Println("您輸入的密碼錯誤,系統退出")
return
}
for {
//界面打印
menu = `====================
歡迎使用用戶管理系統
====================
1.添加用戶
2.查找用戶
3.修改用戶
4.刪除用戶
5.修改密碼
6.退出程序
====================`
fmt.Println(menu)
//用戶選擇
user = lib.Inputstring("請輸入指令:")
fmt.Println("====================")
if user == "1" {
lib.Add(lib.GetId())
} else if user == "2" {
lib.Sel()
} else if user == "3" {
lib.Change()
} else if user == "4" {
lib.Del()
} else if user == "5" {
lib.SetPassword()
}else if user == "6"{
break
} else {
fmt.Println("輸入有誤請重新輸入")
continue
}
}
}
存儲方式的不同只是在存儲函數這裏會有體現 其他文件都一致
//load&save.go
package lib
import (
"encoding/gob"
"fmt"
"os"
"path/filepath"
"sort"
"strconv"
"time"
)
const userpath string = "./user.gob"
//存儲用戶信息
func SaveUser(UserInfo map[int]User) {
//對文件重命名 加時間後綴
if _,err := os.Stat(userpath); err == nil{
// //os.Rename(userpath,fmt.Sprintf("%x",time.Now())+".user.csv")
os.Rename(userpath,strconv.FormatInt(time.Now().Unix(),10)+".user.gob")
}
//刪除文件 保留3個最新的
if filebak,err := filepath.Glob("*.user.gob");err == nil{
// //默認是升序 我們要保留最新的 就用倒敘的方式排序 刪除3個以後的ok
sort.Sort(sort.Reverse(sort.StringSlice(filebak)))
//fmt.Println(filebak)
if len(filebak) >= 3 {
for _,v := range filebak[3:]{
os.Remove(v)
}
}
}
if file,err := os.Create(userpath);err == nil {
defer file.Close()
encode := gob.NewEncoder(file)
encode.Encode(UserInfo)
}
}
//加載用戶信息
func LoadUser() map[int]User {
UserInfo := map[int]User{}
//判斷文件是否存在
if file,err := os.Open(userpath);err == nil {
defer file.Close()
//創建解碼器
decode := gob.NewDecoder(file)
//將流中的數據編碼到對象中 這裏UserInfo 是對象 要取指針
decode.Decode(&UserInfo)
}else { //這裏err != nil
if !os.IsNotExist(err) { //如果不是因爲文件不存在 打印報錯
fmt.Printf("[-]發生錯誤%v",err)
}
}
return UserInfo
}
//imput.go
package lib
import (
"bufio"
"fmt"
"os"
"strings"
"time"
)
//因爲需要用戶輸入的比較多定義一個input函數
func Inputstring(input string) string {
//這裏使用帶緩衝的io
//創建scanner
scanner := bufio.NewScanner(os.Stdin)
fmt.Printf(input)
//獲取用戶信息
scanner.Scan()
//返回讀取的行信息 text每次讀一行
return strings.TrimSpace(scanner.Text())
}
func Inputuser(id int) {
UserInfo := LoadUser()
var user User
var err error
START:
user.ID = id
user.Name = Inputstring("請輸入名字:")
user.Birthday,err = time.Parse("2006-1-2",Inputstring("請輸入生日(如2006-1-2):"))
user.Tel = Inputstring("請輸入聯繫方式:")
user.Addr = Inputstring("請輸入住址:")
user.Desc = Inputstring("請輸入備註:")
if err == nil {
for k,v := range UserInfo {
if k == id {
break
}else if v.Name == user.Name {
fmt.Println("用戶已存在,請重新輸入")
fmt.Println(strings.Repeat("-", 20))
goto START
}else if user.Name == ""{
fmt.Println("用戶名不能爲空,請重新輸入")
goto START
}
}
UserInfo[id] = user
SaveUser(UserInfo)
}else {
fmt.Println("格式有誤,請重新輸入")
goto START
}
}
//add.go
package lib
import "fmt"
//添加用戶
func Add(id int) {
Inputuser(id)
fmt.Println("[+]添加成功")
}
//auth.go
package lib
import (
"crypto/md5"
"fmt"
"io/ioutil"
"os"
"time"
"github.com/howeyc/gopass"
)
//定義密碼輸入次數
//定義用戶結構體
type User struct {
ID int
Name string
Birthday time.Time
Tel string
Addr string
Desc string
}
const (
passwordFile = "./passworld"
maxauth = 3
)
func GetId() int {
var id int
UserInfo := LoadUser()
for k,_ := range UserInfo {
if id < k {
id = k
}
}
return id + 1
}
//使用系統前輸入密碼 超過3次則退出系統
func Auth() bool {
//password, err := os.Open(passwordFile)
password ,err := ioutil.ReadFile(passwordFile)
//打開密碼文件如果err!=nil 就是說打開有問題
if err != nil || len(password) < 1 {
//如果是因爲不存在 那麼我們需要初始化密碼
if os.IsNotExist(err) || len(password) < 1 {
fmt.Print("請輸入初始化密碼:")
bytes, _ := gopass.GetPasswd()
//將用戶輸入的密碼md5加密 寫入文件
ioutil.WriteFile(passwordFile, []byte(fmt.Sprintf("%x",md5.Sum(bytes))), 0644)
return true
} else { //如果不是因爲不存在 那麼打印報錯信息
fmt.Println(err)
}
} else { //如果存在 那麼就需要做密碼驗證
//defer password.Close()
for i := 1; i <= maxauth; i++ {
// user := Inputstring("請輸入密碼:")
fmt.Printf("請輸入密碼:")
bytes, _ := gopass.GetPasswd()
//這裏對輸入的密碼進行md5加密並且與常量比較
//%x是小寫十六進制表示 Sprintf格式化返回一個字符串
passwd ,_:= ioutil.ReadFile(passwordFile)
if fmt.Sprintf("%x", md5.Sum(bytes)) == string(passwd) {
return true
} else {
fmt.Printf("輸入錯誤請重新輸入,您還有%d次機會\n", maxauth-i)
}
}
return false
}
return false
}
func SetPassword() {
fmt.Printf("請輸入舊密碼:")
bytes, _ := gopass.GetPasswd()
if passwd ,_:= ioutil.ReadFile(passwordFile);string(passwd) == fmt.Sprintf("%x",md5.Sum(bytes)){
fmt.Print("請輸入新密碼")
bytes, _ := gopass.GetPasswd()
ioutil.WriteFile(passwordFile, []byte(fmt.Sprintf("%x",md5.Sum(bytes))), 0644)
fmt.Println("密碼修改成功")
}else {
fmt.Println("密碼輸入錯誤")
}
}
//修改用戶
//change.go
package lib
import (
"fmt"
"strconv"
"strings"
)
func Change() {
UserInfo := LoadUser()
if id, err := strconv.Atoi(Inputstring("請輸入修改的用戶id:")); err == nil {
fmt.Println(strings.Repeat("-", 20))
if user, ok := UserInfo[id]; ok {
fmt.Printf("ID:%d\nname:%s\nbirthday:%v\ntel:%s\naddr:%s\ndesc:%s\n",user.ID, user.Name,user.Birthday.Format("2006-1-2"),user.Tel,user.Addr,user.Desc)
fmt.Println(strings.Repeat("-", 20))
affirm := Inputstring("確認是否修改(Y/N):")
if affirm == "Y" || affirm == "y" {
Inputuser(id)
}
} else {
fmt.Println("用戶id不存在")
}
} else {
fmt.Println("輸入錯誤")
}
}
//刪除用戶
//del.go
package lib
import (
"fmt"
"strconv"
"strings"
)
func Del() {
UserInfo := LoadUser()
if id, err := strconv.Atoi(Inputstring("請輸入刪除的用戶id:")); err == nil {
fmt.Println(strings.Repeat("-", 20))
if user, ok := UserInfo[id]; ok {
fmt.Printf("ID:%d\nname:%s\nbirthday:%v\ntel:%s\naddr:%s\ndesc:%s\n",user.ID ,user.Name,user.Birthday.Format("2006-1-2"),user.Tel,user.Addr,user.Desc)
fmt.Println(strings.Repeat("-", 20))
affirm := Inputstring("確認是否刪除(Y/N):")
if affirm == "Y" || affirm == "y" {
delete(UserInfo, id)
SaveUser(UserInfo)
fmt.Println("[-]刪除成功")
}
} else {
fmt.Println("用戶id不存在")
}
} else {
fmt.Println("輸入錯誤")
}
}