| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- 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.governance.unified_responsibilities import (
- UnifiedResponsibilityService,
- )
- from app.core.governance.unified_responsibility_repository import (
- SqlAlchemyUnifiedResponsibilityRepository,
- )
- 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 _unified_service() -> UnifiedResponsibilityService:
- return UnifiedResponsibilityService(
- SqlAlchemyUnifiedResponsibilityRepository(db.session),
- commit=db.session.commit,
- rollback=db.session.rollback,
- )
- 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)
- def _unified_error(exc: Exception):
- db.session.rollback()
- message = str(exc)
- if "If-Match" in message:
- status = 428
- elif isinstance(exc, LookupError):
- status = 404
- elif isinstance(exc, RuntimeError):
- status = 409
- else:
- status = 400
- return jsonify(failed(message, code=status)), status
- @bp.route(
- "/responsibilities/<resource_type>/<resource_uid>",
- 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/<resource_type>/<resource_uid>",
- 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
- @bp.route(
- "/responsibilities/<resource_type>/<resource_uid>/resolved",
- methods=["GET"],
- )
- @require_permissions(RESPONSIBILITIES_READ)
- def resolve_responsibility(resource_type: str, resource_uid: str):
- try:
- return jsonify(
- success(
- _unified_service().resolve(
- resource_type,
- resource_uid,
- at=request.args.get("at"),
- )
- )
- )
- except (ValueError, LookupError, RuntimeError) as exc:
- return _unified_error(exc)
- @bp.route(
- "/responsibilities/hierarchy/<resource_type>/<resource_uid>",
- methods=["PUT"],
- )
- @require_permissions(RESPONSIBILITIES_MANAGE)
- def replace_responsibility_parent(resource_type: str, resource_uid: str):
- try:
- body = request.get_json(silent=True) or {}
- result = _unified_service().set_parent(
- {
- **body,
- "resource_type": resource_type,
- "resource_uid": resource_uid,
- },
- expected_revision=_expected_revision(),
- actor_uid=g.current_user["id"],
- )
- return _etag(
- jsonify(success(result, "责任继承关系已更新")), result["revision"]
- )
- except (ValueError, LookupError, RuntimeError, ResponsibilityValidationError) as exc:
- return _unified_error(exc)
- @bp.route("/responsibilities/delegations", methods=["GET"])
- @require_permissions(RESPONSIBILITIES_READ)
- def list_responsibility_delegations():
- try:
- return jsonify(
- success(
- _unified_service().list_delegations(
- status=request.args.get("status")
- )
- )
- )
- except (ValueError, RuntimeError) as exc:
- return _unified_error(exc)
- @bp.route("/responsibilities/delegations", methods=["POST"])
- @require_permissions(RESPONSIBILITIES_MANAGE)
- def create_responsibility_delegation():
- try:
- result = _unified_service().create_delegation(
- request.get_json(silent=True) or {},
- actor_uid=g.current_user["id"],
- )
- return jsonify(success(result, "责任委派已创建", code=201)), 201
- except (ValueError, RuntimeError) as exc:
- return _unified_error(exc)
- @bp.route(
- "/responsibilities/delegations/<delegation_uid>/revoke",
- methods=["POST"],
- )
- @require_permissions(RESPONSIBILITIES_MANAGE)
- def revoke_responsibility_delegation(delegation_uid: str):
- try:
- result = _unified_service().revoke_delegation(
- delegation_uid,
- expected_version=_expected_revision(),
- actor_uid=g.current_user["id"],
- )
- return _etag(
- jsonify(success(result, "责任委派已收回")), result["current_version"]
- )
- except (ValueError, LookupError, RuntimeError, ResponsibilityValidationError) as exc:
- return _unified_error(exc)
- @bp.route("/responsibilities/delegations/expire", methods=["POST"])
- @require_permissions(RESPONSIBILITIES_MANAGE)
- def expire_responsibility_delegations():
- try:
- body = request.get_json(silent=True) or {}
- return jsonify(
- success(
- _unified_service().expire_delegations(
- at=body.get("at"), actor_uid=g.current_user["id"]
- ),
- "到期委派已收回",
- )
- )
- except (ValueError, RuntimeError) as exc:
- return _unified_error(exc)
- @bp.route("/responsibilities/transfers", methods=["POST"])
- @require_permissions(RESPONSIBILITIES_MANAGE)
- def transfer_departing_responsibility():
- try:
- result = _unified_service().transfer_departing_user(
- request.get_json(silent=True) or {},
- actor_uid=g.current_user["id"],
- )
- return jsonify(success(result, "离岗责任已转交", code=201)), 201
- except (ValueError, RuntimeError) as exc:
- return _unified_error(exc)
- @bp.route("/responsibilities/policies", methods=["GET"])
- @require_permissions(RESPONSIBILITIES_READ)
- def list_responsibility_policies():
- try:
- return jsonify(success(_unified_service().list_policies()))
- except RuntimeError as exc:
- return _unified_error(exc)
- @bp.route("/responsibilities/policies", methods=["POST"])
- @require_permissions(RESPONSIBILITIES_MANAGE)
- def create_responsibility_policy():
- try:
- result = _unified_service().create_policy(
- request.get_json(silent=True) or {},
- actor_uid=g.current_user["id"],
- )
- return _etag(
- jsonify(success(result, "责任策略草稿已创建", code=201)),
- result["current_version"],
- ), 201
- except (ValueError, RuntimeError) as exc:
- return _unified_error(exc)
- @bp.route(
- "/responsibilities/policies/<policy_uid>/revisions",
- methods=["POST"],
- )
- @require_permissions(RESPONSIBILITIES_MANAGE)
- def revise_responsibility_policy(policy_uid: str):
- try:
- body = request.get_json(silent=True) or {}
- result = _unified_service().revise_policy(
- policy_uid,
- body.get("definition"),
- expected_version=_expected_revision(),
- actor_uid=g.current_user["id"],
- )
- return _etag(
- jsonify(success(result, "责任策略新版本已创建")),
- result["current_version"],
- )
- except (ValueError, LookupError, RuntimeError, ResponsibilityValidationError) as exc:
- return _unified_error(exc)
- @bp.route(
- "/responsibilities/policies/<policy_uid>/publish",
- methods=["POST"],
- )
- @require_permissions(RESPONSIBILITIES_MANAGE)
- def publish_responsibility_policy(policy_uid: str):
- try:
- result = _unified_service().publish_policy(
- policy_uid,
- expected_version=_expected_revision(),
- actor_uid=g.current_user["id"],
- )
- return _etag(
- jsonify(success(result, "责任策略已发布")),
- result["current_version"],
- )
- except (ValueError, LookupError, RuntimeError, ResponsibilityValidationError) as exc:
- return _unified_error(exc)
- @bp.route(
- "/responsibilities/<resource_type>/<resource_uid>/joint-review/evaluate",
- methods=["POST"],
- )
- @require_permissions(RESPONSIBILITIES_MANAGE)
- def evaluate_responsibility_joint_review(resource_type: str, resource_uid: str):
- try:
- body = request.get_json(silent=True) or {}
- return jsonify(
- success(
- _unified_service().evaluate_joint_review(
- resource_type, resource_uid, body.get("decisions")
- )
- )
- )
- except (ValueError, LookupError, RuntimeError) as exc:
- return _unified_error(exc)
- @bp.route("/responsibilities/operations", methods=["GET"])
- @require_permissions(RESPONSIBILITIES_READ)
- def responsibility_operations():
- try:
- return jsonify(
- success(
- _unified_service().operations(request.args.get("owner_uid"))
- )
- )
- except (ValueError, RuntimeError) as exc:
- return _unified_error(exc)
|