feat: add employee management, backend health check, and UI improvements
This commit is contained in:
@@ -1,5 +1,15 @@
|
||||
from app.models.approval import ApprovalRecord
|
||||
from app.models.employee_change_log import EmployeeChangeLog
|
||||
from app.models.employee import Employee
|
||||
from app.models.organization import OrganizationUnit
|
||||
from app.models.reimbursement import ReimbursementRequest
|
||||
from app.models.role import Role
|
||||
|
||||
__all__ = ["ApprovalRecord", "Employee", "ReimbursementRequest"]
|
||||
__all__ = [
|
||||
"ApprovalRecord",
|
||||
"Employee",
|
||||
"EmployeeChangeLog",
|
||||
"OrganizationUnit",
|
||||
"ReimbursementRequest",
|
||||
"Role",
|
||||
]
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from datetime import date, datetime
|
||||
|
||||
from sqlalchemy import DateTime, String, func
|
||||
from sqlalchemy import Boolean, Column, Date, DateTime, ForeignKey, String, Table, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.db.base_class import Base
|
||||
|
||||
employee_role_links = Table(
|
||||
"employee_role_links",
|
||||
Base.metadata,
|
||||
Column("employee_id", String(36), ForeignKey("employees.id"), primary_key=True),
|
||||
Column("role_id", String(36), ForeignKey("roles.id"), primary_key=True),
|
||||
)
|
||||
|
||||
|
||||
class Employee(Base):
|
||||
__tablename__ = "employees"
|
||||
@@ -15,11 +22,37 @@ class Employee(Base):
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
|
||||
employee_no: Mapped[str] = mapped_column(String(50), unique=True, index=True)
|
||||
name: Mapped[str] = mapped_column(String(100), index=True)
|
||||
department: Mapped[str] = mapped_column(String(100), index=True)
|
||||
email: Mapped[str] = mapped_column(String(255), unique=True, index=True)
|
||||
gender: Mapped[str | None] = mapped_column(String(20), nullable=True)
|
||||
birth_date: Mapped[date | None] = mapped_column(Date(), nullable=True)
|
||||
phone: Mapped[str | None] = mapped_column(String(30), nullable=True)
|
||||
join_date: Mapped[date | None] = mapped_column(Date(), nullable=True)
|
||||
location: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
||||
position: Mapped[str] = mapped_column(String(100), default="员工")
|
||||
grade: Mapped[str] = mapped_column(String(20), default="P3", index=True)
|
||||
cost_center: Mapped[str | None] = mapped_column(String(50), nullable=True)
|
||||
finance_owner_name: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
||||
employment_status: Mapped[str] = mapped_column(String(30), default="在职", index=True)
|
||||
sync_state: Mapped[str] = mapped_column(String(30), default="已同步")
|
||||
spotlight: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
last_sync_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
organization_unit_id: Mapped[str | None] = mapped_column(
|
||||
ForeignKey("organization_units.id"), nullable=True, index=True
|
||||
)
|
||||
manager_id: Mapped[str | None] = mapped_column(ForeignKey("employees.id"), nullable=True, index=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
||||
)
|
||||
|
||||
organization_unit = relationship("OrganizationUnit", back_populates="employees")
|
||||
manager = relationship("Employee", remote_side=[id], back_populates="reports")
|
||||
reports = relationship("Employee", back_populates="manager")
|
||||
roles = relationship("Role", secondary=employee_role_links, back_populates="employees")
|
||||
change_logs = relationship(
|
||||
"EmployeeChangeLog",
|
||||
back_populates="employee",
|
||||
cascade="all, delete-orphan",
|
||||
order_by="desc(EmployeeChangeLog.occurred_at)",
|
||||
)
|
||||
reimbursement_requests = relationship("ReimbursementRequest", back_populates="employee")
|
||||
|
||||
21
server/src/app/models/employee_change_log.py
Normal file
21
server/src/app/models/employee_change_log.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, ForeignKey, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.db.base_class import Base
|
||||
|
||||
|
||||
class EmployeeChangeLog(Base):
|
||||
__tablename__ = "employee_change_logs"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
|
||||
employee_id: Mapped[str] = mapped_column(ForeignKey("employees.id"), index=True)
|
||||
action: Mapped[str] = mapped_column(String(255))
|
||||
owner: Mapped[str] = mapped_column(String(100))
|
||||
occurred_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), index=True)
|
||||
|
||||
employee = relationship("Employee", back_populates="change_logs")
|
||||
32
server/src/app/models/organization.py
Normal file
32
server/src/app/models/organization.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, ForeignKey, String, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.db.base_class import Base
|
||||
|
||||
|
||||
class OrganizationUnit(Base):
|
||||
__tablename__ = "organization_units"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
|
||||
unit_code: Mapped[str] = mapped_column(String(50), unique=True, index=True)
|
||||
name: Mapped[str] = mapped_column(String(100), index=True)
|
||||
unit_type: Mapped[str] = mapped_column(String(30), default="department", index=True)
|
||||
parent_id: Mapped[str | None] = mapped_column(
|
||||
ForeignKey("organization_units.id"), nullable=True, index=True
|
||||
)
|
||||
cost_center: Mapped[str | None] = mapped_column(String(50), nullable=True)
|
||||
location: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
||||
manager_name: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
||||
)
|
||||
|
||||
parent = relationship("OrganizationUnit", remote_side=[id], back_populates="children")
|
||||
children = relationship("OrganizationUnit", back_populates="parent")
|
||||
employees = relationship("Employee", back_populates="organization_unit")
|
||||
24
server/src/app/models/role.py
Normal file
24
server/src/app/models/role.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, String, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.db.base_class import Base
|
||||
|
||||
|
||||
class Role(Base):
|
||||
__tablename__ = "roles"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
|
||||
role_code: Mapped[str] = mapped_column(String(50), unique=True, index=True)
|
||||
name: Mapped[str] = mapped_column(String(100), unique=True, index=True)
|
||||
description: Mapped[str] = mapped_column(String(500), default="")
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
||||
)
|
||||
|
||||
employees = relationship("Employee", secondary="employee_role_links", back_populates="roles")
|
||||
Reference in New Issue
Block a user