提交文件

This commit is contained in:
2026-02-28 15:16:15 +08:00
parent 1a4e50e0a4
commit 44f250f58e
159 changed files with 61268 additions and 0 deletions

576
QWEN.md Normal file
View File

@@ -0,0 +1,576 @@
# 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