# -*- coding: utf-8 -*- from odoo import http from odoo.http import request from .game_api import _json, _post_body from ..models.chat import GUIDE_NAME class ChatApiController(http.Controller): # ============ 世界聊天 API ============ # 实时采用前端轮询(1.5s 增量拉取),零额外依赖、稳;后续可平滑升级为 bus 推送。 @http.route('/game/api/chat/channels', type='http', auth='public', methods=['GET'], csrf=False) def chat_channels(self, **kw): """当前用户可见频道:世界频道 + AI向导频道(均公开) + 自己参与的私聊/公会。""" uid = request.session.uid Channel = request.env['game.chat.channel'].sudo() public = [('channel_type', 'in', ['world', 'guide'])] if uid: domain = ['|'] + public + [('member_ids', 'in', [uid])] else: domain = public recs = Channel.search_read(domain, ['id', 'name', 'channel_type'], order='sequence, id') return _json({'items': [{ 'id': r['id'], 'name': r['name'], 'type': r['channel_type'], } for r in recs]}) @http.route('/game/api/chat/messages', type='http', auth='public', methods=['GET'], csrf=False) def chat_messages(self, **kw): """拉消息:channel_id 必填;after_id 增量拉取;limit 默认 50。""" channel_id = request.params.get('channel_id') after_id = request.params.get('after_id') try: limit = int(request.params.get('limit') or 50) except (TypeError, ValueError): limit = 50 if not channel_id: return _json({'error': '缺少 channel_id', 'items': []}) try: channel_id = int(channel_id) if after_id: after_id = int(after_id) except (TypeError, ValueError): return _json({'error': '参数格式错误', 'items': []}) Msg = request.env['game.chat.message'].sudo() domain = [('channel_id', '=', channel_id)] if after_id: domain.append(('id', '>', after_id)) recs = Msg.search_read( domain, ['id', 'author_id', 'author_name', 'body', 'create_date'], order='id asc', limit=limit) uid = request.session.uid items = [{ 'id': r['id'], 'author_name': r['author_name'] or '匿名', 'body': r['body'] or '', 'create_date': str(r['create_date']), 'is_self': bool(uid and r.get('author_id') and r['author_id'][0] == uid), 'is_guide': bool(r['author_name'] == GUIDE_NAME), } for r in recs] return _json({'items': items}) @http.route('/game/api/chat/send', type='http', auth='public', methods=['POST', 'OPTIONS'], csrf=False) def chat_send(self, **kw): """发消息:需登录;body 限 1000 字。昵称取游戏角色名(hr.employee)。""" uid = request.session.uid if not uid: return _json({'ok': False, 'error': '请先登录'}) data = _post_body() channel_id = data.get('channel_id') body = (data.get('body') or '').strip() if not channel_id: return _json({'ok': False, 'error': '缺少 channel_id'}) if not body: return _json({'ok': False, 'error': '消息不能为空'}) if len(body) > 1000: return _json({'ok': False, 'error': '消息过长(最多 1000 字)'}) emp = request.env['hr.employee'].sudo().search( [('user_id', '=', uid)], limit=1) author_name = emp.name if emp else request.env['res.users'].sudo().browse(uid).name try: channel_id = int(channel_id) except (TypeError, ValueError): return _json({'ok': False, 'error': '频道参数错误'}) msg = request.env['game.chat.message'].sudo().create({ 'channel_id': channel_id, 'author_id': uid, 'author_name': author_name, 'body': body, }) # AI 向导自动回复:仅对 guide 类型频道生效,与世界频道隔离 guide_reply = None ch = request.env['game.chat.channel'].sudo().browse(channel_id) if ch.exists() and ch.channel_type == 'guide': reply_text = ch.generate_guide_reply(body) if reply_text: reply = request.env['game.chat.message'].sudo().create({ 'channel_id': channel_id, 'author_id': False, 'author_name': GUIDE_NAME, 'body': reply_text, }) guide_reply = { 'id': reply.id, 'author_name': GUIDE_NAME, 'body': reply_text, 'create_date': str(reply.create_date), 'is_self': False, 'is_guide': True, } return _json({ 'ok': True, 'message': { 'id': msg.id, 'author_name': author_name, 'body': body, 'create_date': str(msg.create_date), 'is_self': True, }, 'guide_reply': guide_reply, })