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 }