自定义开发odoo14的统计在线用户人数 (续)
上一篇是只统计在过去几分钟内有活动的用户是在线的。
今天我们来处理当浏览器关闭后,才算是离线用户,否则算在线用户。
要实现当浏览器关闭后才算用户离线的功能,在 Odoo 中较为复杂,因为 Odoo 服务器端无法直接检测到客户端浏览器的关闭事件。不过,我们可以通过一种近似的方法来实现这个功能:定期从客户端(浏览器)发送心跳信号到服务器,以表示用户仍然在线。如果服务器在一定时间内没有收到某个用户的心跳信号,则认为该用户已离线。
以下是按照这种思路修改和更新代码的方法:
from odoo import api, fields, models, http from datetime import timedelta class ResUsers(models.Model): _inherit = 'res.users' last_heartbeat_time = fields.Datetime("Last Heartbeat Time") @api.model def update_last_heartbeat(self): """ 更新用户的最后心跳时间 """ self.sudo().write({'last_heartbeat_time': fields.Datetime.now()}) def is_online(self): """ 判断用户是否在线 """ if not self.last_heartbeat_time: return False return fields.Datetime.now() - self.last_heartbeat_time < timedelta(minutes=5) class IrHttp(models.AbstractModel): _inherit = 'ir.http' @classmethod def _dispatch(cls): """ 拦截 HTTP 请求并更新用户的最后心跳时间 """ response = super(IrHttp, cls)._dispatch() current_user = http.request.env.user if current_user: current_user.update_last_heartbeat() return response
添加 JavaScript 代码
在 Odoo 中,我们需要添加一个 JavaScript 文件来定期发送心跳信号。这个 JavaScript 文件应该被包含在我们的模块中。
- 创建
static/src/js/heartbeat.js
文件:odoo.define('online_users_monitoring.heartbeat', function (require) { "use strict"; var session = require('web.session'); function sendHeartbeat() { session.rpc('/online_users_monitoring/heartbeat', {}); } $(window).on('beforeunload', sendHeartbeat); // 当浏览器窗口关闭时发送心跳 setInterval(sendHeartbeat, 300000); // 每5分钟发送一次心跳 });
在
__manifest__.py
中添加 JavaScript 文件的引用: -
{ # ... 其他代码 ... 'qweb': [ 'static/src/xml/*.xml', ], 'web.assets_backend': [ 'online_users_monitoring/static/src/js/heartbeat.js', ], }
添加 Controller 来处理心跳信号
在
controllers/main.py
文件中,我们需要添加一个控制器来处理心跳信号的请求。from odoo import http class OnlineUsersMonitoring(http.Controller): @http.route('/online_users_monitoring/heartbeat', type='json', auth='user') def heartbeat(self): request.env.user.update_last_heartbeat() return {'status': 'success'}
并在
__init__.py
文件中导入这个控制器:from . import controllers
更新
views/online_users_view.xml
在
online_users_view.xml
中,我们应该展示用户的最后心跳时间而不是最后活动时间。<odoo> <record id="view_online_users_tree" model="ir.ui.view"> <field name="name">online.users.tree</field> <field name="model">res.users</field> <field name="arch" type="xml"> <tree> <field name="name"/> <field name="login"/> <field name="last_heartbeat_time"/> </tree> </field> </record> <record id="action_online_users" model="ir.actions.act_window"> <field name="name">Online Users</field> <field name="res_model">res.users</field> <field name="view_mode">tree</field> <field name="view_id" ref="view_online_users_tree"/> <field name="domain">[('last_activity_time','>=', (DateTime.now() - timedelta(minutes=10)).strftime('%Y-%m-%d %H:%M:%S'))]</field> </record> <menuitem id="menu_online_users" name="Online Users" action="action_online_users" parent="base.menu_management"/> </odoo>
结论
这种方法依赖于客户端定期向服务器发送心跳信号。如果用户关闭了浏览器或者与服务器的连接断开,心跳信号将不再发送,从而允许服务器判断用户已离线。不过,请注意,这种方法可能不是完全准确的,因为如果用户的网络连接断开但浏览器没有关闭,用户也会被判断为离线。此外,这种方法可能会增加服务器的负担,因为每个在线用户都需要定期发送心跳信号。