fonchain-fiee/pkg/service/clock_in_handle.go
2025-02-19 14:24:15 +08:00

421 lines
14 KiB
Go

package service
import (
"context"
"errors"
"fmt"
"github.com/fonchain_enterprise/fonchain-main/api/oa"
"github.com/fonchain_enterprise/fonchain-main/pkg/cache"
"github.com/fonchain_enterprise/fonchain-main/pkg/e"
"github.com/fonchain_enterprise/fonchain-main/pkg/logic/oa_logic"
"github.com/fonchain_enterprise/fonchain-main/pkg/model/login"
"github.com/fonchain_enterprise/fonchain-main/pkg/model/oa_model"
"github.com/fonchain_enterprise/fonchain-main/pkg/utils/holiday"
"github.com/go-redis/redis"
"github.com/jinzhu/copier"
"sort"
"strconv"
"strings"
"time"
)
// ConfirmActionTypeWithOaRecord
/*
获取 打卡类型 ,工作时间, 下一次打卡是 上班卡还是下班 结合 当天的考勤记录
*/
func ConfirmActionTypeWithOaRecord(userInfo login.Info, nowTime time.Time, times []*oa_model.TimeOption) (actionType, workTime, nextOnOff string, err error) {
// 计算 考勤时间点
var currentClickInTime time.Time
_, _, _, currentClickInTime, err = ConfirmCurrentClickInTime(userInfo, times)
if err != nil {
return "", "", "", err
}
actionType, workTime = oa_logic.ConfirmActionTypeV2(nowTime, currentClickInTime, times)
nextOnOff = ConfirmOnOffV2(workTime, times)
return actionType, workTime, nextOnOff, nil
}
func ConfirmCurrentClickInTime(userInfo login.Info, times []*oa_model.TimeOption) (nextOnOff, currentOnOff, currentWorkTime string, currentClickInTime time.Time, err error) {
// 查询 员工 当天的 考勤 记录
oaRecordReq := new(oa.OARecordReq)
oaRecordReq.StaffUID = userInfo.ID
oaRecordReq.Dates = []string{time.Now().Format("2006-01-02")}
oaRecordReq.Page = 1
oaRecordReq.PageSize = 9999
oaRecordRes, err := GrpcOAImpl.OARecord(context.Background(), oaRecordReq)
if err != nil {
return "", "", "", time.Time{}, err
}
if len(oaRecordRes.Data) > 0 { // 获取 当天 最后一次打卡点
oaRecord := oaRecordRes.Data[len(oaRecordRes.Data)-len(oaRecordRes.Data)]
if len(oaRecord.Records) > 0 {
nextOnOff = oa_model.OnWork
hour := oaRecord.Records[len(oaRecord.Records)-1].WorkTime
// 最后一次 打卡记录 如果是 current on -> next off ; current off -> next on
currentOnOff = oaRecord.Records[len(oaRecord.Records)-1].OnOff
if currentOnOff == oa_model.OnWork {
nextOnOff = oa_model.OffWork
}
// 最后一次 打卡 是 最后一次 下班卡 则 next off
if oaRecord.Records[len(oaRecord.Records)-1].WorkTime == times[len(times)-1].OffWorkTime {
nextOnOff = oa_model.OffWork
}
currentClickInTime, _ = time.ParseInLocation("2006-01-02 15:04:05", time.Now().Format("2006-01-02")+" "+hour+":00", time.Local)
currentWorkTime = hour
}
}
return
}
func ConfirmOnOffV1(nowTime time.Time, times []*oa_model.TimeOption) string {
nowTimef, _ := time.ParseInLocation("2006-01-02 15:04:05", nowTime.Format("2006-01-02 15:04:05"), time.Local)
for i := 0; i < len(times); i++ {
nextOnTime := time.Time{}
nextOffTime := time.Time{}
if i+1 < len(times) {
nextOnTime, _ = time.ParseInLocation("2006-01-02 15:04:05", nowTime.Format("2006-01-02")+" "+times[i+1].OnWorkTime+":00", time.Local)
nextOffTime, _ = time.ParseInLocation("2006-01-02 15:04:05", nowTime.Format("2006-01-02")+" "+times[i+1].OffWorkTime+":00", time.Local)
}
onTime, _ := time.ParseInLocation("2006-01-02 15:04:05", nowTime.Format("2006-01-02")+" "+times[i].OnWorkTime+":00", time.Local)
offTime, _ := time.ParseInLocation("2006-01-02 15:04:05", nowTime.Format("2006-01-02")+" "+times[i].OffWorkTime+":00", time.Local)
if nowTimef.Before(onTime) {
return oa_model.OnWork
}
if !nowTimef.Before(offTime) {
if nextOnTime.IsZero() && nextOffTime.IsZero() {
return oa_model.OffWork
} else {
if !nowTimef.After(nextOnTime) {
return oa_model.OffWork
}
if nowTimef.After(nextOnTime) && nowTimef.Before(nextOffTime) {
return oa_model.OnWork
}
}
}
}
return ""
}
func ConfirmOnOffV2(workTime string, times []*oa_model.TimeOption) string {
for i := 0; i < len(times); i++ {
if workTime == times[i].OnWorkTime {
return oa_model.OnWork
}
if workTime == times[i].OffWorkTime {
return oa_model.OffWork
}
}
return ""
}
// ConfirmApplyType
/*
结合 oa审批申请 获取 当前 考勤时间 的 审批类型
*/
func ConfirmApplyType(userInfo login.Info, workTimeFull time.Time, times []*oa_model.TimeOption) (applyType string, workTime string, err error) {
// 查询 oa 申请 请假 调休
applyReq := new(oa.ApplyRecordReq)
applyReq.ApplyType = []string{
oa_model.TypeLeave, // 请假
oa_model.TypeSick,
oa_model.TypeAnnualLeave,
oa_model.TypeMaritalLeave,
oa_model.TypeMatingCheckLeave,
oa_model.TypeMaternityLeave,
oa_model.TypePaternityLeave,
oa_model.TypeParentalLeave,
oa_model.TypeNursingLeave,
oa_model.TypeFuneralLeave,
oa_model.TypeAbortLeave,
oa_model.TypeBreastFeedingLeave,
oa_model.TypeDayOff, // 调休
oa_model.TypeOverTime, // 加班
oa_model.TypeOutWork, // 外勤
}
applyReq.ApplyStatus = []int32{e.ApprovalWorkStatusOk}
applyReq.BeginTime = time.Now().Format("2006-01-02")
applyReq.EndTime = time.Now().Format("2006-01-02")
applyReq.StaffUID = userInfo.ID
applyRes, err := GrpcOAImpl.QueryOaApply(context.Background(), applyReq)
if err != nil {
return applyType, workTime, err
}
//workTimeFull, _ := time.ParseInLocation("2006-01-02 15:04:05", nowTime.Format("2006-01-02")+" "+workTime+":00", time.Local)
if len(applyRes.Data) > 0 {
for i := 0; i < len(applyRes.Data); i++ {
var (
applyStartTimeStr string
applyEndTimeStr string
applyStartTime time.Time
applyEndTime time.Time
)
applyTimes := make([]oa_model.ApplyTime, 0)
_ = copier.CopyWithOption(&applyTimes, applyRes.Data[i].ApplyTimes, copier.Option{DeepCopy: true})
applyStartTimeStr, applyEndTimeStr = oa_logic.ConvertApplyTimeV2(applyTimes, times)
applyStartTime, _ = time.ParseInLocation("2006-01-02 15:04:05", applyStartTimeStr, time.Local)
applyEndTime, _ = time.ParseInLocation("2006-01-02 15:04:05", applyEndTimeStr, time.Local)
if !workTimeFull.Before(applyStartTime) && !workTimeFull.After(applyEndTime) {
applyType = applyRes.Data[i].ApplyType
break
}
}
}
return applyType, workTime, err
}
func ConfirmIsWeek(currentWeek int32, positionUsers []oa_model.QueryTimeOption) (isWeek int32, cycle string, err error) {
// 记录 打卡 周期
startDay, endDay, err := WorkingTimeWeekBest(positionUsers)
if err != nil {
fmt.Println("WorkDays err is :", err.Error())
return
}
respDate, err := holiday.GetSingleData(time.Now().Format("20060102"), true)
if err != nil {
fmt.Println("GetSingleData err is :", err.Error())
return
}
for i := startDay; i <= endDay; i++ {
if i == int(currentWeek) {
isWeek = oa_model.IsWork
break
}
isWeek = oa_model.IsFree
}
if endDay == 7 {
endDay = 0
}
if isWeek == oa_model.IsWork && respDate.Type == 1 {
isWeek = oa_model.IsFree
}
if isWeek == oa_model.IsFree && respDate.Type == 0 && (respDate.WeekDay == 6 || respDate.WeekDay == 0) {
isWeek = oa_model.IsWork
}
cycle = strings.Join([]string{strconv.Itoa(startDay), strconv.Itoa(endDay)}, ",")
return
}
func ConfirmIsNeedClockIn(currentWorkTime string, nowTime time.Time, times []*oa_model.TimeOption) bool {
currentClickInTime, _ := time.ParseInLocation("2006-01-02 15:04:05", time.Now().Format("2006-01-02")+" "+currentWorkTime+":00", time.Local)
for i := 0; i < len(times); i++ {
if currentWorkTime == times[i].OnWorkTime {
OnTime, _ := time.ParseInLocation("2006-01-02 15:04:05", time.Now().Format("2006-01-02")+" "+times[i].OnWorkTime+":01", time.Local)
if nowTime.Before(OnTime) {
return true
}
}
if currentWorkTime == times[i].OffWorkTime {
if i+1 < len(times) {
nextOnTime, _ := time.ParseInLocation("2006-01-02 15:04:05", time.Now().Format("2006-01-02")+" "+times[i+1].OnWorkTime+":01", time.Local)
if nowTime.Before(nextOnTime) && nowTime.After(currentClickInTime) {
return false
}
}
}
}
return false
}
func ConfirmIsWork(cycle string, workDate string) (isWork bool) {
isWork = false
if cycle == "" {
return isWork
}
weeks := strings.Split(cycle, ",")
single, err := holiday.GetSingleData(holiday.ConvertTime(workDate), true)
if err != nil {
fmt.Println("==================== GetSingleData ====================")
fmt.Println("err : ", err.Error())
fmt.Println("==================== GetSingleData ====================")
return false
}
if single.WeekDay == 7 {
single.WeekDay = 0
}
start, _ := strconv.Atoi(weeks[0])
end, _ := strconv.Atoi(weeks[1])
for i := start; i <= end; i++ {
if i == int(single.WeekDay) {
isWork = true
break
}
}
if isWork == true && (single.Type == 1 || single.Type == 2) {
isWork = false
}
if isWork == false && single.Type == 0 && (single.WeekDay == 6 || single.WeekDay == 0) {
//if isWork == false && single.Type == 0 {
isWork = true
}
return
}
// 周末打卡时 只记录 第一次上班卡 和 最后一次下班卡
func OneRoundConfirmCurrentClickInTime(userInfo login.Info) (nextOnOff string, err error) {
// 查询 员工 当天的 考勤 记录
oaRecordReq := new(oa.OARecordReq)
oaRecordReq.StaffUID = userInfo.ID
oaRecordReq.Dates = []string{time.Now().Format("2006-01-02")}
oaRecordReq.Page = 1
oaRecordReq.PageSize = 9999
oaRecordRes, err := GrpcOAImpl.OARecord(context.Background(), oaRecordReq)
if err != nil {
return "", err
}
if len(oaRecordRes.Data) > 0 { // 获取 当天 最后一次打卡点
oaRecord := oaRecordRes.Data[len(oaRecordRes.Data)-len(oaRecordRes.Data)]
if len(oaRecord.Records) > 0 {
nextOnOff = oa_model.OffWork
}
}
return
}
// 考勤记录排序
func OrderByRecord(records []oa_model.Record) []oa_model.Record {
if records == nil {
return records
}
//fmt.Printf("处理 休息日 打卡记录: %+v\n", records)
recordsMap := make(map[string]oa_model.Record)
times := make([]string, 0)
for i := 0; i < len(records); i++ {
if records[i].WorkTime != "" && records[i].IsWork == oa_model.IsWork {
recordsMap[records[i].WorkTime] = records[i]
times = append(times, records[i].WorkTime)
} else if records[i].ActionTime != "" && records[i].WorkTime == "" {
recordsMap[strings.Split(records[i].ActionTime, " ")[1]] = records[i]
times = append(times, strings.Split(records[i].ActionTime, " ")[1])
} else if records[i].IsWork == oa_model.IsFree {
if records[i].OnOff == oa_model.OffWork {
if i == len(records)-1 {
recordsMap[strings.Split(records[i].ActionTime, " ")[1]] = records[i]
times = append(times, strings.Split(records[i].ActionTime, " ")[1])
}
continue
}
recordsMap[strings.Split(records[i].ActionTime, " ")[1]] = records[i]
times = append(times, strings.Split(records[i].ActionTime, " ")[1])
}
}
sort.Strings(times)
newRecords := make([]oa_model.Record, 0)
for i := 0; i < len(times); i++ {
newRecords = append(newRecords, recordsMap[times[i]])
}
return newRecords
}
// 根据经纬度确认是否可以打卡
func ConfirmIsInCompany(lng, lat string) (bool, error) {
if lng == "" || lat == "" {
return false, errors.New(e.ErrLatLog)
}
lngF, _ := strconv.ParseFloat(lng, 64)
latF, _ := strconv.ParseFloat(lat, 64)
if result, err := cache.GeoRadius(cache.ChickInMap, 0, 0, &redis.GeoRadiusQuery{
Radius: 20100,
Unit: "km",
WithGeoHash: false,
WithDist: false,
}); err != nil || len(result) < 1 {
// 从数据库中获取公司的经纬度
res, err := GrpcOAImpl.QueryOaSetting(context.Background(), &oa.OaSettingReq{
Keyword: cache.ChickInMap,
})
if err != nil {
return false, errors.New(e.ErrChickInMap)
}
fmt.Println("=================================== ConfirmIsInCompany QueryOaSetting res =======================================================")
fmt.Printf("res : %+v\n", res.Data)
fmt.Println("=================================== ConfirmIsInCompany QueryOaSetting res =======================================================")
geoLocations := make([]*redis.GeoLocation, 0)
geoLocationsLength := 0
if len(res.Data) > 0 {
for i := 0; i < len(res.Data); i++ {
if res.Data[i].Keyword == cache.ChickInMap {
for j := 0; j < len(res.Data[i].Value.ChickInMap); j++ {
geoLocationsLength++
geoLocation := new(redis.GeoLocation)
geoLocation.Name = res.Data[i].Value.ChickInMap[j].Address
geoLocation.Latitude, _ = strconv.ParseFloat(res.Data[i].Value.ChickInMap[j].Lat, 64)
geoLocation.Longitude, _ = strconv.ParseFloat(res.Data[i].Value.ChickInMap[j].Lng, 64)
if geoLocation.Latitude == 0 || geoLocation.Longitude == 0 {
return false, errors.New(e.ErrDbChickInMap)
}
geoLocations = append(geoLocations, geoLocation)
}
}
}
}
fmt.Println("=================================== ConfirmIsInCompany geoLocations =======================================================")
fmt.Printf("geoLocations : %+v\n", geoLocations)
fmt.Println("=================================== ConfirmIsInCompany geoLocations =======================================================")
saveNum, err := cache.GeoAdd(cache.ChickInMap, geoLocations...)
if err != nil || saveNum != int64(geoLocationsLength) {
return false, errors.New(e.ErrCacheChickInMap)
}
}
// 使用GEORADIUS命令过滤出100米以内的地点
results, err := cache.GeoRadius(cache.ChickInMap, lngF, latF, &redis.GeoRadiusQuery{
Radius: 100,
Unit: "m",
WithGeoHash: false,
WithDist: true,
})
if err != nil {
return false, errors.New(e.ErrConfirmChickInMap)
}
for _, result := range results {
// 检查距离是否在100米以内
if result.Dist <= 100 {
return true, nil
}
}
return false, nil
}