初始
This commit is contained in:
308
api/attendanceMachine/service.go
Normal file
308
api/attendanceMachine/service.go
Normal file
@ -0,0 +1,308 @@
|
||||
// @Author cory 2025/3/5 10:27:00
|
||||
package attendanceMachine
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/tiger1103/gfast/v3/api/v1/common/coryCommon"
|
||||
"github.com/tiger1103/gfast/v3/internal/app/system/dao"
|
||||
wxDao "github.com/tiger1103/gfast/v3/internal/app/wxApplet/dao"
|
||||
wxBusAttendance "github.com/tiger1103/gfast/v3/internal/app/wxApplet/logic/busAttendance"
|
||||
wxModel "github.com/tiger1103/gfast/v3/internal/app/wxApplet/model"
|
||||
wxDo "github.com/tiger1103/gfast/v3/internal/app/wxApplet/model/do"
|
||||
"github.com/tiger1103/gfast/v3/library/liberr"
|
||||
tool "github.com/tiger1103/gfast/v3/utility/coryUtils"
|
||||
"golang.org/x/net/context"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (w AttendanceMachineApi) TaskCreate(ctx context.Context, req *EquipmentTimeClockReq) (res *EquipmentTimeClockRes, err error) {
|
||||
res = new(EquipmentTimeClockRes)
|
||||
err = AttendanceMachine(ctx, req)
|
||||
res.Result = "0"
|
||||
res.Msg = ""
|
||||
|
||||
r := ghttp.RequestFromCtx(ctx)
|
||||
r.Response.ClearBuffer()
|
||||
r.Response.WriteJson(res)
|
||||
return
|
||||
}
|
||||
|
||||
func AttendanceMachine(ctx context.Context, req *EquipmentTimeClockReq) (err error) {
|
||||
logs := req.Logs[0]
|
||||
f1 := logs.Location.Longitude
|
||||
f2 := logs.Location.Latitude
|
||||
res, err := wxBusAttendance.ThisInverseGeocodingFunc(f1 + "," + f2)
|
||||
liberr.ErrIsNil(ctx, err)
|
||||
// 上次打卡距离此次打卡不超过3分支,那么提示“你刚刚已打上/下班卡了哦!”
|
||||
currentTime := time.Now()
|
||||
date := tool.New().GetFormattedDate(currentTime)
|
||||
var bair *wxModel.BusAttendanceInfoRes
|
||||
err = wxDao.BusAttendance.Ctx(ctx).
|
||||
Where("openid", logs.UserID).
|
||||
Where("printing_date", date).
|
||||
Fields("clock_on,commuter").OrderDesc("clock_on").Limit().Scan(&bair)
|
||||
liberr.ErrIsNil(ctx, err, "打卡失败!")
|
||||
if err == nil && bair != nil {
|
||||
//判断t1和t2是否相差180秒,如果小于180秒就直接return
|
||||
flag, err := IsWithinThreeMinutes(currentTime, bair.ClockOn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if flag {
|
||||
txt := ""
|
||||
if bair.Commuter == "1" {
|
||||
txt = "您已上班打卡成功!杜绝重复打卡!"
|
||||
} else {
|
||||
txt = "您已下班打卡成功!杜绝重复打卡!"
|
||||
}
|
||||
err = errors.New(txt)
|
||||
return err
|
||||
}
|
||||
}
|
||||
lng, err := strconv.ParseFloat(f1, 64)
|
||||
if err != nil {
|
||||
err = errors.New("精准度转换错误!")
|
||||
return
|
||||
}
|
||||
lat, err := strconv.ParseFloat(f2, 64)
|
||||
if err != nil {
|
||||
err = errors.New("纬度转换错误!")
|
||||
return
|
||||
}
|
||||
lng, lat = coryCommon.GCJ02toWGS84(lng, lat)
|
||||
// 0-0、判断是否开启打卡功能 (禁止打卡和离职都不允许打卡)
|
||||
var userInfo *wxModel.BusConstructionUserInfoRes
|
||||
err = dao.BusConstructionUser.Ctx(ctx).Where("openid", logs.UserID).Scan(&userInfo)
|
||||
if userInfo.TeamId == 0 {
|
||||
err = errors.New("当前用户暂无班组,无法打卡!")
|
||||
return
|
||||
}
|
||||
|
||||
// 查看当前用户的打卡时间是否请假,如果请假就提示不用打卡
|
||||
count, err := wxDao.BusAskforleave.Ctx(ctx).
|
||||
Where(wxDao.BusAskforleave.Columns().ProjectId, userInfo.ProjectId).
|
||||
Where(wxDao.BusAskforleave.Columns().Openid, userInfo.Openid).
|
||||
Where("(" +
|
||||
"DATE_FORMAT(" + date + ",'%Y-%m-%d') BETWEEN DATE_FORMAT(start_time,'%Y-%m-%d') AND DATE_FORMAT(end_time,'%Y-%m-%d')" +
|
||||
" or " +
|
||||
"DATE_FORMAT(" + date + ",'%Y-%m-%d') BETWEEN DATE_FORMAT(start_time,'%Y-%m-%d') AND DATE_FORMAT(end_time,'%Y-%m-%d')" +
|
||||
")").Count()
|
||||
liberr.ErrIsNil(ctx, err)
|
||||
if count > 0 {
|
||||
liberr.ErrIsNil(ctx, errors.New("您已请假,无需打卡!"))
|
||||
return
|
||||
}
|
||||
|
||||
// 部分班组可以直接跳过在范围内打卡,获取当前用户班组,看看是否是可以在任何地点打卡
|
||||
value, errvalue := wxDao.SysProjectTeam.Ctx(ctx).Where("id", userInfo.TeamId).Fields("is_clock_in").Value()
|
||||
if errvalue != nil {
|
||||
err = errors.New("获取班组打卡状态失败!")
|
||||
return err
|
||||
}
|
||||
if value.String() == "2" {
|
||||
goto breakHere
|
||||
}
|
||||
// 计算是否在打卡范围中
|
||||
if true {
|
||||
// 查询当前用户是否有加入班组,有的话看看是什么项目,再根据项目获取到方正,最后根据方正数据得出打卡范围
|
||||
projectId, _ := wxDao.SysProjectTeamMember.Ctx(ctx).As("a").
|
||||
LeftJoin("sys_project_team as b on a.team_id = b.id").
|
||||
Fields("a.project_id").
|
||||
Where("a.openid", logs.UserID).Value()
|
||||
if projectId != nil {
|
||||
var qf []*wxBusAttendance.ProjectPunchRangeRes
|
||||
err = g.DB().Model("sys_project_punch_range").Where("project_id", projectId).Fields("punch_range").Scan(&qf)
|
||||
if err != nil {
|
||||
err = errors.New("系统错误,请联系管理员!")
|
||||
return err
|
||||
}
|
||||
if len(qf) == 0 {
|
||||
err = errors.New("未设置打卡范围!请联系管理员设置!")
|
||||
return err
|
||||
}
|
||||
countI := len(qf)
|
||||
countII := 0
|
||||
for _, dakaStr := range qf {
|
||||
countII = countII + 1
|
||||
var dataInfo coryCommon.DetailedMap
|
||||
err = json.Unmarshal([]byte(fmt.Sprint(dakaStr.PunchRange)), &dataInfo.Positions)
|
||||
flag := coryCommon.RectangularFrameRange(dataInfo, lng, lat)
|
||||
if flag {
|
||||
goto breakHere
|
||||
} else {
|
||||
// 没有一次范围匹配上,那就直接返回
|
||||
if countII == countI {
|
||||
err = errors.New("不在范围内,打卡无效!")
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = errors.New("未指定打卡范围,请联系管理员!")
|
||||
return err
|
||||
}
|
||||
}
|
||||
breakHere:
|
||||
if userInfo.Status == "1" {
|
||||
err = errors.New("已离职,请联系管理员!")
|
||||
return
|
||||
}
|
||||
if userInfo.Clock == "2" {
|
||||
err = errors.New("已禁止打卡,请联系管理员!")
|
||||
return
|
||||
}
|
||||
|
||||
if len(strings.Trim(userInfo.LeaveDate.String(), "")) != 0 {
|
||||
err = errors.New("已退场,请联系管理员!")
|
||||
return
|
||||
}
|
||||
clockStatus := ""
|
||||
// 1、获取当日时间
|
||||
// 2、查询今日打卡记录
|
||||
gm := wxDao.BusAttendance.Ctx(ctx).
|
||||
Where(wxDao.BusAttendance.Columns().Openid, logs.UserID).
|
||||
Where(wxDao.BusAttendance.Columns().PrintingDate, date)
|
||||
// 3、如果有判断是否打了上班卡,如果没有就打上班卡
|
||||
count1, _ := gm.Where("commuter", "1").Count()
|
||||
if count1 == 0 {
|
||||
// 打上班卡
|
||||
clockStatus = "1"
|
||||
}
|
||||
// 4、如果有判断是否打了下班卡,如果没有就打下班卡
|
||||
count2, _ := gm.Where("commuter", "2").Count()
|
||||
if clockStatus == "" && count2 == 0 {
|
||||
// 打下班卡
|
||||
clockStatus = "2"
|
||||
}
|
||||
// 5、上下班卡都打了,那么就提示今日打卡完成
|
||||
if count1 != 0 && count2 != 0 {
|
||||
// 已完成当日打卡
|
||||
err = errors.New("当日打卡已完成!")
|
||||
return err
|
||||
}
|
||||
|
||||
// 提交数据
|
||||
name := userInfo.NickName
|
||||
if userInfo.UserName != "" {
|
||||
name = userInfo.UserName
|
||||
}
|
||||
|
||||
// 将base64人脸换成图片存储到本地
|
||||
imagePath, err := DecodeBase64Image(logs.Photo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rpath := coryCommon.ResourcePublicToFunc(imagePath, 1)
|
||||
|
||||
attendance := wxDo.BusAttendance{
|
||||
PacePhoto: rpath,
|
||||
UserName: name,
|
||||
ProjectId: userInfo.ProjectId,
|
||||
Openid: userInfo.Openid,
|
||||
Commuter: clockStatus,
|
||||
Lng: lng,
|
||||
Lat: lat,
|
||||
PrintingDate: date,
|
||||
}
|
||||
// 判断此次打卡状态 默认就为缺勤
|
||||
dateTime := tool.New().GetFormattedDateTime(time.Now())
|
||||
if userInfo.ProjectId > 0 {
|
||||
value, err := dao.SysProject.Ctx(ctx).WherePri(userInfo.ProjectId).Fields("punch_range as punchRange").Value()
|
||||
if err != nil {
|
||||
err = errors.New("获取项目打卡范围失败!")
|
||||
}
|
||||
split := strings.Split(value.String(), ",")
|
||||
strType := tool.New().TimeWithin(dateTime, split[0], split[1], clockStatus)
|
||||
attendance.IsPinch = strType
|
||||
attendance.PunchRange = value.String() // 记录项目的打卡时间,保留字段,后期有大用
|
||||
}
|
||||
// 获取此次打卡的日薪
|
||||
vl, err := dao.BusConstructionUser.Ctx(ctx).As("a").
|
||||
LeftJoin("bus_type_of_wage as f on a.type_of_work = f.type_of_work").
|
||||
Fields("if (a.salary>0, a.salary,f.standard) as salary").
|
||||
Where("a.openid", logs.UserID).Value()
|
||||
liberr.ErrIsNil(ctx, err, "获取薪资失败")
|
||||
// 记录打卡时间(上下班打卡时间统一用clock_on)
|
||||
attendance.ClockOn = dateTime
|
||||
attendance.DailyWage = vl
|
||||
attendance.Lng = lng
|
||||
attendance.Lat = lat
|
||||
attendance.Location = res.Regeocode.FormattedAddress
|
||||
_, err = wxDao.BusAttendance.Ctx(ctx).Insert(attendance)
|
||||
liberr.ErrIsNil(ctx, err, "添加失败")
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeBase64Image 函数用于解码 Base64 编码的图片数据并保存为图片
|
||||
func DecodeBase64Image(encodedStr string) (string, error) {
|
||||
// 进行 URL 解码
|
||||
decodedURLStr, err := url.QueryUnescape(encodedStr)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("URL 解码失败: %w", err)
|
||||
}
|
||||
|
||||
// 提取真正的 Base64 数据
|
||||
parts := strings.SplitN(decodedURLStr, ",", 2)
|
||||
if len(parts) != 2 {
|
||||
return "", fmt.Errorf("无效的 Base64 编码图片数据格式")
|
||||
}
|
||||
base64Data := parts[1]
|
||||
|
||||
// 进行 Base64 解码
|
||||
decodedData, err := base64.StdEncoding.DecodeString(base64Data)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Base64 解码失败: %w", err)
|
||||
}
|
||||
|
||||
ht := coryCommon.Helmet
|
||||
str := coryCommon.Ynr(ht)
|
||||
fn := coryCommon.FileName("face")
|
||||
dir, err := os.Getwd()
|
||||
str = dir + "/" + str + fn + ".jpg"
|
||||
str = filepath.ToSlash(str)
|
||||
|
||||
// 保存解码后的图片到文件
|
||||
err = ioutil.WriteFile(str, decodedData, 0644)
|
||||
if err != nil {
|
||||
fmt.Println("保存图片文件出错:", err)
|
||||
return "", fmt.Errorf("保存解码后的图片失败: %w", err)
|
||||
}
|
||||
return str, nil
|
||||
}
|
||||
|
||||
// 判断两个时间是否相差超过 3 分钟
|
||||
func isMinutesDifferenceGreaterThanThree(startTime, endTime time.Time) bool {
|
||||
// 计算时间差(单位:秒)
|
||||
diff := endTime.Sub(startTime).Seconds()
|
||||
|
||||
// 判断是否超过 180 秒(3 分钟)
|
||||
return diff > 180
|
||||
}
|
||||
|
||||
// 判断两个时间的秒数差是否 <= 180 秒
|
||||
func IsWithinThreeMinutes(currentTime time.Time, strTime string) (bool, error) {
|
||||
// 解析字符串时间,使用本地时区
|
||||
loc, _ := time.LoadLocation("Local") // 获取本地时区
|
||||
parsedTime, err := time.ParseInLocation("2006-01-02 15:04:05", strTime, loc)
|
||||
if err != nil {
|
||||
fmt.Println("时间解析错误:", err)
|
||||
return false, err
|
||||
}
|
||||
// 计算时间差(秒)
|
||||
diff := math.Abs(currentTime.Sub(parsedTime).Seconds())
|
||||
|
||||
// 判断是否相差 180 秒
|
||||
return diff <= 180, nil
|
||||
}
|
Reference in New Issue
Block a user