File size: 14,357 Bytes
b4f2238 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | # 法律事务管理系统 - Backend Makefile
# ============================================================
check: lint typecheck ## 运行所有检查
# ============================================================
# GitHub CI 镜像检查(推送前必跑,避免反复修 7 轮)
# ============================================================
ci-check: ## 模拟 GitHub backend (3.12) job 的核心检查(changed-files 模式)
@echo "$(GREEN)=== GitHub CI 预检 (changed-files 模式) ===$(NC)"
@echo ""
@if [ -n "$$CI_CHECK_BASE" ]; then \
BASE="$$CI_CHECK_BASE"; \
else \
UPSTREAM=$$(git rev-parse --abbrev-ref @{u} 2>/dev/null | sed 's|origin/||' || true); \
if [ -n "$$UPSTREAM" ]; then \
BASE="$$UPSTREAM"; \
else \
BASE="main"; \
fi; \
fi; \
if ! git rev-parse "$$BASE" &>/dev/null 2>&1; then \
echo "⚠️ 基准 '$$BASE' 不存在,尝试 merge-base..."; \
BASE=$$(git merge-base HEAD HEAD~1 2>/dev/null || echo ""); \
fi; \
echo "📋 基准: $(YELLOW)$$BASE$(NC) → $(YELLOW)HEAD$(NC)"; \
CHANGED=$$(git diff --name-only --diff-filter=ACMRT $$BASE HEAD 2>/dev/null | grep -E '^apps/.*\.py$$' | sed 's|^apps/||' || true); \
if [ -z "$$CHANGED" ]; then \
echo "✅ 无变更 Python 文件"; \
else \
echo "🔍 [1/3] Ruff (changed files): $$CHANGED"; \
$(PYTHON) -m ruff check $$CHANGED --config ruff.toml; \
echo ""; \
echo "🔍 [2/3] Mypy (changed files, follow-imports=skip): $$CHANGED"; \
PYTHONPATH=apiSystem:. $(PYTHON) -m mypy --config-file mypy.ini --follow-imports=skip $$CHANGED; \
echo ""; \
fi
@echo "🔍 [3/3] 测试收集 (structure smoke):"
$(TEST_ENV) $(PYTEST) tests/ci/structure/ --co -q 2>&1 | tail -3
@echo ""
@echo "$(GREEN)✅ GitHub CI 预检通过! 可以放心推送了$(NC)"
ci-check-full: ## 完整 CI 预检 (含 unit/integration/property smoke)
$(MAKE) ci-check
@echo "🧪 运行 CI 测试套件..."
$(TEST_ENV) $(PYTEST) tests/ci/unit/ -q $(ARGS)
$(TEST_ENV) $(PYTEST) tests/ci/integration/ -q $(ARGS)
$(TEST_ENV) HYPOTHESIS_PROFILE=default $(PYTEST) tests/ci/property/ -q $(ARGS)
.PHONY: help install run test migrate shell lint clean ci-check ci-check-full qcluster-dev
# 默认 Python 和虚拟环境
VENV_DIR ?= .venv
UV := uv
PYTHON := $(VENV_DIR)/bin/python
PYTEST := $(VENV_DIR)/bin/pytest
MANAGE := $(PYTHON) apiSystem/manage.py
UVICORN := $(VENV_DIR)/bin/uvicorn
WATCHFILES := $(VENV_DIR)/bin/watchfiles
PORT ?= 8002
RUN_DEV_FORCE_POLLING ?= true
RUN_DEV_POLL_DELAY_MS ?= 300
QCLUSTER_DEV_FORCE_POLLING ?= true
QCLUSTER_DEV_POLL_DELAY_MS ?= 300
QCLUSTER_WATCH_PATHS ?= apps apiSystem
# 数据库配置(默认 PostgreSQL,可通过 DB_ENGINE=sqlite 切换)
DB_ENGINE ?= postgresql
DATABASE_PATH ?=
DB_NAME ?= fachuan_dev
DB_USER ?= postgres
DB_PASSWORD ?= postgres
DB_HOST ?= 127.0.0.1
DB_PORT ?= 5432
# 测试数据库配置
TEST_DB_ENGINE ?= postgresql
TEST_DB_PATH ?= /tmp/fachuan_pytest.sqlite3
TEST_DB_NAME ?= fachuan_test
TEST_DB_USER ?= postgres
TEST_DB_PASSWORD ?= postgres
TEST_DB_HOST ?= 127.0.0.1
TEST_DB_PORT ?= 5432
export DB_ENGINE DATABASE_PATH DB_NAME DB_USER DB_PASSWORD DB_HOST DB_PORT
# 颜色定义
GREEN := \033[0;32m
YELLOW := \033[0;33m
NC := \033[0m # No Color
TEST_ENV = DJANGO_DEBUG=1 DB_ENGINE=$(TEST_DB_ENGINE) DATABASE_PATH=$(TEST_DB_PATH) DB_NAME=$(TEST_DB_NAME) DB_USER=$(TEST_DB_USER) DB_PASSWORD=$(TEST_DB_PASSWORD) DB_HOST=$(TEST_DB_HOST) DB_PORT=$(TEST_DB_PORT) PYTHONPATH=apiSystem:.
help: ## 显示帮助信息
@echo "$(GREEN)法律事务管理系统 - Backend$(NC)"
@echo ""
@echo "可用命令:"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " $(YELLOW)%-15s$(NC) %s\n", $$1, $$2}'
# ============================================================
# 安装和配置
# ============================================================
install: ## 安装生产依赖
$(UV) sync --no-dev
install-dev: ## 安装全部依赖(含 dev + test)
$(UV) sync
venv: ## 创建虚拟环境
$(UV) venv $(VENV_DIR) --python 3.12
@echo "$(GREEN)虚拟环境已创建,请运行: source $(VENV_DIR)/bin/activate$(NC)"
# ============================================================
# 开发服务器
# ============================================================
run: migrate ## 启动开发服务器 (默认端口 8002, 使用 uvicorn, 可通过 PORT=8003 修改)
cd apiSystem && ../$(UVICORN) apiSystem.asgi:application --host 0.0.0.0 --port $(PORT)
run-dev: migrate ## 启动开发服务器 (热重载稳定模式:默认启用 polling,避免与 qcluster 并行时卡住)
cd apiSystem && WATCHFILES_FORCE_POLLING=$(RUN_DEV_FORCE_POLLING) WATCHFILES_POLL_DELAY_MS=$(RUN_DEV_POLL_DELAY_MS) ../$(UVICORN) apiSystem.asgi:application --host 0.0.0.0 --port $(PORT) --reload --reload-delay 0.5 --reload-dir ../apps --reload-dir . --reload-include '*.html'
run-port: migrate ## 启动开发服务器 (自定义端口, 使用 PORT=8001)
cd apiSystem && ../$(UVICORN) apiSystem.asgi:application --host 0.0.0.0 --port $(PORT)
# ============================================================
# 数据库
# ============================================================
migrate: ## 运行数据库迁移
$(MANAGE) migrate
makemigrations: ## 创建迁移文件
$(MANAGE) makemigrations
migrations: makemigrations migrate ## 创建并运行迁移
showmigrations: ## 显示迁移状态
$(MANAGE) showmigrations
resetdb: ## 重置数据库 (危险!)
@echo "$(YELLOW)警告: 这将删除所有数据!$(NC)"
@read -p "确认继续? [y/N] " confirm && [ "$$confirm" = "y" ]
@if [ "$(DB_ENGINE)" = "sqlite" ]; then \
rm -f apiSystem/db.sqlite3; \
$(MANAGE) migrate; \
elif [ "$(DB_ENGINE)" = "postgresql" ]; then \
$(MANAGE) flush --noinput; \
$(MANAGE) migrate; \
else \
echo "$(YELLOW)未知 DB_ENGINE=$(DB_ENGINE),使用 flush + migrate 兜底$(NC)"; \
$(MANAGE) flush --noinput; \
$(MANAGE) migrate; \
fi
@echo "$(GREEN)数据库已重置 (DB_ENGINE=$(DB_ENGINE))$(NC)"
# ============================================================
# 用户管理
# ============================================================
superuser: ## 创建超级用户
$(MANAGE) createsuperuser
# ============================================================
# 测试
# ============================================================
test: ## 运行测试
$(TEST_ENV) $(PYTEST) tests -v --tb=short $(ARGS)
test-cov: ## 运行测试 (带覆盖率)
$(TEST_ENV) $(PYTEST) tests -v --tb=short --cov=apps --cov-report=html --cov-report=term
test-fast: ## 快速测试 (不显示详情)
$(TEST_ENV) $(PYTEST) tests -q
test-unit: ## 运行单元测试
$(TEST_ENV) $(PYTEST) tests/unit -v --tb=short $(ARGS)
test-integration: ## 运行集成测试
$(TEST_ENV) $(PYTEST) tests/integration -v --tb=short $(ARGS)
test-property: ## 运行 Property-Based Tests
$(TEST_ENV) $(PYTEST) tests/property -v --tb=short $(ARGS)
test-structure: ## 运行结构验证测试
$(TEST_ENV) $(PYTEST) tests/structure -v --tb=short --no-cov $(ARGS)
test-admin: ## 运行 Admin 测试
$(TEST_ENV) $(PYTEST) tests/admin -v --tb=short $(ARGS)
# ============================================================
# 代码质量
# ============================================================
RUFF_RATCHET_PATHS = \
apps/core/security/scrub.py \
apps/core/infrastructure/cache.py \
apps/automation/models/token.py \
apps/automation/services/automation_service_adapter.py \
apps/automation/services/scraper/core/token_service.py \
apps/automation/services/token/cache_manager.py \
apps/automation/services/token/history_recorder.py \
apps/automation/services/token/token_acquisition_orchestrator.py \
apps/automation/services/token/trigger_reasons.py \
apps/client/services/wiring.py \
apps/chat_records/services/wiring.py \
apps/core/services/wiring.py \
apps/core/services/conversation_service.py \
apps/core/services/cause_court_initialization_service.py \
apps/core/services/court_tokens/baoquan_token_service.py \
apps/litigation_ai/services/wiring.py \
apps/litigation_ai/agent/factory.py \
apps/litigation_ai/agent/llm_provider.py \
apps/litigation_ai/agent/middleware.py \
apps/litigation_ai/chains/document_type_parse_chain.py \
apps/litigation_ai/chains/litigation_draft_chain.py \
apps/litigation_ai/chains/litigation_goal_intake_chain.py \
apps/litigation_ai/chains/user_choice_parse_chain.py
lint-ratchet: ## 代码检查(仅棘轮路径)
$(PYTHON) -m ruff check $(RUFF_RATCHET_PATHS) --config ruff.toml
lint: ## 全量代码检查 apps/
$(PYTHON) -m ruff check apps --config ruff.toml
lint-full: lint ## 全量代码检查(lint 别名)
format: ## 格式化代码
$(PYTHON) -m black apps --line-length=120
$(PYTHON) -m isort apps
typecheck-ratchet: ## 类型检查(仅棘轮路径)
$(PYTHON) -m mypy \
apps/core/infrastructure/throttling.py \
apps/core/middleware_request_id.py \
apps/core/exceptions_handlers.py \
apps/core/httpx_clients.py \
apps/core/config/django_runtime.py \
apps/core/protocols/common_protocols.py \
apps/core/dto/auth.py \
apps/core/llm/config.py \
apps/core/llm/service.py \
apps/core/llm/router.py \
apps/core/llm/backends/base.py \
apps/core/services/wiring.py \
apps/core/services/system_config_service.py \
apps/core/security/secret_codec.py \
apps/core/dependencies/automation_sms.py \
apps/core/security/scrub.py \
apps/automation/services/document/document_processing_service_adapter.py \
apps/automation/services/token/trigger_reasons.py \
apps/automation/services/scraper/core/token_service.py \
apps/cases/services/case/case_service.py \
apps/contracts/services/contract/contract_service_adapter.py \
apps/organization/services/org_access_computation_service.py \
apps/organization/services/wiring.py \
--follow-imports=skip \
--config-file mypy.ini
typecheck: ## 全量类型检查 apps/
$(PYTHON) -m mypy apps --config-file mypy.ini
typecheck-full: typecheck ## 全量类型检查(typecheck 别名)
check: lint typecheck ## 运行所有检查
# ============================================================
# Django 工具
# ============================================================
shell: ## Django Shell
$(MANAGE) shell
dbshell: ## 数据库 Shell
$(MANAGE) dbshell
collectstatic: ## 收集静态文件
$(MANAGE) collectstatic --noinput
# ============================================================
# 后台任务
# ============================================================
qcluster: migrate ## 启动 Django-Q 任务队列(与 Django Web 启动顺序不限)
@pkill -9 -f 'python.*manage.py qcluster' 2>/dev/null || true
@sleep 1
$(MANAGE) qcluster
qcluster-start: migrate ## 启动 Django-Q 并处理待处理任务(与 Django Web 启动顺序不限)
@pkill -9 -f 'python.*manage.py qcluster' 2>/dev/null || true
@sleep 1
$(MANAGE) process_pending_tasks --reset
$(MANAGE) qcluster
qcluster-dev: migrate ## 启动 Django-Q(文件变化自动重启)
@echo "$(GREEN)启动 qcluster-dev: 检测 Python 文件变化后自动重启 qcluster$(NC)"
@echo "$(YELLOW)监听目录: $(QCLUSTER_WATCH_PATHS)$(NC)"
@WATCHFILES_FORCE_POLLING=$(QCLUSTER_DEV_FORCE_POLLING) WATCHFILES_POLL_DELAY_MS=$(QCLUSTER_DEV_POLL_DELAY_MS) $(WATCHFILES) --target-type command --filter python --ignore-paths ".venv,node_modules,.git,__pycache__,media,staticfiles,logs,htmlcov,.pytest_cache" "$(MANAGE) qcluster" $(QCLUSTER_WATCH_PATHS)
process-tasks: ## 处理待处理的爬虫任务
$(MANAGE) process_pending_tasks
process-tasks-reset: ## 重置卡住任务并处理待处理任务
$(MANAGE) process_pending_tasks --reset
process-tasks-dry: ## 显示待处理任务(不执行)
$(MANAGE) process_pending_tasks --dry-run
# ============================================================
# 爬虫测试
# ============================================================
test-court-login: ## 测试法院"一张网"登录
$(PYTHON) apps/automation/tests/test_court_zxfw_login.py
check-admin: ## 检查 Admin 配置是否正确
$(PYTHON) scripts/check_admin_config.py
# ============================================================
# Token 管理
# ============================================================
token-example: ## 运行 Token 使用示例
$(PYTHON) scripts/example_use_token.py
test-token: ## 测试 Token 服务
PYTHONPATH=apiSystem:. $(PYTEST) apps/automation/tests/test_token_service.py -v
migrate-token: ## 执行 Token 相关的数据库迁移
$(MANAGE) migrate automation
@echo "$(GREEN)Token 数据库迁移完成$(NC)"
debug-token: ## 调试 Token 捕获问题
$(PYTHON) scripts/debug_token_capture.py
# ============================================================
# 清理
# ============================================================
clean: ## 清理临时文件
find . -type f -name "*.pyc" -delete
find . -type d -name "__pycache__" -delete
find . -type d -name ".pytest_cache" -exec rm -rf {} + 2>/dev/null || true
find . -type f -name ".DS_Store" -delete
rm -rf htmlcov/
rm -rf .coverage
@echo "$(GREEN)清理完成$(NC)"
clean-logs: ## 清理日志文件
rm -f logs/*.log
@echo "$(GREEN)日志已清理$(NC)"
# ============================================================
# Docker (可选)
# ============================================================
docker-build: ## 构建 Docker 镜像
docker build -t lawfirm-backend .
docker-run: ## 运行 Docker 容器
docker run -p 8000:8000 lawfirm-backend
# ============================================================
# 健康检查
# ============================================================
health: ## 检查服务健康状态
@curl -sf http://localhost:$(PORT)/health/ && echo " OK" || echo "服务未运行"
health-detail: ## 详细健康检查
@curl -sf http://localhost:$(PORT)/health/ && echo " OK (db connected)" || echo "服务未运行或数据库不可用"
# ============================================================
# 国际化 (i18n)
# ============================================================
messages: ## 提取翻译字符串,生成 .po 文件
cd apiSystem && ../$(PYTHON) manage.py makemessages -l en -l zh_Hans --ignore='.venv/*' --ignore='venv312/*'
compilemessages: ## 编译 .po 为 .mo 文件
cd apiSystem && ../$(PYTHON) manage.py compilemessages
|