5840a5cb4c
- training_admin_bridge:+update_course/delete_course/update_activity/delete_activity(tdb.update_row/delete_row)
- schemas.operator:+CourseUpdateRequest/ActivityUpdateRequest(字段全可选,仅更新传入项)
- rbac_operator:+PUT/DELETE /admin/courses/{id}、/admin/activities/{id}(action:course.manage/activity.manage + 审计)
- 富操作(增/改/删/态)齐备
Co-Authored-By: Claude <noreply@anthropic.com>
245 lines
8.1 KiB
Python
245 lines
8.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""运营端 → 培训子应用数据桥接(课程 / 活动 / 报名 / 测评)。
|
|
|
|
培训业务数据存于 app.training(独立 serverdata/data/opc.db);运营端 /admin/*
|
|
端点在此聚合读写,避免在平台应用重建一套课程/活动模型。
|
|
|
|
仅作适度封装(list / create / status / patch),状态与原 mock 语义保持一致;
|
|
创建统一生成 id(前缀 C- / E- / B- / T-),时间用 UTC ISO。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from datetime import datetime, timezone
|
|
|
|
from app.training import db as tdb
|
|
|
|
|
|
def _now() -> str:
|
|
return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
|
|
|
|
# ── 课程(courses 表)────────────────────────────────────────────────────
|
|
|
|
def list_courses(status: str | None = None) -> list[dict]:
|
|
rows = tdb.list_all("courses", "created_at DESC")
|
|
if status:
|
|
return [r for r in rows if r.get("status") == status]
|
|
return rows
|
|
|
|
|
|
def create_course(data: dict) -> dict:
|
|
row = dict(data)
|
|
row["id"] = tdb.gen_id("C-")
|
|
row.setdefault("created_at", _now())
|
|
row.setdefault("status", "draft")
|
|
tdb.insert("courses", row)
|
|
return tdb.fetch_by_id("courses", row["id"])
|
|
|
|
|
|
def set_course_status(course_id: str, status: str) -> dict | None:
|
|
if tdb.fetch_by_id("courses", course_id) is None:
|
|
return None
|
|
tdb.update_row("courses", course_id, {"status": status})
|
|
return tdb.fetch_by_id("courses", course_id)
|
|
|
|
|
|
def update_course(course_id: str, patch: dict) -> dict | None:
|
|
if tdb.fetch_by_id("courses", course_id) is None:
|
|
return None
|
|
if patch:
|
|
tdb.update_row("courses", course_id, patch)
|
|
return tdb.fetch_by_id("courses", course_id)
|
|
|
|
|
|
def delete_course(course_id: str) -> bool:
|
|
if tdb.fetch_by_id("courses", course_id) is None:
|
|
return False
|
|
tdb.delete_row("courses", course_id)
|
|
return True
|
|
|
|
|
|
# ── 活动(events 表)─────────────────────────────────────────────────────
|
|
|
|
def list_activities(status: str | None = None) -> list[dict]:
|
|
rows = tdb.list_all("events", "start_at DESC")
|
|
if status:
|
|
return [r for r in rows if r.get("status") == status]
|
|
return rows
|
|
|
|
|
|
def create_activity(data: dict) -> dict:
|
|
row = dict(data)
|
|
row["id"] = tdb.gen_id("E-")
|
|
row.setdefault("mode", "offline")
|
|
row.setdefault("status", "open")
|
|
row.setdefault("duration_min", 90)
|
|
tdb.insert("events", row)
|
|
return tdb.fetch_by_id("events", row["id"])
|
|
|
|
|
|
def set_activity_status(event_id: str, status: str) -> dict | None:
|
|
if tdb.fetch_by_id("events", event_id) is None:
|
|
return None
|
|
tdb.update_row("events", event_id, {"status": status})
|
|
return tdb.fetch_by_id("events", event_id)
|
|
|
|
|
|
def update_activity(event_id: str, patch: dict) -> dict | None:
|
|
if tdb.fetch_by_id("events", event_id) is None:
|
|
return None
|
|
if patch:
|
|
tdb.update_row("events", event_id, patch)
|
|
return tdb.fetch_by_id("events", event_id)
|
|
|
|
|
|
def delete_activity(event_id: str) -> bool:
|
|
if tdb.fetch_by_id("events", event_id) is None:
|
|
return False
|
|
tdb.delete_row("events", event_id)
|
|
return True
|
|
|
|
|
|
# ── 报名(bookings 表)───────────────────────────────────────────────────
|
|
|
|
def list_bookings(status: str | None = None, audit_status: str | None = None) -> list[dict]:
|
|
rows = tdb.list_all("bookings", "created_at DESC")
|
|
if status:
|
|
rows = [r for r in rows if r.get("status") == status]
|
|
if audit_status:
|
|
rows = [r for r in rows if r.get("audit_status") == audit_status]
|
|
# 映射为运营端 Booking 视图
|
|
return [pack_booking(r) for r in rows]
|
|
|
|
|
|
def pack_booking(r: dict) -> dict:
|
|
return {
|
|
"id": r.get("id"),
|
|
"user_id": r.get("username"),
|
|
"user_name": r.get("name"),
|
|
"target_type": "event",
|
|
"target_id": r.get("event_id"),
|
|
"target_title": r.get("event_title"),
|
|
"status": r.get("status"),
|
|
"audit_status": r.get("audit_status"),
|
|
"note": r.get("question"),
|
|
"created_at": r.get("created_at"),
|
|
}
|
|
|
|
|
|
def update_booking(booking_id: str, patch: dict) -> dict | None:
|
|
if tdb.fetch_by_id("bookings", booking_id) is None:
|
|
return None
|
|
allowed = {k: v for k, v in patch.items() if k in ("status", "audit_status", "note", "question")}
|
|
if "note" in allowed:
|
|
allowed["question"] = allowed.pop("note")
|
|
if allowed:
|
|
tdb.update_row("bookings", booking_id, allowed)
|
|
return pack_booking(tdb.fetch_by_id("bookings", booking_id))
|
|
|
|
|
|
# ── 测评(tests 表:OPC 适配度测评记录)─────────────────────────────────────
|
|
|
|
def list_tests() -> list[dict]:
|
|
rows = tdb.list_all("tests", "created_at DESC")
|
|
return [pack_test(r) for r in rows]
|
|
|
|
|
|
def pack_test(r: dict) -> dict:
|
|
return {
|
|
"id": r.get("id"),
|
|
"title": r.get("persona") or "OPC 适配度测评",
|
|
"category": r.get("type_code"),
|
|
"pass_score": 0,
|
|
"question_count": 30,
|
|
"status": "completed",
|
|
"adapt_level": r.get("adapt_level"),
|
|
"tracks": r.get("tracks"),
|
|
"created_at": r.get("created_at"),
|
|
}
|
|
|
|
|
|
def pack_test_if_exists(test_id: str) -> dict | None:
|
|
row = tdb.fetch_by_id("tests", test_id)
|
|
return pack_test(row) if row else None
|
|
|
|
|
|
def create_test(data: dict) -> dict:
|
|
row = {
|
|
"id": tdb.gen_id("T-"),
|
|
"created_at": _now(),
|
|
"username": "",
|
|
"type_code": data.get("category", "opc"),
|
|
"persona": data.get("title", "OPC 适配度测评"),
|
|
"adapt_level": "",
|
|
"tracks": data.get("tracks", ""),
|
|
"version": "",
|
|
}
|
|
tdb.insert("tests", row)
|
|
return pack_test(tdb.fetch_by_id("tests", row["id"]))
|
|
|
|
|
|
def _parse_json(raw):
|
|
if raw is None:
|
|
return None
|
|
if isinstance(raw, (dict, list)):
|
|
return raw
|
|
try:
|
|
return json.loads(raw)
|
|
except (TypeError, ValueError):
|
|
return raw
|
|
|
|
|
|
# ── 调研(survey_logs 表)─────────────────────────────────────────────────
|
|
|
|
def list_surveys() -> list[dict]:
|
|
rows = tdb.list_all("survey_logs", "created_at DESC")
|
|
return [
|
|
{
|
|
"id": r.get("id"),
|
|
"username": r.get("username"),
|
|
"source": r.get("source"),
|
|
"answers": _parse_json(r.get("answers")),
|
|
"created_at": r.get("created_at"),
|
|
}
|
|
for r in rows
|
|
]
|
|
|
|
|
|
# ── 政策(policy_logs 表)─────────────────────────────────────────────────
|
|
|
|
def list_policies() -> list[dict]:
|
|
rows = tdb.list_all("policy_logs", "created_at DESC")
|
|
return [
|
|
{
|
|
"id": r.get("id"),
|
|
"username": r.get("username"),
|
|
"answers": _parse_json(r.get("answers")),
|
|
"policies_count": r.get("policies_count"),
|
|
"subsidies_count": r.get("subsidies_count"),
|
|
"loans_count": r.get("loans_count"),
|
|
"summary": r.get("summary"),
|
|
"created_at": r.get("created_at"),
|
|
}
|
|
for r in rows
|
|
]
|
|
|
|
|
|
# ── 流程(plan_logs 表)───────────────────────────────────────────────────
|
|
|
|
def list_plans() -> list[dict]:
|
|
rows = tdb.list_all("plan_logs", "created_at DESC")
|
|
return [
|
|
{
|
|
"id": r.get("id"),
|
|
"username": r.get("username"),
|
|
"region": r.get("region"),
|
|
"status": r.get("status"),
|
|
"need_park": r.get("need_park"),
|
|
"has_staff": r.get("has_staff"),
|
|
"steps_count": r.get("steps_count"),
|
|
"created_at": r.get("created_at"),
|
|
}
|
|
for r in rows
|
|
]
|