feat: 完善前后端核心功能模块

后端:
- 新增认证(auth)、任务(tasks)、映射(mappings)、对账(reconciliation)、异常(exceptions)、导出(exports) API
- 新增核心模块: database, security, permissions, tenant, exceptions, error_handlers
- 新增数据模型: user, company, reconciliation_task, field_mapping, uploaded_file 等
- 新增服务层: ai_recognizer, file_parser, file_storage, mapping, reconciliation 等
- 添加数据库迁移脚本

前端:
- 新增登录页面和仪表盘页面
- 新增任务列表、任务详情、字段映射页面
- 新增异常处理页面和规则设置页面
- 新增 API 代理路由 /api/[...path]
- 新增 UI 组件库 (button, card, dialog, input, table 等)
- 新增 auth 组件 (ProtectedRoute, PermissionGate)
- 新增 layout 组件 (Header, Sidebar)
- 新增 mapping 组件 (FieldMappingTable, AISuggestionPanel)
- 新增 API 客户端和 hooks (useAsync, useToast, usePermission 等)
- 新增状态管理 (auth-store, company-store, ui-store)
- 集成 Tailwind CSS 和 shadcn/ui 组件库

其他:
- 添加 Alembic 数据库迁移配置
- 添加初始化示例数据脚本
- 更新项目文档
This commit is contained in:
freedakgmail
2026-07-07 09:04:47 +08:00
parent 8487f6eadf
commit 33b4c734aa
117 changed files with 22070 additions and 295 deletions
+180
View File
@@ -0,0 +1,180 @@
from datetime import datetime
from functools import wraps
from typing import Any, Callable, Optional
from fastapi import Request
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.audit_log import AuditAction, AuditLog
from app.schemas.audit_log import AuditLogCreate, AuditLogQuery
class AuditService:
"""审计日志服务"""
@staticmethod
async def log_action(
db: AsyncSession,
company_id: int,
action: str,
resource_type: str,
user_id: Optional[int] = None,
resource_id: Optional[str] = None,
details: Optional[dict[str, Any]] = None,
ip_address: Optional[str] = None,
user_agent: Optional[str] = None,
) -> AuditLog:
"""
记录审计日志
Args:
db: 数据库会话
company_id: 企业 ID
action: 操作类型
resource_type: 资源类型
user_id: 用户 ID
resource_id: 资源 ID
details: 操作详情
ip_address: IP 地址
user_agent: User-Agent
Returns:
创建的审计日志对象
"""
log_data = AuditLogCreate(
company_id=company_id,
user_id=user_id,
action=action,
resource_type=resource_type,
resource_id=resource_id,
details=details,
ip_address=ip_address,
user_agent=user_agent,
)
audit_log = AuditLog(**log_data.model_dump())
db.add(audit_log)
await db.commit()
await db.refresh(audit_log)
return audit_log
@staticmethod
async def get_logs(
db: AsyncSession,
query_params: AuditLogQuery,
) -> list[AuditLog]:
"""
查询审计日志
Args:
db: 数据库会话
query_params: 查询参数
Returns:
审计日志列表
"""
stmt = select(AuditLog)
# 添加过滤条件
if query_params.company_id:
stmt = stmt.where(AuditLog.company_id == query_params.company_id)
if query_params.user_id:
stmt = stmt.where(AuditLog.user_id == query_params.user_id)
if query_params.action:
stmt = stmt.where(AuditLog.action == query_params.action)
if query_params.resource_type:
stmt = stmt.where(AuditLog.resource_type == query_params.resource_type)
if query_params.resource_id:
stmt = stmt.where(AuditLog.resource_id == query_params.resource_id)
if query_params.start_date:
stmt = stmt.where(AuditLog.created_at >= query_params.start_date)
if query_params.end_date:
stmt = stmt.where(AuditLog.created_at <= query_params.end_date)
# 排序和分页
stmt = stmt.order_by(AuditLog.created_at.desc())
stmt = stmt.offset(query_params.skip).limit(query_params.limit)
result = await db.execute(stmt)
return list(result.scalars().all())
@staticmethod
async def log_from_request(
db: AsyncSession,
request: Request,
company_id: int,
action: str,
resource_type: str,
user_id: Optional[int] = None,
resource_id: Optional[str] = None,
details: Optional[dict[str, Any]] = None,
) -> AuditLog:
"""
从 Request 对象中提取信息并记录审计日志
Args:
db: 数据库会话
request: FastAPI 请求对象
company_id: 企业 ID
action: 操作类型
resource_type: 资源类型
user_id: 用户 ID
resource_id: 资源 ID
details: 操作详情
Returns:
创建的审计日志对象
"""
# 提取 IP 地址
ip_address = request.client.host if request.client else None
# 提取 User-Agent
user_agent = request.headers.get("user-agent")
return await AuditService.log_action(
db=db,
company_id=company_id,
action=action,
resource_type=resource_type,
user_id=user_id,
resource_id=resource_id,
details=details,
ip_address=ip_address,
user_agent=user_agent,
)
# 单例实例
audit_service = AuditService()
def audit_log(
action: str,
resource_type: str,
get_resource_id: Optional[Callable] = None,
):
"""
审计日志装饰器
Args:
action: 操作类型
resource_type: 资源类型
get_resource_id: 从函数返回值中获取 resource_id 的函数
Example:
@audit_log(action=AuditAction.CREATE, resource_type="company")
async def create_company(...):
...
"""
def decorator(func: Callable):
@wraps(func)
async def wrapper(*args, **kwargs):
result = await func(*args, **kwargs)
# TODO: 在实现认证后,从上下文获取 company_id 和 user_id
# 目前暂时跳过实际记录
return result
return wrapper
return decorator