Files
opc-manager/backend/helpers.py
mac caebf90438 重构:flask_app.py 拆分为 db/helpers/routes/seed_data + Blueprint
- flask_app.py 1166行→33行纯入口
- 新建 db.py(配置+连接+SQL工具)
- 新建 helpers.py(attach_common/monthly_finance/add_file_index)
- 新建 routes.py(全路由 Blueprint + 装饰器 + TABLES)
- 新建 migrations/seed_data.py(seed_db 搬迁)
- migrations/{tables,columns,data_fixes,seed}.py 改 import 为 from db
- 删除死代码 init_db(228行)+ latest_followup(10行)
- 反向依赖消除:migrations 不再 import flask_app
- 前端零改动,URL 不变
2026-07-02 18:30:24 +08:00

106 lines
3.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# helpers.py — 业务查询辅助函数
# 依赖db.py被 routes.py 和 migrations/seed_data.py 调用
import json
from pathlib import Path
from db import _exec, rows, one
def add_file_index(conn, module, owner_id, owner_version, category, path, external=True):
path = Path(path)
if not path.exists():
return
_exec(conn,
"""INSERT INTO file_assets
(module,owner_id,owner_version,file_category,file_name,file_type,file_size,file_path,is_external)
VALUES (?,?,?,?,?,?,?,?,?)""",
(module, owner_id, owner_version, category, path.name, path.suffix.lower().lstrip("."), path.stat().st_size, str(path), 1 if external else 0),
)
def attach_common(conn, resource, items):
"""批量加载 followups 和 files避免 N+1 查询"""
if not items:
return items
target_map = {"sales": "sales", "proposals": "proposal", "operations": "operation", "products": "product"}
target_type = target_map.get(resource)
ids = [item["id"] for item in items]
# 批量查 followups一次性 IN 查询)
if target_type:
placeholders = ",".join(["?"] * len(ids))
all_followups = rows(
conn,
f"SELECT * FROM follow_up_records WHERE target_type=? AND target_id IN ({placeholders}) ORDER BY followed_at DESC, id DESC",
[target_type] + ids,
)
followups_by_id = {}
for fu in all_followups:
followups_by_id.setdefault(fu["target_id"], []).append(fu)
for item in items:
item["followups"] = followups_by_id.get(item["id"], [])
item["latest_follow_up_record"] = item["followups"][0]["content"] if item["followups"] else ""
# 批量查 filesproposals + operations
file_modules = {"proposals": "proposal", "operations": "operation"}
if resource in file_modules:
module = file_modules[resource]
placeholders = ",".join(["?"] * len(ids))
all_files = rows(
conn,
f"SELECT * FROM file_assets WHERE module=? AND owner_id IN ({placeholders}) ORDER BY id DESC",
[module] + ids,
)
files_by_id = {}
for f in all_files:
files_by_id.setdefault(f["owner_id"], []).append(f)
for item in items:
item["files"] = files_by_id.get(item["id"], [])
return items
def monthly_finance(conn, tenant="科普·无界"):
months = [f"2026-{m:02d}" for m in range(1, 13)]
pfs = rows(conn,
"SELECT sign_amount, sign_month, status, budget_data FROM project_finances WHERE tenant=? AND status='已签约'",
[tenant])
parsed_budgets = []
for pf in pfs:
try:
budget = json.loads(pf.get("budget_data") or "[]")
except (json.JSONDecodeError, TypeError):
budget = []
budget_map = {}
for b in budget:
key = (b.get("month") or "").replace("-", "_")
budget_map[key] = {
"rev": float(b.get("rev") or 0),
"gross": float(b.get("gross") or 0),
"payment": float(b.get("payment") or 0),
"cost": float(b.get("cost") or 0),
}
parsed_budgets.append((pf, budget_map))
data = []
for month in months:
key = month.replace("-", "_")
revenue = gross = payment = cost = sign = 0
for pf, budget_map in parsed_budgets:
if pf["status"] == "已签约" and (pf.get("sign_month") or "") == month:
sign += float(pf["sign_amount"] or 0)
b = budget_map.get(key)
if b:
revenue += b["rev"]
gross += b["gross"]
payment += b["payment"]
cost += b["cost"]
data.append({
"month": month, "revenue": revenue,
"labor": 0, "expense": 0, "purchase": 0,
"gross": gross,
"sign": sign, "payment": payment, "cost": cost,
})
return data