提交文件
This commit is contained in:
576
QWEN.md
Normal file
576
QWEN.md
Normal 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
|
||||
Reference in New Issue
Block a user