# -*- coding: utf-8 -*- import logging from odoo import http from odoo.http import request _logger = logging.getLogger(__name__) def _json(data): headers = { 'Content-Type': 'application/json; charset=utf-8', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type', } try: origin = request.httprequest.headers.get('Origin') if origin: headers['Access-Control-Allow-Origin'] = origin headers['Access-Control-Allow-Credentials'] = 'true' else: headers['Access-Control-Allow-Origin'] = '*' except Exception: headers['Access-Control-Allow-Origin'] = '*' return request.make_response( __import__('json').dumps(data, ensure_ascii=False), headers=headers) def _post_body(): try: raw = request.httprequest.get_data(as_text=True) or '{}' return __import__('json').loads(raw) except Exception: return {} def _require_user(): uid = request.session.uid if not uid: return {'ok': False, 'error': 'auth'} user = request.env['res.users'].sudo().browse(uid) if not user.exists() or not user.active: return {'ok': False, 'error': 'auth'} return None def _get_world(): user = request.env['res.users'].sudo().browse(request.session.uid) \ if request.session.uid else request.env['res.users'] if not user.exists(): return None return request.env['yt.world'].sudo().get_or_create_for_user(user) class YtWorldChestApi(http.Controller): # ── 列表:当前世界某地块的活跃宝箱 ── @http.route('/yt_world/api/chests', type='http', auth='public', methods=['GET', 'OPTIONS'], csrf=False) def list_chests(self, **kw): auth_err = _require_user() if auth_err: return _json(auth_err) world = _get_world() if not world: return _json({'ok': False, 'error': 'no_world'}) domain = [('world_id', '=', world.id), ('state', '=', 'active')] q = kw.get('q'); r = kw.get('r') if q is not None and r is not None: # tile_ref 是 Json,按字典值过滤:保留该地块的宝箱 recs = request.env['yt.world.chest'].sudo().search(domain) recs = recs.filtered(lambda c: (c.tile_ref or {}).get('q') == int(q) and (c.tile_ref or {}).get('r') == int(r)) else: recs = request.env['yt.world.chest'].sudo().search(domain) out = [{ 'id': c.id, 'cell_i': c.cell_i, 'cell_j': c.cell_j, 'tier': c.tier, 'reward_type': c.reward_type, 'reward_amount': c.reward_amount, 'state': c.state, 'tile_ref': c.tile_ref or {}, } for c in recs] return _json({'ok': True, 'chests': out}) # ── 生成:测试期前端每 10s 调一次;生产可挂 ir.cron ── @http.route('/yt_world/api/chest/generate', type='http', auth='public', methods=['POST', 'OPTIONS'], csrf=False) def generate_chest(self, **kw): auth_err = _require_user() if auth_err: return _json(auth_err) world = _get_world() if not world: return _json({'ok': False, 'error': 'no_world'}) body = _post_body() q = body.get('q'); r = body.get('r') grid_n = int(body.get('grid_n') or 20) q = int(q) if q is not None else None r = int(r) if r is not None else None chest = request.env['yt.world.chest'].sudo().generate_for_world( world, q=q, r=r, grid_n=grid_n) if not chest: return _json({'ok': False, 'error': 'no_slot'}) return _json({'ok': True, 'chest': { 'id': chest.id, 'cell_i': chest.cell_i, 'cell_j': chest.cell_j, 'tier': chest.tier, 'reward_type': chest.reward_type, 'reward_amount': chest.reward_amount, 'state': chest.state, 'tile_ref': chest.tile_ref or {}, }}) # ── 开启:前端生物到达宝箱后调用 ── @http.route('/yt_world/api/chest/claim', type='http', auth='public', methods=['POST', 'OPTIONS'], csrf=False) def claim_chest(self, **kw): auth_err = _require_user() if auth_err: return _json(auth_err) world = _get_world() if not world: return _json({'ok': False, 'error': 'no_world'}) body = _post_body() cid = body.get('chest_id') creature_cid = body.get('cid') or body.get('creature_cid') chest = request.env['yt.world.chest'].sudo().browse(int(cid) if cid else 0) if not chest.exists(): return _json({'ok': False, 'error': 'no_chest'}) if chest.world_id.id != world.id: return _json({'ok': False, 'error': 'forbidden'}) res = chest.claim(creature_cid or '') return _json(res)