17 KiB
17 KiB
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
- Frontend: http://localhost:5173
- API Docs: http://localhost:8000/api/v1/docs
- Health Check: http://localhost:8000/health
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:
- Add route in
backend/app/api/v1/<module>.py - Add service method in
backend/app/services/<module>_service.py - Add Pydantic schemas in
backend/app/schemas/schemas.py - Create Alembic migration if DB changes needed
Frontend:
- Add API function in
frontend/src/api/<module>.js - Create/update view component in
frontend/src/views/ - 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
- Client sends credentials to
/auth/login - Server validates and returns JWT token
- Client stores token in
localStorage - Client includes
Authorization: Bearer {token}in subsequent requests - Server validates token via
get_current_userdependency - 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_URLin.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
Related Documentation
- Architecture - System architecture overview
- Database - ER diagrams and table structures
- API - Detailed API documentation
- Backend - Backend development guide
- Frontend - Frontend development guide