mengchang 2 ヶ月 前
コミット
982968658f
1 ファイル変更218 行追加1 行削除
  1. 218 1
      backend/routers/estimate.py

+ 218 - 1
backend/routers/estimate.py

@@ -537,7 +537,7 @@ async def simulate_calc(
         }
     )
 
-@router.post("/simulate", response_class=HTMLResponse)
+@router.post("/simulate7", response_class=HTMLResponse)
 async def simulate_calc(
     request: Request,
     machine_id: int = Form(...),
@@ -731,7 +731,224 @@ async def simulate_calc(
         }
     )
 
+@router.post("/simulate", response_class=HTMLResponse)
+async def simulate_calc(
+    request: Request,
+    machine_id: int = Form(...),
+    db: Session = Depends(get_db)
+):
+    form = await request.form()
+
+    option_ids = []
+    for k, v in form.multi_items():
+        if k.startswith("option_"):
+            option_ids.append(int(v))
+
+    # ---------------- 基准价 ----------------
+
+    base_row = db.execute(text("""
+        SELECT base_price
+        FROM machine_base_price
+        WHERE machine_id=:mid
+        LIMIT 1
+    """), {"mid": machine_id}).fetchone()
+
+    base_price = float(base_row.base_price) if base_row else 1000.0
+
+    # ---------------- 读取选项扣减规则 ----------------
+
+    rows = []
+    if option_ids:
+        rows = db.execute(text("""
+            SELECT
+                p.option_id,
+                p.factor,
+                p.absolute_deduct,
+                p.group_code,
+                p.severity_level,
+                p.sub_weight,
+                p.is_special,
+                p.repair_level,
+                g.cap_ratio,
+                g.group_weight
+            FROM price_option_factor p
+            LEFT JOIN price_damage_group g
+              ON p.group_code = g.group_code
+            WHERE p.option_id IN :ids
+        """), {"ids": tuple(option_ids)}).fetchall()
+
+    # ---------------- 覆盖关系 ----------------
+
+    overrides = db.execute(text("""
+        SELECT
+            trigger_group_code,
+            target_group_code,
+            override_type,
+            override_value
+        FROM price_group_override
+    """)).fetchall()
+
+    override_map = defaultdict(list)
+    for o in overrides:
+        override_map[o.trigger_group_code].append(o)
+
+    # ---------------- 分组 ----------------
+
+    groups = defaultdict(list)
+    specials = []
+    repairs = []
+
+    for r in rows:
+        if r.repair_level and r.repair_level > 0:
+            repairs.append(r)
+            continue
+
+        if r.is_special:
+            specials.append(r)
+            continue
+
+        groups[r.group_code].append(r)
+
+    # ---------------- 覆盖处理 ----------------
+
+    hit_groups = set(groups.keys())
+
+    skip_groups = set()
+    weight_override = {}
+
+    for g in hit_groups:
+        for o in override_map.get(g, []):
+            if o.override_type == "skip":
+                skip_groups.add(o.target_group_code)
+            elif o.override_type == "weight":
+                weight_override[o.target_group_code] = float(o.override_value)
+
+    # ---------------- 分组扣减 ----------------
+
+    total_ratio = 0.0
+    detail_rows = []
+
+    for group_code, items in groups.items():
+
+        if group_code in skip_groups:
+            continue
+
+        items = sorted(
+            items,
+            key=lambda x: (
+                float(x.factor or 0),
+                x.severity_level or 0
+            ),
+            reverse=True
+        )
+
+        main = items[0]
+
+        group_deduct = float(main.factor or 0)
+
+        for sub in items[1:]:
+            group_deduct += float(sub.factor or 0) * float(sub.sub_weight or 0.3)
 
+        cap_ratio = float(main.cap_ratio or 1.0)
+
+        cap = float(main.factor or 0) * cap_ratio
+
+        group_deduct = min(group_deduct, cap)
+
+        group_weight = float(main.group_weight or 1.0)
+        if group_code in weight_override:
+            group_weight = weight_override[group_code]
+
+        final_group_deduct = group_deduct * group_weight
+
+        total_ratio += final_group_deduct
+
+        detail_rows.append({
+            "option_name": f"--{group_code} 组扣减",
+            "factor": round(final_group_deduct, 4)
+        })
+
+        for it in items:
+            detail_rows.append({
+                "option_name": it.option_id,
+                "factor": it.factor
+            })
+
+    # ---------------- special 处理 ----------------
+
+    special_ratio = 0.0
+    for s in specials:
+        special_ratio += float(s.factor or 0)
+
+        detail_rows.append({
+            "option_name": f"特殊项:{s.option_id}",
+            "factor": s.factor
+        })
+
+    # 特殊项封顶(示例:30%)
+    special_ratio = min(special_ratio, 0.30)
+    total_ratio += special_ratio
+
+    # ---------------- 维修分级 ----------------
+
+    repair_ratio = 0.0
+    max_repair_level = 0
+
+    for r in repairs:
+        repair_ratio += float(r.factor or 0)
+        max_repair_level = max(max_repair_level, r.repair_level or 0)
+
+        detail_rows.append({
+            "option_name": f"维修项:{r.option_id}",
+            "factor": r.factor
+        })
+
+    # 维修封顶(示例)
+    if max_repair_level >= 3:
+        repair_ratio = max(repair_ratio, 0.30)
+    elif max_repair_level == 2:
+        repair_ratio = min(repair_ratio, 0.20)
+    else:
+        repair_ratio = min(repair_ratio, 0.10)
+
+    total_ratio += repair_ratio
+
+    # ---------------- 最终价格 ----------------
+
+    total_ratio = min(total_ratio, 0.90)
+
+    final_price = base_price * (1 - total_ratio)
+
+    # ---------------- 返回 ----------------
+
+    row = db.execute(text("""
+        SELECT m.name, t.estimate_packet
+        FROM machine_temp t
+        JOIN t_machine m ON t.machine_id = m.machine_id
+        WHERE t.machine_id = :mid
+    """), {"mid": machine_id}).fetchone()
+
+    import json
+    tpl = json.loads(row.estimate_packet)
+
+    return templates.TemplateResponse(
+        "simulate_one.html",
+        {
+            "request": request,
+            "machine_id": machine_id,
+            "machine_name": row.name,
+            "tpl": tpl,
+            "result": {
+                "base_price": round(base_price, 2),
+                "final_price": round(final_price, 2),
+                "total_ratio": round(total_ratio, 4),
+                "details": detail_rows,
+                "selected": [str(i) for i in option_ids]
+            }
+        }
+    )
+
+    
 @router.post("/estimate2", response_class=HTMLResponse)
 def estimate_submit(
     request: Request,