estimate.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. from fastapi import APIRouter, Request, Depends, Form
  2. from fastapi.responses import HTMLResponse
  3. from sqlalchemy.orm import Session
  4. from sqlalchemy import text
  5. from database import SessionLocal, redis_client
  6. from services.template_service import build_template
  7. from services.price_service import apply_adjust
  8. import json
  9. import uuid
  10. router = APIRouter(prefix="/estimate", tags=["estimate"])
  11. # ================= DB =================
  12. def get_db():
  13. db = SessionLocal()
  14. try:
  15. yield db
  16. finally:
  17. db.close()
  18. # ================= 页面 =================
  19. @router.get("/simulate", response_class=HTMLResponse)
  20. def simulate(machine_id: int, db: Session = Depends(get_db)):
  21. row = db.execute(text("""
  22. SELECT estimate_packet
  23. FROM machine_temp
  24. WHERE machine_id=:mid
  25. """), {"mid": machine_id}).fetchone()
  26. tpl = json.loads(row.estimate_packet)
  27. html = "<h2>估价模拟</h2>"
  28. html += '<form method="post">'
  29. for step in tpl["template"]:
  30. html += f"<h3>{step['stepName']}</h3>"
  31. for p in step["properties"]:
  32. html += f"<b>{p['name']}</b><br>"
  33. for v in p["values"]:
  34. html += f"""
  35. <label>
  36. <input type="checkbox" name="option_ids" value="{v['valueId']}">
  37. {v['valueText']}
  38. </label><br>
  39. """
  40. html += f"""
  41. <input type="hidden" name="machine_id" value="{machine_id}">
  42. <br>
  43. <button type="submit">开始估价</button>
  44. </form>
  45. """
  46. return html
  47. # ================= 提交估价 =================
  48. @router.post("/simulate", response_class=HTMLResponse)
  49. def simulate_calc(
  50. machine_id: int = Form(...),
  51. option_ids: list[str] = Form([]),
  52. db: Session = Depends(get_db)
  53. ):
  54. base_price = 1000.0
  55. factors = db.execute(text("""
  56. SELECT option_id,factor,absolute_deduct
  57. FROM price_option_factor
  58. WHERE option_id IN :ids
  59. """), {"ids": tuple(option_ids)}).fetchall()
  60. price = base_price
  61. for f in factors:
  62. if f.factor:
  63. price *= float(f.factor)
  64. if f.absolute_deduct:
  65. price -= float(f.absolute_deduct)
  66. # 机型调节
  67. adjust = db.execute(text("""
  68. SELECT factor FROM price_adjust_factor
  69. WHERE level='machine'
  70. AND ref_id=:mid
  71. """), {"mid": machine_id}).fetchone()
  72. if adjust:
  73. price *= float(adjust.factor)
  74. html = f"""
  75. <h2>估价结果</h2>
  76. 选择项:{",".join(option_ids)}<br>
  77. 估价:{round(price,2)} 元<br>
  78. <a href="/estimate/simulate?machine_id={machine_id}">返回</a>
  79. """
  80. return html
  81. @router.post("/estimate2", response_class=HTMLResponse)
  82. def estimate_submit(
  83. request: Request,
  84. machine_id: int = Form(...),
  85. capacity: str = Form(...),
  86. option_ids: list[int] = Form([]),
  87. db: Session = Depends(get_db)
  88. ):
  89. # ---------- 基础价格 ----------
  90. base_price = db.execute(
  91. text("""
  92. SELECT base_price
  93. FROM machine_base_price
  94. WHERE machine_id=:mid AND capacity=:cap
  95. """),
  96. {"mid": machine_id, "cap": capacity}
  97. ).scalar() or 0
  98. price = float(base_price)
  99. # ---------- 扣减项 ----------
  100. for oid in option_ids:
  101. r = db.execute(
  102. text("""
  103. SELECT factor, absolute_deduct
  104. FROM price_option_factor
  105. WHERE option_id=:oid
  106. """),
  107. {"oid": oid}
  108. ).fetchone()
  109. if r:
  110. price = price * float(r.factor) - float(r.absolute_deduct)
  111. # ---------- 调节系数 ----------
  112. brand_id = db.execute(
  113. text("""
  114. SELECT brand_id
  115. FROM t_machine
  116. WHERE machine_id=:mid
  117. """),
  118. {"mid": machine_id}
  119. ).scalar()
  120. price = apply_adjust(db, machine_id, brand_id, price)
  121. price = round(max(price, 0), 2)
  122. # ---------- 估价版本 ----------
  123. version_no = str(uuid.uuid4())[:8]
  124. db.execute(
  125. text("""
  126. INSERT INTO estimate_record
  127. (machine_id, capacity, option_ids, final_price, version_no)
  128. VALUES (:m, :c, :o, :p, :v)
  129. """),
  130. {
  131. "m": machine_id,
  132. "c": capacity,
  133. "o": ",".join(map(str, option_ids)),
  134. "p": price,
  135. "v": version_no
  136. }
  137. )
  138. db.commit()
  139. # ---------- 模板(Redis 缓存) ----------
  140. cache_key = f"template:{machine_id}"
  141. cached = redis_client.get(cache_key) if redis_client else None
  142. if cached:
  143. template = json.loads(cached)
  144. else:
  145. rows = db.execute(text("SELECT * FROM release_option")).fetchall()
  146. template = build_template(rows)
  147. if redis_client:
  148. redis_client.set(cache_key, json.dumps(template), ex=3600)
  149. # ---------- 重新加载页面 ----------
  150. machines = db.execute(
  151. text("""
  152. SELECT machine_id, name
  153. FROM t_machine
  154. ORDER BY brand_name, name
  155. """)
  156. ).fetchall()
  157. capacities = db.execute(
  158. text("""
  159. SELECT capacity
  160. FROM machine_base_price
  161. WHERE machine_id=:mid
  162. """),
  163. {"mid": machine_id}
  164. ).fetchall()
  165. return request.app.state.templates.TemplateResponse(
  166. "estimate.html",
  167. {
  168. "request": request,
  169. "machines": machines,
  170. "capacities": capacities,
  171. "template": template,
  172. "price": price,
  173. "version": version_no,
  174. "selected_machine": machine_id,
  175. "selected_capacity": capacity
  176. }
  177. )