Files
gochat/backend/scripts/compare_routes.py
T
rogee aeddedf2a3 Reorganize repo: backend/, deploy/, docs/ layout + AGENTS.md
Restructure the monorepo into clear top-level directories:
- backend/: Go module root (cmd, internal, pkg, configs, migrations,
  docs/swagger, scripts, tests, go.mod, Makefile, .air.toml)
- deploy/: Docker (Dockerfile, docker-compose*), quickstart, fluentd
- docs/: project documentation + reports/ (moved from repo root)
- AGENTS.md: new AI coding-agent guide at repo root

Update all references to the new layout:
- Dockerfile: COPY backend/go.mod, COPY backend/ (context = repo root)
- docker-compose files: context ../.., dockerfile deploy/docker/Dockerfile,
  env_file ../../.env, volume mounts ../../backend:/app
- deploy/quickstart/compose.yaml: dockerfile deploy/docker/Dockerfile
- CI: working-directory: backend for go commands, file deploy/docker/Dockerfile,
  coverage path backend/coverage.out, health_check backend/scripts/
- backend/Makefile: docker target uses -f ../deploy/docker/Dockerfile ../
- README: architecture tree, quickstart, config paths updated

Move root stray scripts (rename_models.*, run_m11_tests.sh, verify_build.sh,
gorm_bool_main.go) to backend/scripts/legacy/. All moves via git mv to
preserve history. Build, vet, SQLite tests, and docker compose config verified.
2026-07-07 14:44:12 +08:00

100 lines
3.6 KiB
Python

#!/usr/bin/env python3
"""Precise comparison of GoChat routes vs Chatwoot routes.
Normalizes parameter names (:account_id → :id, etc.) for fair comparison.
"""
import re
import sys
def normalize(path):
"""Normalize route params to generic :id for comparison."""
# Replace all :xxx_id variants with :id
path = re.sub(r':[a-zA-Z_]+_id', ':id', path)
# Replace :website_token, :thread_id, :slug, :identifier, etc. with :id
path = re.sub(r':(website_token|thread_id|slug|identifier|channel_type|fb_id|locale|category_slug|article_slug|portal_id|user_id)', ':id', path)
# Remove trailing slashes
path = path.rstrip('/')
return path
def load_routes(filepath):
routes = {}
with open(filepath) as f:
for line in f:
line = line.strip()
if not line:
continue
parts = line.split(' ', 1)
if len(parts) == 2:
method, path = parts
norm = normalize(path)
routes[(method, norm)] = (method, path)
return routes
def main():
gochat = load_routes('/tmp/gochat_routes_all.txt')
chatwoot = load_routes('/tmp/chatwoot_routes_all.txt')
gochat_keys = set(gochat.keys())
chatwoot_keys = set(chatwoot.keys())
matched = gochat_keys & chatwoot_keys
missing_in_gochat = chatwoot_keys - gochat_keys
extra_in_gochat = gochat_keys - chatwoot_keys
print("=" * 80)
print("GoChat vs Chatwoot 路由对比报告")
print("=" * 80)
print(f"\nGoChat 路由总数: {len(gochat_keys)}")
print(f"Chatwoot API路由总数: {len(chatwoot_keys)}")
print(f"匹配路由数: {len(matched)}")
print(f"GoChat缺失路由数: {len(missing_in_gochat)}")
print(f"GoChat多余路由数: {len(extra_in_gochat)}")
print(f"覆盖率: {len(matched)/len(chatwoot_keys)*100:.1f}%")
# Group missing routes by resource prefix
print(f"\n{'=' * 80}")
print("GoChat缺失路由 (按资源分组)")
print("=" * 80)
by_resource = {}
for key in sorted(missing_in_gochat):
method, norm_path = key
orig_method, orig_path = chatwoot[key]
# Extract resource name
m = re.match(r'/api/v1(/platform/api/v1)?(/public)?(/[a-zA-Z_]+(?:_scope)?/?)?(/[a-zA-Z_]+)', norm_path)
if m:
resource = m.group(0)
else:
resource = norm_path.split('/:id')[0] if '/:id' in norm_path else norm_path
by_resource.setdefault(resource, []).append((method, orig_path, norm_path))
for resource in sorted(by_resource.keys()):
routes = by_resource[resource]
print(f"\n [{resource}] — 缺失{len(routes)}条路由:")
for method, orig_path, norm_path in sorted(routes):
print(f" {method} {orig_path}")
# Group extra routes
print(f"\n{'=' * 80}")
print("GoChat多余路由 (Chatwoot中不存在)")
print("=" * 80)
by_resource_extra = {}
for key in sorted(extra_in_gochat):
method, norm_path = key
orig_method, orig_path = gochat[key]
# Extract top-level prefix
parts = norm_path.split('/')
if len(parts) >= 4:
resource = '/' + '/'.join(parts[:4])
else:
resource = norm_path
by_resource_extra.setdefault(resource, []).append((method, orig_path))
for resource in sorted(by_resource_extra.keys()):
routes = by_resource_extra[resource]
print(f"\n [{resource}] — 多余{len(routes)}条路由:")
for method, orig_path in sorted(routes):
print(f" {method} {orig_path}")
if __name__ == '__main__':
main()