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

577 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
```bash
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
```bash
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
```python
# 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
```python
# 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
```python
class StaffResponse(StaffBase):
"""员工响应"""
model_config = ConfigDict(from_attributes=True) # Required for ORM mode
id: int
status: StaffStatus
created_at: datetime
```
#### Async Database Pattern
```python
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
```python
# 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
```python
# 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
```javascript
// 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>`)
```vue
<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
```javascript
// 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
```javascript
// 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
```javascript
// 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
```bash
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)
```bash
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:**
```bash
# 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:**
```bash
# Clean and reinstall
rm -rf node_modules package-lock.json
npm install
```
---
## Related Documentation
- [Architecture](docs/architecture.md) - System architecture overview
- [Database](docs/database.md) - ER diagrams and table structures
- [API](docs/api.md) - Detailed API documentation
- [Backend](docs/backend.md) - Backend development guide
- [Frontend](docs/frontend.md) - Frontend development guide