Files
yuthon_base/yuthon_bpd/controllers/main.py
T
2026-09-22 15:09:11 +08:00

198 lines
7.8 KiB
Python

# -*- coding: utf-8 -*-
import json
from odoo import http
from odoo.exceptions import AccessError
from odoo.http import request, Response
def _json(data, status=200):
return Response(json.dumps(data, ensure_ascii=False),
mimetype='application/json', status=status)
def _body():
try:
return json.loads(request.httprequest.get_data(as_text=True) or '{}')
except (ValueError, TypeError):
return {}
class BpdController(http.Controller):
"""业务流程图前端的数据接口。
读图对所有登录用户开放;新建节点 / 连线 / 移动 / 改名 / 删除 等写操作
只有系统管理员有 write/create 权限(权限由 ir.model.access.csv 控制),
普通用户会收到 403,前端据此提示「需管理员权限」。
"""
# ---------------- 只读:拿单张图 / 图列表 ----------------
@http.route('/yuthon_bpd/api/diagram/data', type='http',
auth='user', methods=['GET'], csrf=False)
def api_diagram_data(self, **kw):
"""不带 id:返回默认图(key=erp_mto);带 ?id=N:返回指定图。"""
Diagram = request.env['yuthon.bpd.diagram']
did = kw.get('id')
if did not in (None, '', '0'):
try:
did = int(did)
except (TypeError, ValueError):
return _json({'error': 'bad_id'}, 400)
rec = Diagram.browse(did).exists()
if not rec:
return _json({'error': 'not_found'}, 404)
return _json({'diagram': rec.to_json()})
data = Diagram.get_default()
if not data:
return _json({'error': 'no_diagram'}, 404)
return _json({'diagram': data})
@http.route('/yuthon_bpd/api/diagram/list', type='http',
auth='user', methods=['GET'], csrf=False)
def api_diagram_list(self, **kw):
"""供前端下拉切换:返回所有流程图的 id / 名称 / key。"""
recs = request.env['yuthon.bpd.diagram'].search([], order='id')
return _json({'diagrams': [
{'id': r.id, 'name': r.name, 'key': r.key or ''} for r in recs
]})
# ---------------- 写:节点 ----------------
@http.route('/yuthon_bpd/api/diagram/<int:did>/node', type='http',
auth='user', methods=['POST'], csrf=False)
def api_create_node(self, did, **kw):
diagram = request.env['yuthon.bpd.diagram'].browse(did).exists()
if not diagram:
return _json({'error': 'not_found'}, 404)
p = _body()
label = (p.get('label') or '').strip()[:120]
if not label:
return _json({'error': 'empty_label'}, 400)
lane_id = p.get('lane_id') or False
try:
node = request.env['yuthon.bpd.node'].create({
'diagram_id': did,
'lane_id': int(lane_id) if lane_id else False,
'label': label,
'sub': (p.get('sub') or '').strip()[:60],
'x': int(p.get('x', 0) or 0),
'y': int(p.get('y', 0) or 0),
'w': int(p.get('w', 96) or 96),
'h': int(p.get('h', 34) or 34),
'shape': p.get('shape') or 'box',
'variant': p.get('variant') or 'doc',
})
except AccessError:
return _json({'error': 'forbidden'}, 403)
except Exception as exc:
if 'unique' in str(exc).lower():
return _json({'error': 'duplicate'}, 409)
raise
return _json({'node': node.to_json()})
@http.route('/yuthon_bpd/api/node/<int:nid>/move', type='http',
auth='user', methods=['POST'], csrf=False)
def api_move_node(self, nid, **kw):
node = request.env['yuthon.bpd.node'].browse(nid).exists()
if not node:
return _json({'error': 'not_found'}, 404)
p = _body()
try:
node.write({
'x': int(p.get('x', node.x)),
'y': int(p.get('y', node.y)),
})
except AccessError:
return _json({'error': 'forbidden'}, 403)
return _json({'ok': True})
@http.route('/yuthon_bpd/api/node/<int:nid>/rename', type='http',
auth='user', methods=['POST'], csrf=False)
def api_rename_node(self, nid, **kw):
node = request.env['yuthon.bpd.node'].browse(nid).exists()
if not node:
return _json({'error': 'not_found'}, 404)
p = _body()
label = (p.get('label') or '').strip()[:120]
if not label:
return _json({'error': 'empty_label'}, 400)
try:
node.write({'label': label})
except AccessError:
return _json({'error': 'forbidden'}, 403)
return _json({'ok': True, 'label': node.label})
# ---------------- 写:连线 ----------------
@http.route('/yuthon_bpd/api/diagram/<int:did>/edge', type='http',
auth='user', methods=['POST'], csrf=False)
def api_create_edge(self, did, **kw):
diagram = request.env['yuthon.bpd.diagram'].browse(did).exists()
if not diagram:
return _json({'error': 'not_found'}, 404)
p = _body()
try:
frm = int(p.get('from'))
to = int(p.get('to'))
except (TypeError, ValueError):
return _json({'error': 'bad_params'}, 400)
if frm == to:
return _json({'error': 'same_node'}, 400)
Node = request.env['yuthon.bpd.node']
if not (Node.browse(frm).exists() and Node.browse(to).exists()):
return _json({'error': 'node_missing'}, 404)
try:
edge = request.env['yuthon.bpd.edge'].create({
'diagram_id': did,
'from_id': frm,
'to_id': to,
'label': (p.get('label') or '').strip()[:60],
'style': p.get('style') or 'solid',
})
except AccessError:
return _json({'error': 'forbidden'}, 403)
except Exception as exc:
if 'unique' in str(exc).lower():
return _json({'error': 'duplicate'}, 409)
raise
return _json({'edge': edge.to_json()})
@http.route('/yuthon_bpd/api/edge/<int:eid>/delete', type='http',
auth='user', methods=['POST'], csrf=False)
def api_delete_edge(self, eid, **kw):
edge = request.env['yuthon.bpd.edge'].browse(eid).exists()
if not edge:
return _json({'error': 'not_found'}, 404)
try:
edge.unlink()
except AccessError:
return _json({'error': 'forbidden'}, 403)
return _json({'ok': True})
# ---------------- 写:泳道 ----------------
@http.route('/yuthon_bpd/api/diagram/<int:did>/lane', type='http',
auth='user', methods=['POST'], csrf=False)
def api_create_lane(self, did, **kw):
diagram = request.env['yuthon.bpd.diagram'].browse(did).exists()
if not diagram:
return _json({'error': 'not_found'}, 404)
p = _body()
name = (p.get('name') or '新泳道').strip()[:60]
try:
lane = request.env['yuthon.bpd.lane'].create({
'diagram_id': did,
'name': name,
'kind': p.get('kind') or 'column',
'x': int(p.get('x', 24) or 24),
'y': int(p.get('y', 86) or 86),
'w': int(p.get('w', 300) or 300),
'h': int(p.get('h', 600) or 600),
'head_h': int(p.get('head_h', 34) or 34),
'label_pos': p.get('label_pos') or 'top-center',
})
except AccessError:
return _json({'error': 'forbidden'}, 403)
return _json({'lane': {
'id': lane.id, 'name': lane.name, 'kind': lane.kind,
'x': lane.x, 'y': lane.y, 'w': lane.w, 'h': lane.h,
'head_h': lane.head_h, 'label_pos': lane.label_pos,
}})