员工考勤系统1.0

学习golang也有一个月了,今天面试出了一个简易的项目题。当时没有写完,现在作为一个小项目来实现。顺便巩固一下go的组合与转发,再第一次结合使用mysql一起学习。技术小白欢迎大家讨论,指正。


题目具体的描述记不清了,现在大概写一下要实现的功能:

公司有一个考勤机,记录每个员工的打卡时间。主管也是员工之一,主管可以按照员工工号或者考勤时间来查看打卡情况,主管也可以查看倒数第n个打卡的员工。


需求分析

员工需求分析

  1. 实现打卡登记功能

主管需求分析

  1. 实现打卡登记功能
  2. 按员工ID查看打卡情况
  3. 按打卡时间查看打卡情况
  4. 查看倒数第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("---------------------------------------")
}

某软件公司,员工人数 100 人左右,大部分员工是软件研发人员,包括项目经理、软件 设计师、程序员、测试工程师、实施工程师等,除此之外还包括行政人员、财务人员。公司 在软件研发及日常管理上有一套成熟的管理方法,在没有考勤系统之前,与考勤相关的管理 工作是这样的:  每位员工需要上午上班时打一次卡,下午下班时打一次卡,中午的休息不需要打卡。  期间如果需要外出工作,从公司出发时需要打一次卡,回到公司时需要打一次卡。  员工请假需要填写请假条,请假分为事假、病假、年假等多种情况,请假需要直接 领导审批,甚至还需要高层领导的审批。  行政部每天统计考勤信息,包括打卡信息、外出信息、请假信息,每月将考勤汇总 信息提交给财务部。  财务部根据考勤汇总信息,调整员工的薪金。 但这样的管理方式,出现了一些意外事件:  某员工想请年休假,但行政部告知该员工的当年度年休假已经休完了。年休假的管 理出现了问题,很可能会影响员工的工作积极性。  某员工投诉当月薪金多扣了钱,原因是考勤信息统计有误。于是财务部将责任推到 行政部,行政部推诿财务部要求不明确。  某天出现了紧急状况,高层领导想找员工 A 来处理,但员工 A 当天请了假,高层 领导并不知情。 公司高层期望通过考勤系统提高考勤工作的效率和准确性,避免因为考勤问题影响 正常工作。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值