from __future__ import annotations from flask import g, jsonify, request from app import db from app.api.system import bp from app.core.governance.responsibilities import ( ResponsibilityConflict, ResponsibilityService, ResponsibilityUserUnavailable, ResponsibilityValidationError, SqlAlchemyResponsibilityRepository, ) from app.core.system.permissions import ( RESPONSIBILITIES_MANAGE, RESPONSIBILITIES_READ, require_permissions, ) from app.models.result import failed, success def _service() -> ResponsibilityService: return ResponsibilityService(SqlAlchemyResponsibilityRepository(db.session)) def _etag(response, revision: int): response.headers["ETag"] = f'"{int(revision)}"' return response def _expected_revision() -> int: raw = str(request.headers.get("If-Match") or "").strip() if raw.startswith("W/"): raw = raw[2:].strip() raw = raw.strip('"') if not raw or not raw.isdigit(): raise ResponsibilityValidationError("missing valid If-Match revision") return int(raw) @bp.route( "/responsibilities//", methods=["GET"], ) @require_permissions(RESPONSIBILITIES_READ) def get_responsibility_matrix(resource_type: str, resource_uid: str): try: result = _service().get(resource_type, resource_uid) return _etag(jsonify(success(result)), result["revision"]) except ResponsibilityValidationError as exc: return jsonify(failed(str(exc), code=400)), 400 @bp.route( "/responsibilities//", methods=["PUT"], ) @require_permissions(RESPONSIBILITIES_MANAGE) def replace_responsibility_matrix(resource_type: str, resource_uid: str): try: revision = _expected_revision() body = request.get_json(silent=True) or {} result = _service().replace( resource_type=resource_type, resource_uid=resource_uid, assignments=body.get("assignments"), expected_revision=revision, actor_uid=g.current_user["id"], ) db.session.commit() return _etag(jsonify(success(result, "责任矩阵已更新")), result["revision"]) except ResponsibilityConflict as exc: db.session.rollback() return jsonify(failed(str(exc), code=409)), 409 except ResponsibilityUserUnavailable as exc: db.session.rollback() return jsonify(failed(str(exc), code=422)), 422 except ResponsibilityValidationError as exc: db.session.rollback() status = 428 if "If-Match" in str(exc) else 400 return jsonify(failed(str(exc), code=status)), status