Files
hospital_performance/QWEN.md
2026-02-28 15:16:15 +08:00

17 KiB
Raw Permalink Blame History

QWEN.md - 医院绩效考核管理系统

Context for AI coding agents working in this repository.


Project Overview

A Hospital Performance Management System (医院绩效考核管理系统) designed for a county-level TCM (Traditional Chinese Medicine) hospital. The system provides comprehensive performance assessment management including:

  • Department Management - Hierarchical department structure
  • Staff Management - Employee information and tracking
  • Assessment Indicators - KPI definition and management
  • Performance Evaluation - Assessment workflow with review process
  • Data Analytics - Reports, statistics, and trend analysis
  • Salary Calculation - Performance-based payroll processing

Tech Stack

Layer Technology
Backend FastAPI 0.115+ · SQLAlchemy 2.0 (async) · PostgreSQL · Alembic · Pydantic v2
Frontend Vue 3 (Composition API) · Element Plus · Pinia · Vite · ECharts
Auth JWT (python-jose) · bcrypt password hashing
Database PostgreSQL 14+ (asyncpg driver)

Quick Start

Prerequisites

  • Python 3.10+
  • Node.js 18+
  • PostgreSQL 14+

Backend Setup

cd backend

# Create virtual environment
python -m venv venv
venv\Scripts\activate  # Windows

# Install dependencies
pip install -r requirements.txt

# Configure environment
copy .env.example .env
# Edit .env with your database credentials

# Create database
createdb hospital_performance

# Run migrations
alembic upgrade head

# Initialize default data (optional)
python init_db.py

# Start server
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000

Frontend Setup

cd frontend

# Install dependencies
npm install

# Start dev server (http://localhost:5173)
npm run dev

# Build for production
npm run build

Access the Application

Default Credentials

  • Username: admin
  • Password: admin123

Project Structure

hospital-performance/
├── backend/                          # Backend service
│   ├── app/
│   │   ├── api/v1/                   # API routes
│   │   │   ├── auth.py               # Authentication
│   │   │   ├── departments.py        # Department CRUD
│   │   │   ├── staff.py              # Staff CRUD
│   │   │   ├── indicators.py         # Indicator CRUD
│   │   │   ├── assessments.py        # Assessment workflow
│   │   │   ├── salary.py             # Salary calculation
│   │   │   ├── stats.py              # Statistics & reports
│   │   │   └── finance.py            # Finance management
│   │   ├── core/                     # Core modules
│   │   │   ├── config.py             # Settings management
│   │   │   ├── database.py           # DB connection & session
│   │   │   ├── security.py           # JWT & password hashing
│   │   │   └── init_db.py            # DB initialization
│   │   ├── models/
│   │   │   └── models.py             # SQLAlchemy ORM models
│   │   ├── schemas/
│   │   │   └── schemas.py            # Pydantic v2 schemas
│   │   ├── services/                 # Business logic layer
│   │   │   ├── staff_service.py
│   │   │   ├── department_service.py
│   │   │   ├── indicator_service.py
│   │   │   ├── assessment_service.py
│   │   │   ├── salary_service.py
│   │   │   └── stats_service.py
│   │   ├── utils/                    # Utility functions
│   │   └── main.py                   # FastAPI app factory
│   ├── alembic/                      # Database migrations
│   ├── tests/                        # Test files (empty)
│   ├── requirements.txt
│   └── .env.example
│
├── frontend/                         # Frontend application
│   ├── src/
│   │   ├── api/                      # API client (Axios)
│   │   │   ├── request.js            # Axios instance + interceptors
│   │   │   ├── auth.js
│   │   │   ├── staff.js
│   │   │   ├── department.js
│   │   │   ├── indicator.js
│   │   │   ├── assessment.js
│   │   │   ├── salary.js
│   │   │   └── stats.js
│   │   ├── stores/                   # Pinia stores
│   │   │   ├── user.js               # User state & auth
│   │   │   └── app.js                # App-wide state
│   │   ├── router/
│   │   │   └── index.js              # Vue Router config
│   │   ├── views/                    # Page components
│   │   │   ├── Login.vue
│   │   │   ├── Layout.vue            # Main layout with sidebar
│   │   │   ├── Dashboard.vue
│   │   │   ├── basic/                # Basic data management
│   │   │   │   ├── Departments.vue
│   │   │   │   ├── Staff.vue
│   │   │   │   └── Indicators.vue
│   │   │   ├── assessment/           # Assessment management
│   │   │   │   ├── Assessments.vue
│   │   │   │   └── AssessmentDetail.vue
│   │   │   ├── salary/               # Salary management
│   │   │   │   └── Salary.vue
│   │   │   ├── reports/              # Reports & analytics
│   │   │   │   └── Reports.vue
│   │   │   └── finance/              # Finance management
│   │   │       └── Finance.vue
│   │   ├── components/               # Reusable components
│   │   ├── assets/                   # Static assets (SCSS)
│   │   ├── App.vue
│   │   └── main.js
│   ├── dist/                         # Production build
│   ├── package.json
│   └── vite.config.js
│
├── docs/                             # Documentation
│   ├── index.md
│   ├── architecture.md
│   ├── database.md
│   ├── api.md
│   ├── backend.md
│   └── frontend.md
│
├── AGENTS.md                         # AI agent guidelines
└── README.md

Database Schema

Database: PostgreSQL 14+

Core Tables

Table Description
departments Department hierarchy (tree structure with parent_id)
staff Employee information
indicators Assessment indicators/KPIs
assessments Assessment records (header)
assessment_details Assessment line items (scores per indicator)
salary_records Monthly salary calculations
users System users for authentication

Key Enums

# Department types
DeptType: CLINICAL, MEDICAL_TECH, MEDICAL_AUXILIARY, ADMIN, LOGISTICS

# Staff status
StaffStatus: ACTIVE, LEAVE, RESIGNED, RETIRED

# Assessment workflow status
AssessmentStatus: DRAFT  SUBMITTED  REVIEWED  FINALIZED (or REJECTED)

# Indicator types
IndicatorType: QUALITY, QUANTITY, EFFICIENCY, SERVICE, COST

Assessment Workflow

┌─────────┐    ┌───────────┐    ┌──────────┐    ┌────────────┐
│  DRAFT  │ →  │ SUBMITTED │ →  │ REVIEWED │ →  │ FINALIZED  │
│  草稿   │    │   已提交   │    │  已审核   │    │   已确认    │
└─────────┘    └───────────┘    └──────────┘    └────────────┘
                                       ↓
                                  ┌──────────┐
                                  │ REJECTED │
                                  │  已驳回   │
                                  └──────────┘

Development Conventions

Backend (Python/FastAPI)

Import Order

# 1. Standard library
from datetime import datetime
from typing import Optional, List

# 2. Third-party
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession

# 3. Local imports (absolute paths)
from app.core.database import get_db
from app.schemas.schemas import StaffCreate
from app.services.staff_service import StaffService

Naming Conventions

  • Files: snake_case.py (e.g., staff_service.py)
  • Classes: PascalCase (e.g., StaffService, StaffCreate)
  • Functions/Variables: snake_case (e.g., get_staff_list)
  • Constants: UPPER_SNAKE_CASE

Pydantic v2 Pattern

class StaffResponse(StaffBase):
    """员工响应"""
    model_config = ConfigDict(from_attributes=True)  # Required for ORM mode
    id: int
    status: StaffStatus
    created_at: datetime

Async Database Pattern

async def get_staff_list(db: AsyncSession, page: int = 1, page_size: int = 20):
    offset = (page - 1) * page_size
    result = await db.execute(select(Staff).offset(offset).limit(page_size))
    staff_list = result.scalars().all()
    
    count_result = await db.execute(select(func.count()).select_from(Staff))
    total = count_result.scalar()
    
    return staff_list, total

API Response Format

# Standard response
return {
    "code": 200,
    "message": "success",
    "data": result,
    "total": total,      # For paginated responses
    "page": page,
    "page_size": page_size
}

# Error response
raise HTTPException(status_code=404, detail="员工不存在")

Service Layer Pattern

# API route delegates to service
@router.get("/staff")
async def get_staff_list(
    db: AsyncSession = Depends(get_db),
    page: int = 1,
    page_size: int = 20
):
    staff_list, total = await StaffService.get_list(db, page, page_size)
    return {"data": staff_list, "total": total, "page": page, "page_size": page_size}

Frontend (Vue 3/JavaScript)

Import Order

// 1. Vue
import { ref, reactive, onMounted } from 'vue'

// 2. Third-party
import { ElMessage, ElMessageBox } from 'element-plus'

// 3. Local (use @ alias)
import { getStaffList, createStaff } from '@/api/staff'
import { useUserStore } from '@/stores/user'

Component Pattern (<script setup>)

<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { getStaffList } from '@/api/staff'

// Reactive state
const loading = ref(false)
const tableData = ref([])
const form = reactive({ name: '', status: 'active' })

// Functions
async function loadData() {
  loading.value = true
  try {
    const res = await getStaffList(form)
    tableData.value = res.data || []
  } finally {
    loading.value = false
  }
}

onMounted(() => {
  loadData()
})
</script>

Pinia Store Pattern

// stores/user.js
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', () => {
  const token = ref('')
  const userInfo = ref(null)

  async function login(username, password) {
    const res = await loginApi({ username, password })
    token.value = res.access_token
    localStorage.setItem('token', res.access_token)
  }

  function logout() {
    token.value = ''
    userInfo.value = null
    localStorage.removeItem('token')
  }

  return { token, userInfo, login, logout }
})

API Layer Pattern

// api/staff.js
import request from './request'

export function getStaffList(params) {
  return request.get('/staff', { params })
}

export function createStaff(data) {
  return request.post('/staff', data)
}

Error Handling

// User confirmation
await ElMessageBox.confirm('确定要删除吗?', '提示', { type: 'warning' })

// Feedback
ElMessage.success('操作成功')
ElMessage.error('操作失败')

// Loading state pattern
try {
  await createStaff(form)
  ElMessage.success('创建成功')
} finally {
  submitting.value = false
}

Key API Endpoints

Method Endpoint Description
POST /auth/login User login
GET /auth/me Get current user
GET /departments List departments
POST /departments Create department
GET /staff List staff (paginated)
POST /staff Create staff
GET /indicators List indicators
POST /assessments Create assessment
POST /assessments/batch Batch create assessments
POST /assessments/{id}/submit Submit assessment
POST /assessments/{id}/review Review assessment
POST /salary/generate Generate salary records
GET /stats/department Department statistics
GET /stats/trend Trend analysis

Common Tasks

Add a New API Endpoint

Backend:

  1. Add route in backend/app/api/v1/<module>.py
  2. Add service method in backend/app/services/<module>_service.py
  3. Add Pydantic schemas in backend/app/schemas/schemas.py
  4. Create Alembic migration if DB changes needed

Frontend:

  1. Add API function in frontend/src/api/<module>.js
  2. Create/update view component in frontend/src/views/
  3. Add route in frontend/src/router/index.js

Database Migration

cd backend

# Generate new migration
alembic revision --autogenerate -m "Description of changes"

# Apply migrations
alembic upgrade head

# Rollback one migration
alembic downgrade -1

Run Tests (when available)

cd backend
pytest
pytest tests/test_specific.py -v
pytest -k "test_name" -v

Architecture Notes

Layered Architecture

┌─────────────────────────────────────────┐
│           Frontend (Vue 3)              │
│  Views → Components → Stores → API      │
└───────────────────┬─────────────────────┘
                    │ HTTP/JSON
                    ▼
┌─────────────────────────────────────────┐
│            Backend (FastAPI)            │
│  ┌─────────────────────────────────┐   │
│  │  API Layer (routes)             │   │
│  └──────────────┬──────────────────┘   │
│                 ▼                       │
│  ┌─────────────────────────────────┐   │
│  │  Service Layer (business logic) │   │
│  └──────────────┬──────────────────┘   │
│                 ▼                       │
│  ┌─────────────────────────────────┐   │
│  │  ORM Layer (SQLAlchemy)         │   │
│  └──────────────┬──────────────────┘   │
└───────────────────┼─────────────────────┘
                    ▼
┌─────────────────────────────────────────┐
│         Database (PostgreSQL)           │
└─────────────────────────────────────────┘

Authentication Flow

  1. Client sends credentials to /auth/login
  2. Server validates and returns JWT token
  3. Client stores token in localStorage
  4. Client includes Authorization: Bearer {token} in subsequent requests
  5. Server validates token via get_current_user dependency
  6. 401 responses trigger redirect to login page

Salary Calculation Logic

Assessment (FINALIZED status)
    ↓
Read performance score
    ↓
Apply performance ratio (from staff.perf_ratio)
    ↓
Calculate: performance_bonus = base_salary × (score/100) × perf_ratio
    ↓
Generate SalaryRecord with total_salary = base + bonus - deduction + allowance

Troubleshooting

Backend Issues

Database connection error:

  • Ensure PostgreSQL is running
  • Check DATABASE_URL in .env
  • Verify database exists: createdb hospital_performance
  • Check PostgreSQL credentials (username, password, port)

Migration issues:

# Check migration status
alembic current

# Show pending migrations
alembic history

# Clear and re-migrate (dev only)
alembic downgrade base
alembic upgrade head

Frontend Issues

API requests failing:

  • Ensure backend is running on port 8000
  • Check proxy config in vite.config.js
  • Verify token in localStorage is valid

Build errors:

# Clean and reinstall
rm -rf node_modules package-lock.json
npm install

  • Architecture - System architecture overview
  • Database - ER diagrams and table structures
  • API - Detailed API documentation
  • Backend - Backend development guide
  • Frontend - Frontend development guide