Compare commits

...
2 Commits
Author SHA1 Message Date
Hermes CI Fix d7e97978c6 fix: 写接口entity_id token优先(resolve_entity_for_request)+ 登录页用顶层entity_id 2026-08-11 11:32:30 +08:00
Hermes CI Fix 52ec2c0f36 fix: 登录页entity_id存储取login响应顶层字段 — user对象无entity_id导致cma_entity_id恒为1
- login响应结构: {token, entity_id, entity_name, entity_short_name, user:{...}}
- 原代码读 res.user?.entity_id ?? 1 → 登录博海(2)却存1 → 页面显式传参1与token绑定2不一致 → get_entity_id返回403
- 改为 res.entity_id / res.entity_short_name(与MainLayout切换器存储逻辑一致)
2026-08-11 11:32:27 +08:00
4 changed files with 21 additions and 18 deletions
+4 -4
View File
@@ -1,10 +1,10 @@
"""资金管理API — 资金缺口预测 + 收付款计划 + 预警 (资金管理智能体)""" """资金管理API — 资金缺口预测 + 收付款计划 + 预警 (资金管理智能体)"""
import logging import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta
from fastapi import APIRouter, HTTPException, Depends, Query from fastapi import APIRouter, HTTPException, Depends, Query, Request
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from app.database import get_db from app.database import get_db
from app.deps import get_entity_id from app.deps import get_entity_id, resolve_entity_for_request
from app.auth_middleware import require_role from app.auth_middleware import require_role
from app.models import CashPlan from app.models import CashPlan
from app.utils.cash_forecast_engine import ( from app.utils.cash_forecast_engine import (
@@ -119,7 +119,7 @@ def api_list_plans(
@router.post("/plans") @router.post("/plans")
def api_create_plan(data: dict, db: Session = Depends(get_db)): def api_create_plan(request: Request, data: dict, db: Session = Depends(get_db)):
"""新建收付款计划""" """新建收付款计划"""
plan_type = data.get("plan_type") plan_type = data.get("plan_type")
if plan_type not in ("receive", "pay"): if plan_type not in ("receive", "pay"):
@@ -136,7 +136,7 @@ def api_create_plan(data: dict, db: Session = Depends(get_db)):
raise HTTPException(400, "plan_date格式应为YYYY-MM-DD") raise HTTPException(400, "plan_date格式应为YYYY-MM-DD")
plan = CashPlan( plan = CashPlan(
entity_id=int(data.get("entity_id", 1)), entity_id=resolve_entity_for_request(request, int(data.get("entity_id", 1))),
plan_type=plan_type, plan_type=plan_type,
amount=amount, amount=amount,
plan_date=plan_date, plan_date=plan_date,
+6 -6
View File
@@ -1,6 +1,6 @@
"""预测模拟API — 管理会计OS""" """预测模拟API — 管理会计OS"""
import logging import logging
from fastapi import APIRouter, HTTPException, Depends from fastapi import APIRouter, HTTPException, Depends, Request
from app.utils.predict_engine import ( from app.utils.predict_engine import (
cvp_analysis, npv, irr, cvp_analysis, npv, irr,
sensitivity_analysis, scenario_analysis, sensitivity_analysis, scenario_analysis,
@@ -10,7 +10,7 @@ from app.utils.cash_forecast_engine import (
calculate_accuracy, generate_scenario_suggestion, calculate_accuracy, generate_scenario_suggestion,
) )
from app.database import get_db from app.database import get_db
from app.deps import get_entity_id from app.deps import get_entity_id, resolve_entity_for_request
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
logger = logging.getLogger("cma.predict") logger = logging.getLogger("cma.predict")
@@ -165,10 +165,10 @@ def api_cvp_detailed(data: dict):
@router.post("/cash-forecast") @router.post("/cash-forecast")
def api_cash_forecast(data: dict, db: Session = Depends(get_db)): def api_cash_forecast(request: Request, data: dict, db: Session = Depends(get_db)):
"""现金流预测 — 根据历史KPI推算未来30天现金流""" """现金流预测 — 根据历史KPI推算未来30天现金流"""
try: try:
entity_id = int(data.get("entity_id", 1)) entity_id = resolve_entity_for_request(request, int(data.get("entity_id", 1)))
days = int(data.get("days", 30)) days = int(data.get("days", 30))
current_cash = float(data["current_cash"]) if data.get("current_cash") else None current_cash = float(data["current_cash"]) if data.get("current_cash") else None
result = forecast_cash_flow(entity_id, db, days, current_cash) result = forecast_cash_flow(entity_id, db, days, current_cash)
@@ -628,10 +628,10 @@ def _get_dim_detail_indicators(dimension: str, entity: dict) -> list:
@router.post("/growth-quality") @router.post("/growth-quality")
def api_growth_quality(data: dict): def api_growth_quality(request: Request, data: dict):
"""增长质量诊断 — 五维度评分+综合评分+诊断结论""" """增长质量诊断 — 五维度评分+综合评分+诊断结论"""
try: try:
entity_id = data.get("entity_id") entity_id = resolve_entity_for_request(request, data.get("entity_id"))
ENTITY_DATA = { ENTITY_DATA = {
1: {"entity":"陕西酣客文化传媒","rebateRate":82.8,"trueGrossMargin":18.6,"cashRatio":0.6,"expenseGrowthRate":2.2,"revenueGrowthRate":1.0,"mgmtRatio":447}, 1: {"entity":"陕西酣客文化传媒","rebateRate":82.8,"trueGrossMargin":18.6,"cashRatio":0.6,"expenseGrowthRate":2.2,"revenueGrowthRate":1.0,"mgmtRatio":447},
2: {"entity":"陕西博海网络科技","rebateRate":0,"trueGrossMargin":13.1,"cashRatio":6.7,"expenseGrowthRate":0.8,"revenueGrowthRate":1.0,"mgmtRatio":1.4}, 2: {"entity":"陕西博海网络科技","rebateRate":0,"trueGrossMargin":13.1,"cashRatio":6.7,"expenseGrowthRate":0.8,"revenueGrowthRate":1.0,"mgmtRatio":1.4},
+9 -6
View File
@@ -7,11 +7,11 @@
import json import json
from datetime import datetime, date from datetime import datetime, date
from fastapi import APIRouter, Depends, HTTPException, Query from fastapi import APIRouter, Depends, HTTPException, Query, Request
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from app.database import get_db from app.database import get_db
from app.deps import get_entity_id from app.deps import get_entity_id, resolve_entity_for_request
from app.auth_middleware import require_auth, require_role from app.auth_middleware import require_auth, require_role
from app.models import TaxRecord, InvoiceCheck, SocialSecurity, ExpenseReimbursement from app.models import TaxRecord, InvoiceCheck, SocialSecurity, ExpenseReimbursement
@@ -159,6 +159,7 @@ def list_tax_records(
@router.post("/records") @router.post("/records")
def create_tax_record( def create_tax_record(
request: Request,
data: dict, data: dict,
db: Session = Depends(get_db), db: Session = Depends(get_db),
_=Depends(require_role("ceo", "finance")), _=Depends(require_role("ceo", "finance")),
@@ -171,7 +172,7 @@ def create_tax_record(
if tax_type not in TAX_TYPE_LABELS: if tax_type not in TAX_TYPE_LABELS:
raise HTTPException(400, f"无效税种: {tax_type},可选 vat/income/surtax") raise HTTPException(400, f"无效税种: {tax_type},可选 vat/income/surtax")
t = TaxRecord( t = TaxRecord(
entity_id=data.get("entity_id") or 1, entity_id=resolve_entity_for_request(request, data.get("entity_id") or 1),
period=period, period=period,
tax_type=tax_type, tax_type=tax_type,
tax_payable=data.get("tax_payable") or 0, tax_payable=data.get("tax_payable") or 0,
@@ -382,6 +383,7 @@ def list_invoices(
@router.post("/invoices") @router.post("/invoices")
def create_invoice( def create_invoice(
request: Request,
data: dict, data: dict,
db: Session = Depends(get_db), db: Session = Depends(get_db),
_=Depends(require_role("ceo", "finance")), _=Depends(require_role("ceo", "finance")),
@@ -393,7 +395,7 @@ def create_invoice(
if data.get("amount") is None: if data.get("amount") is None:
raise HTTPException(400, "缺少必要参数: amount") raise HTTPException(400, "缺少必要参数: amount")
inv = InvoiceCheck( inv = InvoiceCheck(
entity_id=data.get("entity_id") or 1, entity_id=resolve_entity_for_request(request, data.get("entity_id") or 1),
invoice_no=str(invoice_no).strip(), invoice_no=str(invoice_no).strip(),
amount=data.get("amount"), amount=data.get("amount"),
invoice_type=data.get("invoice_type") or "vat", invoice_type=data.get("invoice_type") or "vat",
@@ -577,6 +579,7 @@ def list_ss_records(
@router.post("/ss") @router.post("/ss")
def create_ss_record( def create_ss_record(
request: Request,
data: dict, data: dict,
db: Session = Depends(get_db), db: Session = Depends(get_db),
_=Depends(require_role("ceo", "finance")), _=Depends(require_role("ceo", "finance")),
@@ -586,9 +589,9 @@ def create_ss_record(
period = data.get("period") period = data.get("period")
if not employee or not period: if not employee or not period:
raise HTTPException(400, "缺少必要参数: employee, period") raise HTTPException(400, "缺少必要参数: employee, period")
all_records = db.query(SocialSecurity).filter(SocialSecurity.entity_id == (data.get("entity_id") or 1)).all() all_records = db.query(SocialSecurity).filter(SocialSecurity.entity_id == resolve_entity_for_request(request, data.get("entity_id") or 1)).all()
s = SocialSecurity( s = SocialSecurity(
entity_id=data.get("entity_id") or 1, entity_id=resolve_entity_for_request(request, data.get("entity_id") or 1),
employee=employee, employee=employee,
period=period, period=period,
base_amount=data.get("base_amount") or 0, base_amount=data.get("base_amount") or 0,
+2 -2
View File
@@ -60,8 +60,8 @@ async function handleLogin() {
const res: any = await authApi.login({ ...form, entity_id: Number(form.entity_id) }) const res: any = await authApi.login({ ...form, entity_id: Number(form.entity_id) })
localStorage.setItem('cma_token', res.token) localStorage.setItem('cma_token', res.token)
localStorage.setItem('cma_user', JSON.stringify(res.user)) localStorage.setItem('cma_user', JSON.stringify(res.user))
localStorage.setItem('cma_entity_id', String(res.user?.entity_id ?? 1)) localStorage.setItem('cma_entity_id', String(res.entity_id ?? res.user?.entity_id ?? 1))
localStorage.setItem('cma_entity_name', res.user?.entity_short_name || res.user?.entity_name || '') localStorage.setItem('cma_entity_name', res.entity_short_name || res.entity_name || '')
router.push('/dashboard') router.push('/dashboard')
} catch (e: any) { } catch (e: any) {
ElMessage.error(e?.response?.data?.detail || '登录失败') ElMessage.error(e?.response?.data?.detail || '登录失败')