order_adjust.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. """订单调整 API"""
  2. from typing import Optional, List
  3. from fastapi import APIRouter, Depends, HTTPException, Query
  4. from sqlalchemy.orm import Session
  5. from pydantic import BaseModel, Field
  6. from app.core.database import get_db
  7. from app.core.auth import get_current_user
  8. from app.core.response import success_response, error_response
  9. from app.crud.order_adjust import order_adjust_crud
  10. from app.schemas.order_adjust import AdjustType
  11. router = APIRouter(prefix="/api/web/admin/order-adjust", tags=["订单调整"])
  12. class CreateAdjustRequest(BaseModel):
  13. """创建调整请求"""
  14. order_id: str = Field(..., description="订单ID")
  15. order_item_id: Optional[str] = Field(None, description="订单项ID")
  16. adjust_type: str = Field(..., description="调整类型: reduce-减量, delete-删除, substitute-替代")
  17. before_quantity: float = Field(..., description="调整前数量")
  18. after_quantity: float = Field(..., description="调整后数量")
  19. before_amount: float = Field(..., description="调整前金额")
  20. after_amount: float = Field(..., description="调整后金额")
  21. reason: str = Field(..., min_length=1, max_length=500, description="调整原因")
  22. substitute_product_id: Optional[str] = Field(None, description="替代商品ID")
  23. class AdjustResponse(BaseModel):
  24. """调整记录响应"""
  25. id: str
  26. order_id: str
  27. order_item_id: Optional[str]
  28. adjust_type: str
  29. adjust_type_text: str
  30. before_quantity: float
  31. after_quantity: float
  32. before_amount: float
  33. after_amount: float
  34. difference_amount: float
  35. reason: str
  36. substitute_product_id: Optional[str]
  37. operator_id: str
  38. operator_name: str
  39. create_at: str
  40. create_name: str
  41. class Config:
  42. from_attributes = True
  43. def adjust_to_response(adjust) -> dict:
  44. """将调整记录转换为响应格式"""
  45. adjust_type_map = {
  46. "reduce": "减量",
  47. "delete": "删除",
  48. "substitute": "替代"
  49. }
  50. return {
  51. "id": adjust.id,
  52. "orderId": adjust.order_id,
  53. "orderItemId": adjust.order_item_id,
  54. "adjustType": adjust.adjust_type,
  55. "adjustTypeText": adjust_type_map.get(adjust.adjust_type, adjust.adjust_type),
  56. "beforeQuantity": float(adjust.before_quantity or 0),
  57. "afterQuantity": float(adjust.after_quantity or 0),
  58. "beforeAmount": float(adjust.before_amount or 0),
  59. "afterAmount": float(adjust.after_amount or 0),
  60. "differenceAmount": float(adjust.difference_amount or 0),
  61. "reason": adjust.reason,
  62. "substituteProductId": adjust.substitute_product_id,
  63. "operatorId": adjust.operator_id,
  64. "operatorName": adjust.operator_name,
  65. "createAt": str(adjust.create_at) if adjust.create_at else "",
  66. "createName": adjust.create_name or ""
  67. }
  68. @router.post("/create", response_model=dict)
  69. def create_adjust(
  70. request: CreateAdjustRequest,
  71. db: Session = Depends(get_db),
  72. current_user: dict = Depends(get_current_user)
  73. ):
  74. """创建订单调整记录"""
  75. try:
  76. # 验证调整类型
  77. valid_types = ["reduce", "delete", "substitute"]
  78. if request.adjust_type not in valid_types:
  79. return error_response(400, f"无效的调整类型,支持的类型: {', '.join(valid_types)}")
  80. # 验证调整数量
  81. if request.after_quantity < 0:
  82. return error_response(400, "调整后数量不能为负数")
  83. # 验证金额
  84. if request.after_amount < 0:
  85. return error_response(400, "调整后金额不能为负数")
  86. # 创建调整记录
  87. adjust = order_adjust_crud.create_adjust(
  88. db=db,
  89. order_id=request.order_id,
  90. order_item_id=request.order_item_id,
  91. adjust_type=request.adjust_type,
  92. before_quantity=request.before_quantity,
  93. after_quantity=request.after_quantity,
  94. before_amount=request.before_amount,
  95. after_amount=request.after_amount,
  96. reason=request.reason,
  97. operator_id=current_user.get("user_id", ""),
  98. operator_name=current_user.get("real_name", current_user.get("username", "")),
  99. substitute_product_id=request.substitute_product_id
  100. )
  101. return success_response({
  102. "id": adjust.id,
  103. "message": "订单调整记录已创建"
  104. })
  105. except Exception as e:
  106. return error_response(500, f"创建调整记录失败: {str(e)}")
  107. @router.get("/list/{order_id}", response_model=dict)
  108. def get_order_adjusts_by_order(
  109. order_id: str,
  110. db: Session = Depends(get_db),
  111. current_user: dict = Depends(get_current_user)
  112. ):
  113. """获取指定订单的所有调整记录"""
  114. try:
  115. adjusts = order_adjust_crud.get_by_order(db, order_id)
  116. return success_response({
  117. "list": [adjust_to_response(a) for a in adjusts],
  118. "total": len(adjusts),
  119. "totalAmount": order_adjust_crud.get_total_adjust_amount(db, order_id)
  120. })
  121. except Exception as e:
  122. return error_response(500, f"获取调整记录失败: {str(e)}")
  123. @router.post("/list", response_model=dict)
  124. def get_order_adjust_list(
  125. page: int = 1,
  126. pageSize: int = 10,
  127. keyword: Optional[str] = None,
  128. adjustType: Optional[str] = None,
  129. startDate: Optional[str] = None,
  130. endDate: Optional[str] = None,
  131. db: Session = Depends(get_db),
  132. current_user: dict = Depends(get_current_user)
  133. ):
  134. """获取订单调整列表(支持分页和搜索)"""
  135. try:
  136. adjusts, total = order_adjust_crud.get_list(
  137. db=db,
  138. page=page,
  139. pageSize=pageSize,
  140. keyword=keyword,
  141. adjust_type=adjustType,
  142. start_date=startDate,
  143. end_date=endDate
  144. )
  145. return success_response({
  146. "list": [adjust_to_response(a) for a in adjusts],
  147. "total": total,
  148. "page": page,
  149. "pageSize": pageSize
  150. })
  151. except Exception as e:
  152. return error_response(500, f"获取调整记录列表失败: {str(e)}")
  153. @router.get("/detail/{adjust_id}", response_model=dict)
  154. def get_adjust_detail(
  155. adjust_id: str,
  156. db: Session = Depends(get_db),
  157. current_user: dict = Depends(get_current_user)
  158. ):
  159. """获取调整记录详情"""
  160. try:
  161. adjust = order_adjust_crud.get(db, adjust_id)
  162. if not adjust:
  163. return error_response(404, "调整记录不存在")
  164. if adjust.is_del == 1:
  165. return error_response(404, "调整记录已删除")
  166. return success_response(adjust_to_response(adjust))
  167. except Exception as e:
  168. return error_response(500, f"获取调整记录详情失败: {str(e)}")
  169. @router.delete("/{adjust_id}", response_model=dict)
  170. def delete_adjust(
  171. adjust_id: str,
  172. db: Session = Depends(get_db),
  173. current_user: dict = Depends(get_current_user)
  174. ):
  175. """删除调整记录(仅管理员可操作)"""
  176. try:
  177. # 检查权限
  178. if current_user.get("role") != "admin":
  179. return error_response(403, "仅管理员可删除调整记录")
  180. adjust = order_adjust_crud.get(db, adjust_id)
  181. if not adjust:
  182. return error_response(404, "调整记录不存在")
  183. # 软删除
  184. order_adjust_crud.delete(db, adjust_id)
  185. # 创建历史记录
  186. order_adjust_crud.create_adjust_history(
  187. db=db,
  188. original_record=adjust,
  189. operator_id=current_user.get("user_id", ""),
  190. operator_name=current_user.get("real_name", current_user.get("username", ""))
  191. )
  192. return success_response({"message": "删除成功"})
  193. except Exception as e:
  194. return error_response(500, f"删除调整记录失败: {str(e)}")
  195. @router.get("/statistics/{order_id}", response_model=dict)
  196. def get_adjust_statistics(
  197. order_id: str,
  198. db: Session = Depends(get_db),
  199. current_user: dict = Depends(get_current_user)
  200. ):
  201. """获取订单调整统计"""
  202. try:
  203. adjusts = order_adjust_crud.get_by_order(db, order_id)
  204. # 按类型统计
  205. reduce_count = sum(1 for a in adjusts if a.adjust_type == "reduce")
  206. delete_count = sum(1 for a in adjusts if a.adjust_type == "delete")
  207. substitute_count = sum(1 for a in adjusts if a.adjust_type == "substitute")
  208. total_refund = sum(
  209. abs(a.difference_amount or 0) for a in adjusts
  210. if a.difference_amount and a.difference_amount > 0
  211. )
  212. return success_response({
  213. "totalCount": len(adjusts),
  214. "reduceCount": reduce_count,
  215. "deleteCount": delete_count,
  216. "substituteCount": substitute_count,
  217. "totalRefund": total_refund
  218. })
  219. except Exception as e:
  220. return error_response(500, f"获取统计信息失败: {str(e)}")