570 lines
23 KiB
Python
570 lines
23 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""信用认证体系端点:认证类型/我的认证/提交申请/技能测试/运营审核。
|
||
|
||
覆盖四大认证线:个人认证 / 企业认证 / OPC人才认证(付费) / 技能认证(按需付费)。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from fastapi import APIRouter, Depends, HTTPException, Request
|
||
from pydantic import BaseModel
|
||
|
||
from ..dependencies import get_db
|
||
from ...rbac import require_roles, write_audit
|
||
from ...infrastructure.repositories import Database, new_id, utcnow_iso
|
||
from ...infrastructure.models import Certification, CertificationType, SkillTest
|
||
import json
|
||
|
||
router = APIRouter(tags=["certifications"])
|
||
|
||
_ANY = ("opc_member", "carrier", "operator")
|
||
_OPERATOR = ("operator",)
|
||
|
||
|
||
# ── Pydantic 请求模型 ──────────────────────────────────────────────────────
|
||
|
||
class CertApplyRequest(BaseModel):
|
||
cert_type: str
|
||
materials: dict = {}
|
||
level: str = ""
|
||
|
||
|
||
class CertResubmitRequest(BaseModel):
|
||
materials: dict = {}
|
||
|
||
|
||
class CertReviewRequest(BaseModel):
|
||
status: str # approved / rejected / revoked
|
||
review_note: str = ""
|
||
credit_points: int | None = None
|
||
|
||
|
||
class SkillTestStartRequest(BaseModel):
|
||
skill_domain: str
|
||
|
||
|
||
class SkillTestSubmitRequest(BaseModel):
|
||
answers: dict = {}
|
||
|
||
|
||
# ── 认证类型配置 ────────────────────────────────────────────────────────────
|
||
|
||
@router.get("/cert/types", summary="认证类型列表(按大类分组)")
|
||
async def list_cert_types(
|
||
category: str = "",
|
||
db: Database = Depends(get_db),
|
||
user: dict = Depends(require_roles(*_ANY)),
|
||
):
|
||
from ...infrastructure.cache import cache as redis_cache
|
||
cache_key = f"cert:types:{category or 'all'}"
|
||
cached = await redis_cache.get_json(cache_key)
|
||
if cached is not None:
|
||
return cached
|
||
types = await db.cert_types.list(category=category)
|
||
# 按大类分组
|
||
grouped: dict[str, list] = {}
|
||
for t in types:
|
||
grouped.setdefault(t["category"], []).append(t)
|
||
result = {"items": types, "grouped": grouped}
|
||
import asyncio
|
||
asyncio.create_task(redis_cache.set_json(cache_key, result, ttl=300))
|
||
return result
|
||
|
||
|
||
@router.get("/cert/types/{code}", summary="认证类型详情")
|
||
async def get_cert_type(
|
||
code: str,
|
||
db: Database = Depends(get_db),
|
||
user: dict = Depends(require_roles(*_ANY)),
|
||
):
|
||
t = await db.cert_types.get_by_code(code)
|
||
if t is None:
|
||
raise HTTPException(status_code=404, detail="认证类型不存在")
|
||
return t
|
||
|
||
|
||
# ── 我的认证 ────────────────────────────────────────────────────────────────
|
||
|
||
@router.get("/me/certs", summary="我的认证列表")
|
||
async def my_certs(
|
||
cert_type: str = "",
|
||
status: str = "",
|
||
category: str = "",
|
||
db: Database = Depends(get_db),
|
||
user: dict = Depends(require_roles(*_ANY)),
|
||
):
|
||
items = await db.certifications.list(
|
||
user_id=user["id"], cert_type=cert_type, status=status, category=category
|
||
)
|
||
# 统计已获得的信用加分
|
||
total_credit = sum(i.get("credit_points", 0) for i in items if i.get("status") in ("active", "approved"))
|
||
return {"items": items, "total_credit_points": total_credit}
|
||
|
||
|
||
@router.get("/me/certs/{cert_id}", summary="我的认证详情")
|
||
async def get_my_cert(
|
||
cert_id: str,
|
||
db: Database = Depends(get_db),
|
||
user: dict = Depends(require_roles(*_ANY)),
|
||
):
|
||
c = await db.certifications.get(cert_id)
|
||
if c is None or c.get("user_id") != user["id"]:
|
||
raise HTTPException(status_code=404, detail="认证记录不存在")
|
||
return c
|
||
|
||
|
||
@router.post("/cert/apply", summary="提交认证申请")
|
||
async def apply_cert(
|
||
req: CertApplyRequest,
|
||
request: Request,
|
||
db: Database = Depends(get_db),
|
||
user: dict = Depends(require_roles(*_ANY)),
|
||
):
|
||
# 校验认证类型
|
||
cert_type_info = await db.cert_types.get_by_code(req.cert_type)
|
||
if cert_type_info is None:
|
||
raise HTTPException(status_code=400, detail="认证类型不存在")
|
||
|
||
# 检查是否已有生效中的同类型认证
|
||
existing = await db.certifications.get_active(user["id"], req.cert_type)
|
||
if existing:
|
||
raise HTTPException(status_code=400, detail="该类型认证已生效,无需重复申请")
|
||
|
||
# 检查是否有待审核的同类型认证
|
||
pending = await db.certifications.list(user_id=user["id"], cert_type=req.cert_type, status="pending")
|
||
if pending:
|
||
raise HTTPException(status_code=400, detail="该类型认证审核中,请耐心等待")
|
||
|
||
# 自动审核模式:直接通过
|
||
now = utcnow_iso()
|
||
status = "approved" if cert_type_info.get("review_mode") == "auto" else "pending"
|
||
credit_points = cert_type_info.get("credit_points", 0) if status == "approved" else 0
|
||
expires_at = ""
|
||
if cert_type_info.get("validity_days", 0) > 0 and status == "approved":
|
||
from datetime import datetime, timedelta
|
||
exp = datetime.now() + timedelta(days=cert_type_info["validity_days"])
|
||
expires_at = exp.strftime("%Y-%m-%dT%H:%M:%S")
|
||
|
||
cert = await db.certifications.create(
|
||
user_id=user["id"],
|
||
cert_type=req.cert_type,
|
||
cert_category=cert_type_info.get("category", "personal"),
|
||
level=req.level,
|
||
status=status,
|
||
materials=req.materials,
|
||
credit_points=credit_points,
|
||
fee_amount=cert_type_info.get("fee", 0),
|
||
submitted_at=now,
|
||
approved_at=now if status == "approved" else "",
|
||
expires_at=expires_at,
|
||
)
|
||
|
||
await write_audit(db, action="cert.apply", resource="certification", resource_id=cert["id"],
|
||
detail=f"type={req.cert_type}, status={status}", user=user, request=request)
|
||
|
||
# 实时通知:待人工审核时通知运营;自动通过时回执申请人
|
||
from ...services.notification_service import notify as _n
|
||
if status == "pending":
|
||
await _n(db, user["id"], "cert", "认证申请已提交",
|
||
f"「{cert_type_info.get('name') or req.cert_type}」认证已提交,请等待审核",
|
||
event_code="cert.submitted", link="/opc/credit", ref_type="certification", ref_id=cert["id"])
|
||
try:
|
||
from ...services.notification_service import notify_roles
|
||
await notify_roles(db, ["operator"], "ops", "新认证待审核",
|
||
f"新认证申请「{cert_type_info.get('name') or req.cert_type}」待审核",
|
||
event_code="ops.cert_review", link="/cert-admin",
|
||
ref_type="certification", ref_id=cert["id"])
|
||
except Exception: # noqa: BLE001
|
||
pass
|
||
elif status == "approved":
|
||
await _n(db, user["id"], "cert", "认证审核通过",
|
||
f"「{cert_type_info.get('name') or req.cert_type}」认证已自动通过"
|
||
+ (f",信用分 +{credit_points}" if credit_points else ""),
|
||
event_code="cert.approved", level="success", link="/opc/credit",
|
||
ref_type="certification", ref_id=cert["id"])
|
||
return {"ok": True, "item": cert, "auto_approved": status == "approved"}
|
||
|
||
|
||
@router.post("/cert/{cert_id}/resubmit", summary="重新提交认证(驳回后)")
|
||
async def resubmit_cert(
|
||
cert_id: str,
|
||
req: CertResubmitRequest,
|
||
request: Request,
|
||
db: Database = Depends(get_db),
|
||
user: dict = Depends(require_roles(*_ANY)),
|
||
):
|
||
c = await db.certifications.get(cert_id)
|
||
if c is None or c.get("user_id") != user["id"]:
|
||
raise HTTPException(status_code=404, detail="认证记录不存在")
|
||
if c.get("status") not in ("rejected",):
|
||
raise HTTPException(status_code=400, detail="仅驳回状态可重新提交")
|
||
|
||
updated = await db.certifications.update(cert_id, {
|
||
"materials": req.materials,
|
||
"status": "pending",
|
||
"submitted_at": utcnow_iso(),
|
||
"review_note": "",
|
||
})
|
||
|
||
await write_audit(db, action="cert.resubmit", resource="certification", resource_id=cert_id,
|
||
detail="重新提交", user=user, request=request)
|
||
return {"ok": True, "item": updated}
|
||
|
||
|
||
@router.delete("/cert/{cert_id}", summary="撤销认证申请(审核前)")
|
||
async def cancel_cert(
|
||
cert_id: str,
|
||
request: Request,
|
||
db: Database = Depends(get_db),
|
||
user: dict = Depends(require_roles(*_ANY)),
|
||
):
|
||
c = await db.certifications.get(cert_id)
|
||
if c is None or c.get("user_id") != user["id"]:
|
||
raise HTTPException(status_code=404, detail="认证记录不存在")
|
||
if c.get("status") not in ("pending", "not_started"):
|
||
raise HTTPException(status_code=400, detail="仅审核中状态可撤销")
|
||
|
||
updated = await db.certifications.update(cert_id, {"status": "revoked"})
|
||
await write_audit(db, action="cert.cancel", resource="certification", resource_id=cert_id,
|
||
detail="用户撤销申请", user=user, request=request)
|
||
return {"ok": True, "item": updated}
|
||
|
||
|
||
# ── 技能测试 ────────────────────────────────────────────────────────────────
|
||
|
||
@router.get("/skill/domains", summary="技能测试领域列表")
|
||
async def list_skill_domains(
|
||
db: Database = Depends(get_db),
|
||
user: dict = Depends(require_roles(*_ANY)),
|
||
):
|
||
types = await db.cert_types.list(category="skill")
|
||
# 附带用户已完成的最高成绩
|
||
result = []
|
||
for t in types:
|
||
domain = t["code"].replace("skill_", "")
|
||
best = await db.skill_tests.get_latest_completed(user["id"], domain)
|
||
t["my_best_score"] = best.get("score", 0) if best else 0
|
||
t["my_best_level"] = best.get("level", "") if best else ""
|
||
result.append(t)
|
||
return {"items": result}
|
||
|
||
|
||
@router.post("/skill/test/start", summary="开始技能测试(创建测试记录)")
|
||
async def start_skill_test(
|
||
req: SkillTestStartRequest,
|
||
request: Request,
|
||
db: Database = Depends(get_db),
|
||
user: dict = Depends(require_roles(*_ANY)),
|
||
):
|
||
# 校验领域
|
||
cert_type = await db.cert_types.get_by_code(f"skill_{req.skill_domain}")
|
||
if cert_type is None:
|
||
raise HTTPException(status_code=400, detail="技能领域不存在")
|
||
|
||
# 检查是否有未完成的测试
|
||
existing = await db.skill_tests.list(user_id=user["id"], skill_domain=req.skill_domain, status="in_progress")
|
||
if existing:
|
||
return {"ok": True, "item": existing[0], "resumed": True}
|
||
|
||
test = await db.skill_tests.create(
|
||
user_id=user["id"],
|
||
skill_domain=req.skill_domain,
|
||
status="in_progress",
|
||
started_at=utcnow_iso(),
|
||
)
|
||
|
||
await write_audit(db, action="skill.test.start", resource="skill_test", resource_id=test["id"],
|
||
detail=f"domain={req.skill_domain}", user=user, request=request)
|
||
return {"ok": True, "item": test, "resumed": False}
|
||
|
||
|
||
@router.post("/skill/test/{test_id}/submit", summary="提交技能测试")
|
||
async def submit_skill_test(
|
||
test_id: str,
|
||
req: SkillTestSubmitRequest,
|
||
request: Request,
|
||
db: Database = Depends(get_db),
|
||
user: dict = Depends(require_roles(*_ANY)),
|
||
):
|
||
test = await db.skill_tests.get(test_id)
|
||
if test is None or test.get("user_id") != user["id"]:
|
||
raise HTTPException(status_code=404, detail="测试记录不存在")
|
||
if test.get("status") != "in_progress":
|
||
raise HTTPException(status_code=400, detail="测试已提交或已过期")
|
||
|
||
# 简单评分:从 answers 中计算客观题得分(实际应从题库判分)
|
||
objective_score = 0
|
||
total_objective = 0
|
||
for qid, ans in req.answers.items():
|
||
if isinstance(ans, dict) and ans.get("type") == "objective":
|
||
total_objective += 1
|
||
if ans.get("correct"):
|
||
objective_score += 1
|
||
|
||
score = int(objective_score / max(total_objective, 1) * 100) if total_objective > 0 else 0
|
||
|
||
# 等级判定
|
||
if score >= 95:
|
||
level = "expert"
|
||
elif score >= 85:
|
||
level = "advanced"
|
||
elif score >= 75:
|
||
level = "intermediate"
|
||
elif score >= 60:
|
||
level = "beginner"
|
||
else:
|
||
level = ""
|
||
|
||
now = utcnow_iso()
|
||
updated = await db.skill_tests.update(test_id, {
|
||
"status": "completed" if level else "failed",
|
||
"score": score,
|
||
"level": level,
|
||
"objective_score": objective_score,
|
||
"answers": req.answers,
|
||
"submitted_at": now,
|
||
"scored_at": now,
|
||
})
|
||
|
||
# 通过则创建认证记录
|
||
cert_id = ""
|
||
if level:
|
||
cert_type = await db.cert_types.get_by_code(f"skill_{test['skill_domain']}")
|
||
credit_map = {"beginner": 40, "intermediate": 80, "advanced": 120, "expert": 200}
|
||
credit = credit_map.get(level, 0)
|
||
from datetime import datetime, timedelta
|
||
exp = datetime.now() + timedelta(days=cert_type.get("validity_days", 730) if cert_type else 730)
|
||
cert = await db.certifications.create(
|
||
user_id=user["id"],
|
||
cert_type=f"skill_{test['skill_domain']}",
|
||
cert_category="skill",
|
||
level=level,
|
||
status="active",
|
||
materials={"test_id": test_id, "score": score},
|
||
credit_points=credit,
|
||
submitted_at=now,
|
||
approved_at=now,
|
||
expires_at=exp.strftime("%Y-%m-%dT%H:%M:%S"),
|
||
)
|
||
cert_id = cert["id"]
|
||
# 回写 certification_id
|
||
await db.skill_tests.update(test_id, {"certification_id": cert_id})
|
||
# 信用事件:技能测试通过(按等级 40/80/120/200,单用户按 ref_id 去重)
|
||
from ...services.credit_engine import on_credit_event
|
||
await on_credit_event(
|
||
db, user_id=user["id"], event_code="skill.test.passed",
|
||
payload={"level": level}, ref_type="skill_test", ref_id=test_id,
|
||
reason=f"技能测试通过({level})",
|
||
)
|
||
|
||
await write_audit(db, action="skill.test.submit", resource="skill_test", resource_id=test_id,
|
||
detail=f"score={score}, level={level}", user=user, request=request)
|
||
return {"ok": True, "item": updated, "certification_id": cert_id}
|
||
|
||
|
||
@router.get("/skill/test/{test_id}/result", summary="技能测试结果")
|
||
async def get_skill_test_result(
|
||
test_id: str,
|
||
db: Database = Depends(get_db),
|
||
user: dict = Depends(require_roles(*_ANY)),
|
||
):
|
||
test = await db.skill_tests.get(test_id)
|
||
if test is None or test.get("user_id") != user["id"]:
|
||
raise HTTPException(status_code=404, detail="测试记录不存在")
|
||
return test
|
||
|
||
|
||
@router.get("/skill/my-certs", summary="我的技能认证列表")
|
||
async def my_skill_certs(
|
||
db: Database = Depends(get_db),
|
||
user: dict = Depends(require_roles(*_ANY)),
|
||
):
|
||
items = await db.certifications.list(user_id=user["id"], category="skill", status="active")
|
||
return {"items": items}
|
||
|
||
|
||
# ── 用户公开认证信息(脱敏) ──────────────────────────────────────────────────
|
||
|
||
@router.get("/users/{user_id}/certs", summary="获取用户公开认证信息(脱敏)")
|
||
async def get_user_public_certs(
|
||
user_id: str,
|
||
db: Database = Depends(get_db),
|
||
user: dict = Depends(require_roles(*_ANY)),
|
||
):
|
||
items = await db.certifications.list(user_id=user_id, status="active")
|
||
# 脱敏:移除 materials 中的敏感信息
|
||
public = []
|
||
for c in items:
|
||
public.append({
|
||
"id": c["id"],
|
||
"cert_type": c["cert_type"],
|
||
"cert_category": c["cert_category"],
|
||
"level": c["level"],
|
||
"status": c["status"],
|
||
"credit_points": c["credit_points"],
|
||
"approved_at": c["approved_at"],
|
||
"expires_at": c["expires_at"],
|
||
})
|
||
return {"items": public}
|
||
|
||
|
||
# ── 运营端:认证审核 ─────────────────────────────────────────────────────────
|
||
|
||
@router.get("/admin/cert/list", summary="认证申请列表(运营端)")
|
||
async def admin_list_certs(
|
||
status: str = "",
|
||
cert_type: str = "",
|
||
category: str = "",
|
||
db: Database = Depends(get_db),
|
||
user: dict = Depends(require_roles(*_OPERATOR)),
|
||
):
|
||
items = await db.certifications.list(
|
||
status=status, cert_type=cert_type, category=category
|
||
)
|
||
return {"items": items}
|
||
|
||
|
||
@router.get("/admin/cert/{cert_id}", summary="认证申请详情(运营端)")
|
||
async def admin_get_cert(
|
||
cert_id: str,
|
||
db: Database = Depends(get_db),
|
||
user: dict = Depends(require_roles(*_OPERATOR)),
|
||
):
|
||
c = await db.certifications.get(cert_id)
|
||
if c is None:
|
||
raise HTTPException(status_code=404, detail="认证记录不存在")
|
||
return c
|
||
|
||
|
||
@router.post("/admin/cert/{cert_id}/review", summary="审核认证申请(通过/驳回/撤销)")
|
||
async def admin_review_cert(
|
||
cert_id: str,
|
||
req: CertReviewRequest,
|
||
request: Request,
|
||
db: Database = Depends(get_db),
|
||
user: dict = Depends(require_roles(*_OPERATOR)),
|
||
):
|
||
c = await db.certifications.get(cert_id)
|
||
if c is None:
|
||
raise HTTPException(status_code=404, detail="认证记录不存在")
|
||
|
||
if req.status not in ("approved", "rejected", "revoked", "active"):
|
||
raise HTTPException(status_code=400, detail="状态仅支持 approved/rejected/rejected/active")
|
||
|
||
# 通过时计算信用加分和有效期
|
||
credit_points = req.credit_points
|
||
expires_at = c.get("expires_at", "")
|
||
if req.status in ("approved", "active"):
|
||
if credit_points is None:
|
||
cert_type_info = await db.cert_types.get_by_code(c["cert_type"])
|
||
credit_points = cert_type_info.get("credit_points", 0) if cert_type_info else 0
|
||
if not expires_at:
|
||
cert_type_info = await db.cert_types.get_by_code(c["cert_type"])
|
||
validity = cert_type_info.get("validity_days", 0) if cert_type_info else 0
|
||
if validity > 0:
|
||
from datetime import datetime, timedelta
|
||
exp = datetime.now() + timedelta(days=validity)
|
||
expires_at = exp.strftime("%Y-%m-%dT%H:%M:%S")
|
||
|
||
updated = await db.certifications.set_status(
|
||
cert_id, req.status,
|
||
reviewer_id=user["id"],
|
||
review_note=req.review_note,
|
||
credit_points=credit_points,
|
||
expires_at=expires_at,
|
||
)
|
||
|
||
# 信用事件:认证通过按类别加分;撤销/驳回已生效认证则扣回
|
||
from ...services.credit_engine import on_credit_event, recalc_user
|
||
cert_category = (c.get("cert_category") or "").strip()
|
||
event_map = {
|
||
"identity": "cert.identity", "merchant": "cert.merchant",
|
||
"enterprise": "cert.enterprise", "education": "cert.education",
|
||
"qualification": "cert.qualification", "opc": "cert.opc",
|
||
"skill": "cert.skill",
|
||
}
|
||
if req.status in ("approved", "active"):
|
||
event_code = event_map.get(cert_category, "")
|
||
if event_code:
|
||
await on_credit_event(
|
||
db, user_id=c["user_id"], event_code=event_code,
|
||
ref_type="certification", ref_id=cert_id,
|
||
reason=f"{c.get('cert_type', '')} 认证审核通过",
|
||
)
|
||
elif req.status == "revoked" and c.get("status") in ("active", "approved"):
|
||
# 扣回:认证撤销按当时加分反向冲正
|
||
back = credit_points if credit_points is not None else 0
|
||
if back and back > 0:
|
||
await db.credit_ledger.add(
|
||
user_id=c["user_id"], event_code="manual.adjust", delta=-back,
|
||
dimension="compliance", reason=f"认证撤销扣回({c.get('cert_type', '')})",
|
||
ref_type="certification", ref_id=cert_id, source="admin",
|
||
operator_id=user["id"],
|
||
)
|
||
await recalc_user(db, c["user_id"])
|
||
|
||
await write_audit(db, action="admin.cert.review", resource="certification", resource_id=cert_id,
|
||
detail=f"status={req.status}, credit={credit_points}", user=user, request=request)
|
||
# 实时通知:审核结果回传申请人(通过/驳回),认证通过含信用变化提示
|
||
from ...services.notification_service import notify as _n
|
||
_title = {"approved": "认证审核通过", "active": "认证已生效", "rejected": "认证审核未通过",
|
||
"revoked": "认证已撤销"}.get(req.status, "认证状态更新")
|
||
_lvl = "success" if req.status in ("approved", "active") else "warning"
|
||
_credit_txt = f",信用分 +{credit_points}" if req.status in ("approved", "active") and credit_points else ""
|
||
await _n(db, c["user_id"], "cert", _title,
|
||
f"你的「{c.get('cert_type', '')}」认证{_title}{_credit_txt}"
|
||
f"{(':' + req.review_note) if req.review_note else ''}",
|
||
event_code=f"cert.{'approved' if req.status in ('approved','active') else ('rejected' if req.status=='rejected' else 'revoked')}",
|
||
level=_lvl, link="/opc/credit", ref_type="certification", ref_id=cert_id)
|
||
# 认证通过时同步发徽章提示
|
||
if req.status in ("approved", "active") and cert_category == "opc":
|
||
await _n(db, c["user_id"], "cert", "OPC 人才认证已生效",
|
||
"你已获得 OPC 人才认证,可参与高等级任务与投标",
|
||
event_code="cert.badge_granted", level="success", link="/opc/credit",
|
||
ref_type="certification", ref_id=cert_id)
|
||
return {"ok": True, "item": updated}
|
||
|
||
|
||
@router.get("/admin/cert/types", summary="认证类型配置列表(运营端)")
|
||
async def admin_list_cert_types(
|
||
db: Database = Depends(get_db),
|
||
user: dict = Depends(require_roles(*_OPERATOR)),
|
||
):
|
||
items = await db.cert_types.list(active_only=False)
|
||
return {"items": items}
|
||
|
||
|
||
@router.post("/admin/cert/types", summary="新增认证类型(运营端)")
|
||
async def admin_create_cert_type(
|
||
body: dict,
|
||
request: Request,
|
||
db: Database = Depends(get_db),
|
||
user: dict = Depends(require_roles(*_OPERATOR)),
|
||
):
|
||
code = body.get("code", "")
|
||
if not code:
|
||
raise HTTPException(status_code=400, detail="缺少认证类型编码")
|
||
existing = await db.cert_types.get_by_code(code)
|
||
if existing:
|
||
raise HTTPException(status_code=400, detail="认证类型编码已存在")
|
||
|
||
t = await db.cert_types.create(**body)
|
||
await write_audit(db, action="admin.cert.type.create", resource="certification_type",
|
||
resource_id=t["id"], detail=code, user=user, request=request)
|
||
return {"ok": True, "item": t}
|
||
|
||
|
||
@router.put("/admin/cert/types/{code}", summary="编辑认证类型(运营端)")
|
||
async def admin_update_cert_type(
|
||
code: str,
|
||
body: dict,
|
||
request: Request,
|
||
db: Database = Depends(get_db),
|
||
user: dict = Depends(require_roles(*_OPERATOR)),
|
||
):
|
||
t = await db.cert_types.update(code, body)
|
||
if t is None:
|
||
raise HTTPException(status_code=404, detail="认证类型不存在")
|
||
await write_audit(db, action="admin.cert.type.update", resource="certification_type",
|
||
resource_id=code, detail=json.dumps(body, ensure_ascii=False), user=user, request=request)
|
||
return {"ok": True, "item": t}
|