product_parameter.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. """API - 商品参数模板管理"""
  2. from typing import Optional, Tuple, Dict, Any
  3. from fastapi import APIRouter, Depends, HTTPException, Query
  4. from sqlalchemy.orm import Session
  5. from app.core.database import get_db
  6. from app.core.auth import get_current_user
  7. from app.core.response import success_response, error_response
  8. from app.crud.product_parameter import product_parameter
  9. from app.schemas.product_parameter import (
  10. ProductParameterCreate,
  11. ProductParameterUpdate,
  12. ProductParameterResponse,
  13. ProductParameterListResponse,
  14. ProductParameterSimpleResponse
  15. )
  16. router = APIRouter(prefix="/web/admin/product-parameters", tags=["商品参数模板管理"])
  17. def require_admin(current_user: dict = Depends(get_current_user)):
  18. """验证运营人员权限"""
  19. if current_user.get("role") not in {"admin", "super_admin"}:
  20. raise HTTPException(status_code=403, detail="需要运营人员权限")
  21. return current_user
  22. def _resolve_payload_with_crypto_fallback(request: dict, current_user: dict) -> Tuple[Dict[str, Any], Optional[dict]]:
  23. """兼容明文/密文请求,返回业务 payload。失败时返回 error_response。"""
  24. req_dict = request if isinstance(request, dict) else {}
  25. # 若是密文信封,优先解密。
  26. if isinstance(req_dict, dict) and req_dict.get("enc") == 1 and req_dict.get("edata"):
  27. try:
  28. from app.core import api_crypto
  29. from app.core.config import settings
  30. ts = req_dict.get("ts")
  31. nonce = req_dict.get("nonce")
  32. edata = req_dict.get("edata")
  33. operator_user_id = current_user.get("user_id") if isinstance(current_user, dict) else None
  34. fixed_secret = getattr(settings, "API_ENCRYPT_FIXED_SECRET", "") or ""
  35. if not (fixed_secret and operator_user_id and ts is not None and nonce):
  36. return {}, error_response(400, "密文解析配置缺失")
  37. key = api_crypto.derive_key(
  38. fixed_secret=fixed_secret,
  39. user_id=str(operator_user_id),
  40. ts=int(ts),
  41. nonce=str(nonce),
  42. )
  43. plain = api_crypto.decrypt_json(str(edata), key)
  44. payload = plain.get("data", plain) if isinstance(plain, dict) else {}
  45. if isinstance(payload, dict):
  46. return payload, None
  47. return {}, error_response(400, "密文解密后数据格式错误")
  48. except Exception as exc:
  49. return {}, error_response(400, f"密文解密失败: {exc}")
  50. payload = req_dict.get("data", req_dict) if isinstance(req_dict, dict) else {}
  51. return (payload if isinstance(payload, dict) else {}), None
  52. @router.get("", response_model=dict)
  53. def list_parameters(
  54. page: int = Query(1, ge=1, description="页码"),
  55. pageSize: int = Query(10, ge=1, le=100, description="每页条数"),
  56. keyword: Optional[str] = Query(None, description="关键词搜索"),
  57. isActive: Optional[int] = Query(None, description="是否启用"),
  58. db: Session = Depends(get_db),
  59. current_user: dict = Depends(require_admin)
  60. ):
  61. """获取参数模板列表"""
  62. items, total = product_parameter.get_multi_paged(
  63. db,
  64. skip=(page - 1) * pageSize,
  65. limit=pageSize,
  66. keyword=keyword,
  67. is_active=isActive
  68. )
  69. return success_response({
  70. "list": [{
  71. "id": p.id,
  72. "name": p.name,
  73. "code": p.code,
  74. "paramType": p.param_type,
  75. "options": p.options,
  76. "placeholder": p.placeholder,
  77. "isRequired": p.is_required,
  78. "isActive": p.is_active,
  79. "sortOrder": p.sort_order,
  80. "remark": p.remark,
  81. "createAt": str(p.create_at) if p.create_at else None
  82. } for p in items],
  83. "total": total,
  84. "page": page,
  85. "pageSize": pageSize
  86. })
  87. @router.get("/simple", response_model=dict)
  88. def list_parameters_simple(
  89. db: Session = Depends(get_db),
  90. current_user: dict = Depends(require_admin)
  91. ):
  92. """获取参数模板简洁列表(用于下拉选择)"""
  93. items = product_parameter.get_active_all(db)
  94. return success_response({
  95. "list": [{
  96. "id": p.id,
  97. "name": p.name,
  98. "code": p.code,
  99. "paramType": p.param_type,
  100. "options": p.options
  101. } for p in items],
  102. "total": len(items)
  103. })
  104. @router.get("/{parameter_id}", response_model=dict)
  105. def get_parameter(
  106. parameter_id: str,
  107. db: Session = Depends(get_db),
  108. current_user: dict = Depends(require_admin)
  109. ):
  110. """获取参数详情"""
  111. param = product_parameter.get(db, parameter_id)
  112. if not param:
  113. return error_response(404, "参数不存在")
  114. return success_response({
  115. "id": param.id,
  116. "name": param.name,
  117. "code": param.code,
  118. "paramType": param.param_type,
  119. "options": param.options,
  120. "placeholder": param.placeholder,
  121. "isRequired": param.is_required,
  122. "isActive": param.is_active,
  123. "sortOrder": param.sort_order,
  124. "remark": param.remark,
  125. "createAt": str(param.create_at) if param.create_at else None
  126. })
  127. @router.post("", response_model=dict)
  128. def create_parameter(
  129. request: dict,
  130. db: Session = Depends(get_db),
  131. current_user: dict = Depends(require_admin)
  132. ):
  133. """创建参数模板"""
  134. payload, req_err = _resolve_payload_with_crypto_fallback(request, current_user)
  135. if req_err is not None:
  136. return req_err
  137. try:
  138. create_data = ProductParameterCreate.model_validate(payload)
  139. except Exception as exc:
  140. return error_response(400, f"参数错误: {exc}")
  141. # 检查编码是否已存在
  142. if product_parameter.code_exists(db, create_data.code):
  143. return error_response(400, "参数编码已存在")
  144. creator = {"user_id": current_user.get("user_id"), "real_name": current_user.get("real_name")}
  145. param = product_parameter.create(db, create_data.model_dump(), creator)
  146. return success_response({
  147. "id": param.id,
  148. "name": param.name,
  149. "code": param.code
  150. })
  151. @router.put("/{parameter_id}", response_model=dict)
  152. def update_parameter(
  153. parameter_id: str,
  154. request: dict,
  155. db: Session = Depends(get_db),
  156. current_user: dict = Depends(require_admin)
  157. ):
  158. """更新参数模板"""
  159. payload, req_err = _resolve_payload_with_crypto_fallback(request, current_user)
  160. if req_err is not None:
  161. return req_err
  162. try:
  163. update_data = ProductParameterUpdate.model_validate(payload)
  164. except Exception as exc:
  165. return error_response(400, f"参数错误: {exc}")
  166. # 检查编码是否已存在(排除自身)
  167. if update_data.code:
  168. if product_parameter.code_exists(db, update_data.code, exclude_id=parameter_id):
  169. return error_response(400, "参数编码已存在")
  170. updater = {"user_id": current_user.get("user_id"), "real_name": current_user.get("real_name")}
  171. param = product_parameter.update(db, parameter_id, update_data.model_dump(exclude_unset=True), updater)
  172. if not param:
  173. return error_response(404, "参数不存在")
  174. return success_response({
  175. "id": param.id,
  176. "name": param.name,
  177. "code": param.code
  178. })
  179. @router.delete("/{parameter_id}", response_model=dict)
  180. def delete_parameter(
  181. parameter_id: str,
  182. db: Session = Depends(get_db),
  183. current_user: dict = Depends(require_admin)
  184. ):
  185. """删除参数模板"""
  186. result = product_parameter.delete(db, parameter_id)
  187. if not result:
  188. return error_response(404, "参数不存在")
  189. return success_response({"message": "删除成功"})