学习golang也有一个月了,今天面试出了一个简易的项目题。当时没有写完,现在作为一个小项目来实现。顺便巩固一下go的组合与转发,再第一次结合使用mysql一起学习。技术小白欢迎大家讨论,指正。
题目具体的描述记不清了,现在大概写一下要实现的功能:
公司有一个考勤机,记录每个员工的打卡时间。主管也是员工之一,主管可以按照员工工号或者考勤时间来查看打卡情况,主管也可以查看倒数第n个打卡的员工。
需求分析
员工需求分析
- 实现打卡登记功能
主管需求分析
- 实现打卡登记功能
- 按员工ID查看打卡情况
- 按打卡时间查看打卡情况
- 查看倒数第n个打卡的人
系统类图
太久没接触UML了,研究了好久才画出来,不知道对不对,先按照这个来实现。
代码实现
不含数据库版
这一版的所有数据都是随机生成并且直接在main函数中插入的。
package main
import (
"fmt"
"math/rand"
"sort"
"time"
)
type person struct {
id int
name string
time time.Time
}
type employee struct {
person
}
type manager struct {
person
}
type signer struct {
personList map[int]person
}
type privilege interface {
lookupById(signer2 *signer)
lookupByTime(signer2 *signer)
lookupN(signer2 *signer, n int)
}
func (p person) signIN(s *signer) {
//p.time = time.Now()
if s.personList == nil {
s.personList = make(map[int]person)
}
s.personList[p.id] = p
}
func (m manager) lookupById(list *signer) {
keys := []int{}
for k := range list.personList {
keys = append(keys, k)
}
sort.Ints(keys)
for _, k := range keys {
fmt.Printf("id:%v, name:%v\n", k, list.personList[k].name)
}
}
type KeyValue struct {
Key int
Value time.Time
}
func (m manager) lookupByTime(list *signer) {
var keyValuePairs []KeyValue
for k, v := range list.personList {
keyValuePairs = append(keyValuePairs, KeyValue{Key: k, Value: v.time})
}
sort.Slice(keyValuePairs, func(i, j int) bool {
return keyValuePairs[i].Value.Before(keyValuePairs[j].Value)
})
for _, kv := range keyValuePairs {
fmt.Printf("id: %v, name: %v, time: %v\n", kv.Key, list.personList[kv.Key].name, kv.Value)
}
}
func (m manager) lookupN(list *signer, n int) {
var keyValuePairs []KeyValue
for k, v := range list.personList {
keyValuePairs = append(keyValuePairs, KeyValue{Key: k, Value: v.time})
}
sort.Slice(keyValuePairs, func(i, j int) bool {
return keyValuePairs[i].Value.After(keyValuePairs[j].Value)
})
for index, kv := range keyValuePairs {
if index == n-1 {
fmt.Printf("id: %v, name: %v, time: %v\n", kv.Key, list.personList[kv.Key].name, kv.Value)
}
}
}
func main() {
var s signer
s.personList = make(map[int]person)
var m manager
fixedDate := time.Date(2023, time.September, 18, 0, 0, 0, 0, time.UTC)
rand.Seed(time.Now().UnixNano())
var people []person
for i := 1; i <= 10; i++ {
randomTime := generateRandomTime(fixedDate)
people = append(people, person{
id: i,
name: fmt.Sprintf("Person%d", i),
time: randomTime,
})
}
// 打卡上班
for _, p := range people {
//打卡
p.signIN(&s)
}
//主管按ID查看打卡情况
m.lookupById(&s)
fmt.Println("------------------------------------------")
//主管按打卡时间查看打卡情况
m.lookupByTime(&s)
fmt.Println("------------------------------------------")
//主管查看倒数第n个打卡的员工
m.lookupN(&s, 3)
fmt.Println("------------------------------------------")
//测试接口
var prv privilege
prv = m
prv.lookupN(&s, 3)
fmt.Println("------------------------------------------")
}
// 生成随机时间在指定日期的同一天不同时刻
func generateRandomTime(date time.Time) time.Time {
// 生成随机时、分、秒
hour := rand.Intn(24)
minute := rand.Intn(60)
second := rand.Intn(60)
// 构造随机时间
randomTime := time.Date(date.Year(), date.Month(), date.Day(), hour, minute, second, 0, time.UTC)
return randomTime
}
注意:只有manager类实现了privilege接口,employee没有实现privilege接口。当employee对象企图通过接口调用特权方法时会出现以下错误:
含数据库版(MySQL)
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
"log"
"sort"
"time"
)
var db *sql.DB
type person struct {
id int64
name string
time time.Time
}
type employee struct {
person
}
type manager struct {
person
}
type signer struct {
personList map[int64]person
}
type privilege interface {
lookupById(signer2 *signer)
lookupByTime(signer2 *signer)
lookupN(signer2 *signer, n int)
}
func initDB() {
dsn := "数据用户名:数据库密码@tcp(连接数据库套接字)/数据库名" //数据库信息
var err error
db, err = sql.Open("mysql", dsn) //不会校验账号密码是否正确
if err != nil { //dsn格式错误报错
fmt.Printf("dsn %s invalid, err:%v\n", dsn, err)
return
}
err = db.Ping() //尝试与数据库建立连接(校验dsn是否正确)
if err != nil {
fmt.Printf("open %s failed, err:%v\n", dsn, err)
return
}
fmt.Println("连接数据库成功!")
}
// 向数据表中插入数据
func (s *signer) signIN(p *person) (int64, error) {
result, err := db.Exec("INSERT INTO person (name, time) VALUES (?, ?)", p.name, p.time)
if err != nil {
return 0, fmt.Errorf("addPerson: %v\n", err)
}
id, err := result.LastInsertId()
if err != nil {
return 0, fmt.Errorf("addPerson: %v\n", err)
}
p.id = id
if s.personList == nil {
s.personList = make(map[int64]person)
}
s.personList[id] = *p
return id, nil
}
func (m manager) lookupById(list *signer) {
keys := []int64{}
for k := range list.personList {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
return keys[i] < keys[j]
})
for _, k := range keys {
fmt.Printf("id:%v, name:%v\n", k, list.personList[k].name)
}
}
type KeyValue struct {
Key int64
Value time.Time
}
func (m manager) lookupByTime(list *signer) {
var keyValuePairs []KeyValue
for k, v := range list.personList {
keyValuePairs = append(keyValuePairs, KeyValue{Key: k, Value: v.time})
}
sort.Slice(keyValuePairs, func(i, j int) bool {
return keyValuePairs[i].Value.Before(keyValuePairs[j].Value)
})
for _, kv := range keyValuePairs {
fmt.Printf("id: %v, name: %v, time: %v\n", kv.Key, list.personList[kv.Key].name, kv.Value)
}
}
func (m manager) lookupN(list *signer, n int) {
var keyValuePairs []KeyValue
for k, v := range list.personList {
keyValuePairs = append(keyValuePairs, KeyValue{Key: k, Value: v.time})
}
sort.Slice(keyValuePairs, func(i, j int) bool {
return keyValuePairs[i].Value.After(keyValuePairs[j].Value)
})
for index, kv := range keyValuePairs {
if index == n-1 {
fmt.Printf("id: %v, name: %v, time: %v\n", kv.Key, list.personList[kv.Key].name, kv.Value)
}
}
}
// 将数据加载到personList中
func loadPersonsFromDB(s *signer) error {
rows, err := db.Query("SELECT id, name, time FROM person")
if err != nil {
return fmt.Errorf("loadPersonsFromDB: %v\n", err)
}
defer rows.Close()
for rows.Next() {
var p person
var timeStr string // 时间字符串临时变量
if err := rows.Scan(&p.id, &p.name, &timeStr); err != nil {
return fmt.Errorf("loadPersonsFromDB: %v\n", err)
}
// 将时间字符串转为time.Time格式
p.time, err = time.Parse("2006-01-02 15:04:05", timeStr)
if err != nil {
return fmt.Errorf("loadPersonsFromDB: %v\n", err)
}
s.personList[p.id] = p
}
if err := rows.Err(); err != nil {
return fmt.Errorf("loadPersonsFromDB: %v\n", err)
}
return nil
}
func main() {
initDB() //调用输出化数据库的函数
var p person
var s signer
personInsert := person{
name: "小王",
time: time.Now(),
}
fmt.Println("Formatted time:", personInsert.time)
p.time = personInsert.time
/*-----------------------打卡--------------------------*/
pid, err := s.signIN(&personInsert)
if err != nil {
log.Fatal(err)
}
fmt.Printf("ID of added person: %v\n", pid)
if s.personList == nil {
s.personList = make(map[int64]person)
}
err = loadPersonsFromDB(&s)
if err != nil {
log.Fatal(err)
}
//fmt.Println(s.personList)
/*-----------------------特权--------------------------*/
//按id顺序输出
var m manager
m.lookupById(&s)
fmt.Println("---------------------------------------")
//按时间顺序输出
m.lookupByTime(&s)
fmt.Println("---------------------------------------")
//输出倒数第n个打卡的
m.lookupN(&s, 3)
fmt.Println("---------------------------------------")
}