From 3b715f7c2a5e6ef9fec3d5f7521803f617a09883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Thu, 5 Jun 2025 11:54:13 +0800 Subject: [PATCH] 1 --- attendance_iot/models/attendance_iot_user.py | 2 +- attendance_iot/security/ir.model.access.csv | 10 +- .../controllers/applet_attendance.py | 103 ++++++++---------- 3 files changed, 54 insertions(+), 61 deletions(-) diff --git a/attendance_iot/models/attendance_iot_user.py b/attendance_iot/models/attendance_iot_user.py index b469e4e83..457034188 100644 --- a/attendance_iot/models/attendance_iot_user.py +++ b/attendance_iot/models/attendance_iot_user.py @@ -2,7 +2,7 @@ from odoo import api, fields, models, tools from odoo.exceptions import ValidationError -class AttendanceIotUser(models.Model): +class AttendanceIotUser(models.Model): _name = 'attendance.iot.user' attendance_iot_id = fields.Many2one('attendance.iot', string="设备号") diff --git a/attendance_iot/security/ir.model.access.csv b/attendance_iot/security/ir.model.access.csv index 4570dc0aa..5fa500b13 100644 --- a/attendance_iot/security/ir.model.access.csv +++ b/attendance_iot/security/ir.model.access.csv @@ -1,6 +1,6 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_attendance_iot,attendance_iot,model_attendance_iot,base.group_user,1,1,1,1 -access_attendance_iot_manager,attendance_iot_manager,model_attendance_iot_manager,base.group_user,1,1,1,1 -access_attendance_iot_manager_wizard,attendance_iot_manager_wizard,model_attendance_iot_manager_wizard,base.group_user,1,1,1,1 -access_attendance_iot_datas,attendance_iot_datas,model_attendance_iot_datas,base.group_user,1,1,1,1 -access_attendance_iot_user,attendance_iot_user,model_attendance_iot_user,base.group_user,1,1,1,1 \ No newline at end of file +access_attendance_iot,attendance.iot,model_attendance_iot,base.group_user,1,1,1,1 +access_attendance_iot_manager,attendance.iot.manager,model_attendance_iot_manager,base.group_user,1,1,1,1 +access_attendance_iot_manager_wizard,attendance.iot.manager.wizard,model_attendance_iot_manager_wizard,base.group_user,1,1,1,1 +access_attendance_iot_datas,attendance.iot.datas,model_attendance_iot_datas,base.group_user,1,1,1,1 +access_attendance_iot_user,attendance.iot.user,model_attendance_iot_user,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/yuthon_hr_attendance/controllers/applet_attendance.py b/yuthon_hr_attendance/controllers/applet_attendance.py index 957635046..5d8157833 100644 --- a/yuthon_hr_attendance/controllers/applet_attendance.py +++ b/yuthon_hr_attendance/controllers/applet_attendance.py @@ -3,6 +3,8 @@ from odoo import models, fields, api, http, SUPERUSER_ID, _ from datetime import datetime import math import logging +from geopy.distance import geodesic +import json _logger = logging.getLogger(__name__) @@ -12,13 +14,15 @@ class AppletAttendance(Controller): 小程序考勤接口 """ - @route('/yuthon/att/today', methods=['POST'], type='http', auth='public', cors='*', csrf=False) + @route('/yuthon/att/today', methods=['POST'], type='http', auth='none', cors='*', csrf=False) def apple_yuthon_att_today(self, **kw): """ 小程序获取当前用户打卡信息 :return: { 'location': 经纬度-打卡地点, + 'att_longitude': '经度' + 'att_latitude': '纬度' 'class': [班次,开始时间-结束时间] 'user_id': 用户id 'check_in': [上班卡,正常] @@ -26,39 +30,38 @@ class AppletAttendance(Controller): 'state': 是否可以打卡['yes', 'no'] 'state_result': 不可打卡原因 'xxx' } - # 0.判断是否有用户,否则返回“此用户无权限” - # 1.根据经纬度判断是否可以打卡,如果不可以就直接返回“超出范围,不允许打卡” - # 2.把这个人的地址location,class,user_id,state-yes,state_result='' - # 2.check_in "YYYY-MM-DD HH:MM:SS" ['', ''] 如果没有传 '' - # 2.check_out "YYYY-MM-DD HH:MM:SS" ['', ''] 如果没有传 '' """ - user_id = int(kw.get('id')) - current_data = kw.get('current_data') - current_data = datetime.strptime(current_data, "%Y-%m-%d") + user_id = int(kw['user_id']) + current_data = fields.Date.today() user_id = request.env.user.search([('id', '=', user_id)]) if not user_id: - return {"Msg": "此用户没有权限,请联系管理员配置。%s" % (user_id.name), 'state': 'no', 'state_result': '无权限'} - # 1.根据经纬度判断是否可以打卡,如果不可以就直接返回“超出范围,不允许打卡” - employee = request.env["hr.employee"].search([('user_id', '=', user_id)]) - company_id = employee.company_id - address_id = request.env["applet.address"].search([('name', '=', company_id.name)]) - att_longitude = kw.get('att_longitude')# 打卡经度 - att_latitude = kw.get('att_latitude') # 打卡纬度 - longitude = address_id.longitude # 要求经度 - latitude = address_id.latitude # 要求纬度 - if not self.is_in_range(att_latitude, att_longitude, latitude, longitude, threshold=0.3): + result = "此用户没有权限,请联系管理员配置。%s" % (user_id.name) + return {'state': 'no', 'state_result': result} + user_id = request.env(user=SUPERUSER_ID)["res.users"].search([('id', '=', user_id.id)]) + employee_id = request.env(user=SUPERUSER_ID)["hr.employee"].search([('name', '=', user_id.name)]) + address_id = request.env(user=SUPERUSER_ID)["applet.address"].search([('address_name', '=', employee_id.company_id.name)]) + if not address_id: + return {'state': 'no', 'state_result': "该打卡地址没有配置"} + att_longitude = kw['att_longitude'] # 打卡经度 + att_latitude = kw['att_latitude'] # 打卡纬度 + longitude = address_id.longitude # 要求经度 + latitude = address_id.latitude # 要求纬度 + if not att_longitude or not att_latitude: + return {"Msg": "经纬度必填"} + card_rice = address_id.card_rice * 0.001 # 打卡范围 + if not self.is_within_radius_geopy(att_latitude, att_longitude, latitude, longitude, card_rice): return {"Msg": "超出范围,不允许打卡", 'state': 'no', 'state_result': '超出范围'} else: - attendance_id = request.env["hr.attendance"].search([('employee_id', '=', employee.id), ('current_data', '=', current_data)]) + attendance_id = request.env(user=SUPERUSER_ID)["hr.attendance"].search([('employee_id', '=', employee_id.id), ('current_data', '=', current_data)]) check_in = attendance_id.check_in check_out = attendance_id.check_out attendance_class = attendance_id.shift_name + class_start_time = '2025-6-5 11:36' # + class_end_time = '2025-6-5 11:36' # is_early_card = attendance_id.is_early_card is_late_card = attendance_id.is_late_card - # 预处理 check_in 和 check_out 的状态 check_in_status = '未打卡' if not check_in else ('迟到' if is_late_card else '正常') check_out_status = '未打卡' if not check_out else ('早退' if is_early_card else '正常') - # 确定最终状态描述 state_result = '正常' if is_late_card and is_early_card: state_result = '迟到+早退' @@ -68,20 +71,31 @@ class AppletAttendance(Controller): state_result = '早退' elif not check_in or not check_out: state_result = '未打卡' - # 格式化时间字符串 check_in_str = check_in.strftime('%H:%M:%S') if check_in else ' ' check_out_str = check_out.strftime('%H:%M:%S') if check_out else ' ' - - # 统一返回结构 - return { - 'location': '%s' % address_id.address_name2, - 'user_id': user_id, - 'state': 'yes', # 无论是否打卡,state 始终为 'yes'(根据业务调整) - 'class': attendance_class, - 'state_result': state_result, - 'check_in': [check_in_str, check_in_status], - 'check_out': [check_out_str, check_out_status], + return_data = { + 'data': { + 'location': '%s' % address_id.address_name, + 'user_id': user_id.id, + 'state': 'yes', + 'class': [attendance_class, class_start_time, class_end_time], + 'state_result': state_result, + 'check_in': check_in_str + ' ' + check_in_status, + 'check_out': check_out_str + ' ' + check_out_status, + }, + 'code': '200', + 'message': 'OK', } + return json.dumps(return_data) + + def is_within_radius_geopy(self, lat1, lon1, lat2, lon2, radius_km): + """ + 使用geopy库判断是否在范围内 + """ + point1 = (lat1, lon1) + point2 = (lat2, lon2) + distance = geodesic(point1, point2).kilometers + return distance <= radius_km @route('/yuthon/att/record', methods=['POST'], type='json', auth='none', csrf=False) def apple_yuthon_att_record(self, **kw): @@ -100,25 +114,4 @@ class AppletAttendance(Controller): 'checkin_time': checkin_time, # 打卡时间 'source': 'mini_programs', # 打卡来源 'location_title': location_title, # 打卡地点 - }) - print("---------------------------------------------测试成功---------------------------------------") - - - - def calculate_distance(self, lat1, lon1, lat2, lon2): - """计算两个经纬度点之间的球面距离(公里)""" - # 将角度转换为弧度 - lat1, lon1, lat2, lon2 = map(math.radians, [lat1, lon1, lat2, lon2]) - # Haversine公式 - dlat = lat2 - lat1 - dlon = lon2 - lon1 - a = math.sin(dlat / 2) ** 2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2 - c = 2 * math.asin(math.sqrt(a)) - # 地球半径(公里) - earth_radius = 6371 - return c * earth_radius - # 判断是否在范围内(例如允许范围为0.5公里) - - def is_in_range(self, att_lat, att_lon, req_lat, req_lon, threshold=0.5): - distance = self.calculate_distance(att_lat, att_lon, req_lat, req_lon) - return distance <= threshold \ No newline at end of file + }) \ No newline at end of file