admin.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. from fastapi import APIRouter, Request, Depends, Form
  2. from fastapi.responses import HTMLResponse, RedirectResponse
  3. from sqlalchemy import text # 新增这一行
  4. from sqlalchemy.orm import Session
  5. from database import SessionLocal
  6. import json
  7. import time
  8. from datetime import datetime
  9. from datetime import datetime
  10. from services.template_builder import build_estimate_template
  11. router = APIRouter(prefix="/admin", tags=["admin"])
  12. # ---------- DB ----------
  13. def get_db():
  14. db = SessionLocal()
  15. try:
  16. yield db
  17. finally:
  18. db.close()
  19. # ---------- 机型管理 ----------
  20. @router.get("/machines", response_class=HTMLResponse)
  21. def machine_page(db: Session = Depends(get_db)):
  22. categories = db.execute(text("""
  23. SELECT category_id, name FROM t_chx_category
  24. """)).fetchall()
  25. machines = db.execute(text("""
  26. SELECT brand_name, name, type_name
  27. FROM t_machine
  28. ORDER BY id DESC
  29. LIMIT 50
  30. """)).fetchall()
  31. html = """
  32. <h2>机型管理</h2>
  33. <form method="post" action="/admin/machines/add">
  34. 分类:
  35. <select name="type_id">
  36. """
  37. for c in categories:
  38. html += f"<option value='{c.category_id}'>{c.name}</option>"
  39. html += """
  40. </select><br><br>
  41. 品牌名称:<input name="brand_name"><br><br>
  42. 机型名称:<input name="machine_name"><br><br>
  43. <button type="submit">新增机型</button>
  44. </form>
  45. <hr>
  46. <ul>
  47. """
  48. for m in machines:
  49. html += f"<li>{m.type_name} | {m.brand_name} - {m.name}</li>"
  50. html += "</ul>"
  51. return html
  52. @router.post("/machines/add")
  53. def add_machine(
  54. type_id: int = Form(...),
  55. brand_name: str = Form(...),
  56. machine_name: str = Form(...),
  57. db: Session = Depends(get_db)
  58. ):
  59. category = db.execute(
  60. text("SELECT name FROM t_chx_category WHERE category_id=:id"),
  61. {"id": type_id}
  62. ).fetchone()
  63. machine_id = int(time.time() * 1000)
  64. brand_id = int(time.time() * 1000)
  65. db.execute(text("""
  66. INSERT INTO t_machine
  67. (
  68. code,
  69. type_id,
  70. type_name,
  71. brand_id,
  72. brand_name,
  73. machine_id,
  74. name,
  75. create_time
  76. )
  77. VALUES
  78. (
  79. 'mobile',
  80. :type_id,
  81. :type_name,
  82. :brand_id,
  83. :brand_name,
  84. :machine_id,
  85. :name,
  86. :time
  87. )
  88. """), {
  89. "type_id": type_id,
  90. "type_name": category.name,
  91. "brand_id": brand_id,
  92. "brand_name": brand_name,
  93. "machine_id": machine_id,
  94. "name": machine_name,
  95. "time": datetime.now()
  96. })
  97. db.commit()
  98. return RedirectResponse("/admin/machines", status_code=302)
  99. # ---------- release_option 管理 ----------
  100. @router.get("/options", response_class=HTMLResponse)
  101. def option_page(db: Session = Depends(get_db)):
  102. rows = db.execute(text("""
  103. SELECT step_name, option_key_name, option_name
  104. FROM release_option
  105. ORDER BY step_name, option_key_name
  106. """)).fetchall()
  107. # 组装树
  108. tree = {}
  109. for r in rows:
  110. step = r.step_name
  111. key = r.option_key_name
  112. opt = r.option_name
  113. tree.setdefault(step, {})
  114. if key:
  115. tree[step].setdefault(key, [])
  116. if opt:
  117. tree[step][key].append(opt)
  118. # 渲染 HTML
  119. html = "<h2>检测项管理</h2>"
  120. for step, keys in tree.items():
  121. html += f"<h3>📂 {step}</h3><ul>"
  122. for key, opts in keys.items():
  123. html += f"<li>📁 {key}<ul>"
  124. for o in opts:
  125. html += f"<li>✅ {o}</li>"
  126. html += "</ul></li>"
  127. html += "</ul>"
  128. # 表单
  129. html += """
  130. <hr>
  131. <h3>➕ 新增父级</h3>
  132. <form method="post" action="/admin/options/add">
  133. 父级名称:<input name="step_name">
  134. <button type="submit">添加</button>
  135. </form>
  136. <h3>➕ 新增子级</h3>
  137. <form method="post" action="/admin/options/add">
  138. 父级名称:<input name="step_name">
  139. 子级名称:<input name="option_key_name">
  140. <button type="submit">添加</button>
  141. </form>
  142. <h3>➕ 新增选项</h3>
  143. <form method="post" action="/admin/options/add">
  144. 父级名称:<input name="step_name">
  145. 子级名称:<input name="option_key_name">
  146. 选项名称:<input name="option_name">
  147. <button type="submit">添加</button>
  148. </form>
  149. """
  150. return html
  151. @router.post("/options/add")
  152. def add_option(
  153. step_name: str = Form(...),
  154. option_key_name: str = Form(""),
  155. option_name: str = Form(""),
  156. db: Session = Depends(get_db)
  157. ):
  158. db.execute(text("""
  159. INSERT INTO release_option
  160. (step_id, step_name, option_key_id, option_key_name, option_id, option_name)
  161. VALUES
  162. (
  163. 2,
  164. :step,
  165. UNIX_TIMESTAMP(),
  166. :key,
  167. UNIX_TIMESTAMP(),
  168. :opt
  169. )
  170. """), {
  171. "step": step_name,
  172. "key": option_key_name,
  173. "opt": option_name
  174. })
  175. db.commit()
  176. return RedirectResponse("/admin/options", status_code=302)
  177. # ---------- API ----------
  178. @router.get("/capacities/{machine_id}")
  179. def capacities(machine_id: int, db: Session = Depends(get_db)):
  180. return db.execute(
  181. "SELECT capacity FROM machine_base_price WHERE machine_id=:id",
  182. {"id": machine_id}
  183. ).fetchall()
  184. @router.post("/release_option/add")
  185. def add_option(
  186. step_id: int = Form(...),
  187. step_name: str = Form(...),
  188. option_key_name: str = Form(...),
  189. option_name: str = Form(...),
  190. db: Session = Depends(get_db)
  191. ):
  192. db.execute("""
  193. INSERT INTO release_option(step_id,step_name,option_key_id,option_key_name,option_id,option_name)
  194. VALUES (:sid,:sname, UNIX_TIMESTAMP(), :kname, UNIX_TIMESTAMP(), :oname)
  195. """, {
  196. "sid": step_id,
  197. "sname": step_name,
  198. "kname": option_key_name,
  199. "oname": option_name
  200. })
  201. db.commit()
  202. return {"ok": True}
  203. @router.get("/capacities/{machine_id}")
  204. def capacities(machine_id: int, db: Session = Depends(get_db)):
  205. return db.execute(
  206. "SELECT capacity FROM machine_base_price WHERE machine_id=:id",
  207. {"id": machine_id}
  208. ).fetchall()
  209. @router.get("/factors", response_class=HTMLResponse)
  210. def factors(request: Request, db: Session = Depends(get_db)):
  211. rows = db.execute(text("""
  212. SELECT ro.option_name, pf.option_id, pf.factor, pf.absolute_deduct
  213. FROM price_option_factor pf
  214. LEFT JOIN release_option ro ON pf.option_id = ro.option_id
  215. """)).fetchall()
  216. options = db.execute(text("""
  217. SELECT DISTINCT option_id, option_name FROM release_option
  218. """)).fetchall()
  219. return request.app.templates.TemplateResponse(
  220. "factors.html",
  221. {
  222. "request": request,
  223. "rows": rows,
  224. "options": options
  225. }
  226. )
  227. @router.post("/factors/save")
  228. def save_factor(
  229. option_id: int = Form(...),
  230. factor: float = Form(1.0),
  231. absolute_deduct: float = Form(0),
  232. db: Session = Depends(get_db)
  233. ):
  234. db.execute(text("""
  235. INSERT INTO price_option_factor(option_id,factor,absolute_deduct)
  236. VALUES (:oid,:f,:d)
  237. ON DUPLICATE KEY UPDATE
  238. factor=:f, absolute_deduct=:d
  239. """), {
  240. "oid": option_id,
  241. "f": factor,
  242. "d": absolute_deduct
  243. })
  244. db.commit()
  245. return RedirectResponse("/admin/factors", status_code=302)
  246. @router.get("/adjust", response_class=HTMLResponse)
  247. def adjust_page(request: Request, db: Session = Depends(get_db)):
  248. rows = db.execute(text("SELECT * FROM price_adjust_factor")).fetchall()
  249. return request.app.templates.TemplateResponse(
  250. "adjust.html",
  251. {"request": request, "rows": rows}
  252. )
  253. @router.post("/adjust/save")
  254. def save_adjust(
  255. level: str = Form(...),
  256. ref_id: int = Form(0),
  257. factor: float = Form(...),
  258. db: Session = Depends(get_db)
  259. ):
  260. db.execute(text("""
  261. INSERT INTO price_adjust_factor(level,ref_id,factor)
  262. VALUES (:l,:r,:f)
  263. """), {
  264. "l": level,
  265. "r": ref_id,
  266. "f": factor
  267. })
  268. db.commit()
  269. return RedirectResponse("/admin/adjust", status_code=302)
  270. @router.post("/machine-template/generate")
  271. def generate_machine_template(
  272. machine_id: int = Form(...),
  273. db: Session = Depends(get_db)
  274. ):
  275. # 1. release_option
  276. options = db.execute(text("""
  277. SELECT step_name, option_key_name, option_id, option_name
  278. FROM release_option
  279. ORDER BY step_id, option_key_id, option_id
  280. """)).fetchall()
  281. if not options:
  282. return {"error": "release_option 为空"}
  283. template = build_estimate_template(options)
  284. # 2. 写入 machine_temp
  285. db.execute(text("""
  286. INSERT INTO machine_temp
  287. (machine_id, temp_type, estimate_packet, create_time, update_time)
  288. VALUES (:mid, '00', :json, :now, :now)
  289. ON DUPLICATE KEY UPDATE
  290. estimate_packet = :json,
  291. update_time = :now
  292. """), {
  293. "mid": machine_id,
  294. "json": json.dumps(template, ensure_ascii=False),
  295. "now": datetime.now()
  296. })
  297. db.commit()
  298. return RedirectResponse("/admin/machine-templates", status_code=302)
  299. @router.get("/machine-templates", response_class=HTMLResponse)
  300. def machine_templates(db: Session = Depends(get_db)):
  301. rows = db.execute(text("""
  302. SELECT m.machine_id, m.name, m.brand_name,
  303. mt.id AS temp_id
  304. FROM t_machine m
  305. LEFT JOIN machine_temp mt
  306. ON m.machine_id = mt.machine_id
  307. AND mt.temp_type = '00'
  308. ORDER BY m.brand_name, m.name
  309. LIMIT 100
  310. """)).fetchall()
  311. html = "<h2>机型模板管理</h2><table border=1>"
  312. html += "<tr><th>品牌</th><th>机型</th><th>模板</th><th>操作</th></tr>"
  313. for r in rows:
  314. status = "已生成" if r.temp_id else "未生成"
  315. btn = "重新生成" if r.temp_id else "生成"
  316. html += f"""
  317. <tr>
  318. <td>{r.brand_name}</td>
  319. <td>{r.name}</td>
  320. <td>{status}</td>
  321. <td>
  322. <form method="post" action="/admin/machine-template/generate">
  323. <input type="hidden" name="machine_id" value="{r.machine_id}">
  324. <button type="submit">{btn}</button>
  325. </form>
  326. </td>
  327. </tr>
  328. """
  329. html += "</table>"
  330. return html
  331. @router.get("/categories", response_class=HTMLResponse)
  332. def category_page(db: Session = Depends(get_db)):
  333. rows = db.execute(text("SELECT * FROM t_chx_category")).fetchall()
  334. html = "<h2>分类管理</h2><ul>"
  335. for r in rows:
  336. html += f"<li>{r.name}</li>"
  337. html += "</ul>"
  338. html += """
  339. <h3>新增分类</h3>
  340. <form method="post">
  341. 名称:<input name="name"/>
  342. <button type="submit">添加</button>
  343. </form>
  344. """
  345. return html
  346. @router.post("/categories")
  347. def add_category(name: str = Form(...), db: Session = Depends(get_db)):
  348. db.execute(
  349. text("INSERT INTO t_chx_category(category_id,name) VALUES (UNIX_TIMESTAMP(),:n)"),
  350. {"n": name}
  351. )
  352. db.commit()
  353. return RedirectResponse("/admin/categories", 302)