421 lines
14 KiB
Go
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
|
|
}
|