Compare commits
3 Commits
134a3824d3
...
71c2ab2005
| Author | SHA1 | Date | |
|---|---|---|---|
| 71c2ab2005 | |||
| 76c3b4af13 | |||
| 0adccb60d2 |
@ -15,6 +15,7 @@ from fastapi import APIRouter, Depends, Query
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from backend.app.api.deps import get_current_user, require_permissions, require_roles
|
||||
from backend.app.core.cache import cache_delete_pattern, cache_get, cache_set
|
||||
from backend.app.core.exceptions import AppException
|
||||
from backend.app.core.error_codes import ErrorCode
|
||||
from backend.app.db import get_db_session
|
||||
@ -88,13 +89,19 @@ def get_pricing_rule(
|
||||
返回值:定价规则详情信息。
|
||||
权限要求:管理员(admin)或经理(manager)。
|
||||
"""
|
||||
cache_key = f"pricing:rule:{product_id}"
|
||||
cached = cache_get(cache_key)
|
||||
if cached is not None:
|
||||
return success_payload(cached)
|
||||
rule = session.query(ProductPricingRule).filter(
|
||||
ProductPricingRule.product_id == product_id,
|
||||
ProductPricingRule.deleted == 0,
|
||||
).first()
|
||||
if not rule:
|
||||
raise AppException(code=ErrorCode.NOT_FOUND, message="定价规则不存在", status_code=404)
|
||||
return success_payload(_rule_to_dict(rule))
|
||||
rule_data = _rule_to_dict(rule)
|
||||
cache_set(cache_key, rule_data, ttl=300)
|
||||
return success_payload(rule_data)
|
||||
|
||||
|
||||
@router.post("/pricing-rules")
|
||||
@ -132,6 +139,7 @@ def create_pricing_rule(
|
||||
)
|
||||
session.add(rule)
|
||||
session.commit()
|
||||
cache_delete_pattern("pricing:*")
|
||||
session.refresh(rule)
|
||||
return success_payload(_rule_to_dict(rule))
|
||||
|
||||
@ -161,6 +169,7 @@ def update_pricing_rule(
|
||||
for k, v in data.items():
|
||||
setattr(rule, k, v)
|
||||
session.commit()
|
||||
cache_delete_pattern("pricing:*")
|
||||
session.refresh(rule)
|
||||
return success_payload(_rule_to_dict(rule))
|
||||
|
||||
@ -187,6 +196,7 @@ def delete_pricing_rule(
|
||||
raise AppException(code=ErrorCode.NOT_FOUND, message="定价规则不存在", status_code=404)
|
||||
rule.deleted = 1
|
||||
session.commit()
|
||||
cache_delete_pattern("pricing:*")
|
||||
return success_payload({"deleted": True})
|
||||
|
||||
|
||||
@ -230,6 +240,7 @@ def create_supplier_cost(
|
||||
cost = SupplierProductCost(**payload.model_dump())
|
||||
session.add(cost)
|
||||
session.commit()
|
||||
cache_delete_pattern("pricing:*")
|
||||
session.refresh(cost)
|
||||
return success_payload(_cost_to_dict(cost))
|
||||
|
||||
@ -258,6 +269,7 @@ def update_supplier_cost(
|
||||
for k, v in data.items():
|
||||
setattr(cost, k, v)
|
||||
session.commit()
|
||||
cache_delete_pattern("pricing:*")
|
||||
session.refresh(cost)
|
||||
return success_payload(_cost_to_dict(cost))
|
||||
|
||||
@ -283,6 +295,7 @@ def delete_supplier_cost(
|
||||
raise AppException(code=ErrorCode.NOT_FOUND, message="供应商成本不存在", status_code=404)
|
||||
cost.deleted = 1
|
||||
session.commit()
|
||||
cache_delete_pattern("pricing:*")
|
||||
return success_payload({"deleted": True})
|
||||
|
||||
|
||||
@ -326,6 +339,7 @@ def create_price_tier(
|
||||
tier = ProductPriceTier(**payload.model_dump())
|
||||
session.add(tier)
|
||||
session.commit()
|
||||
cache_delete_pattern("pricing:*")
|
||||
session.refresh(tier)
|
||||
return success_payload(_tier_to_dict(tier))
|
||||
|
||||
@ -354,6 +368,7 @@ def update_price_tier(
|
||||
for k, v in data.items():
|
||||
setattr(tier, k, v)
|
||||
session.commit()
|
||||
cache_delete_pattern("pricing:*")
|
||||
session.refresh(tier)
|
||||
return success_payload(_tier_to_dict(tier))
|
||||
|
||||
@ -379,6 +394,7 @@ def delete_price_tier(
|
||||
raise AppException(code=ErrorCode.NOT_FOUND, message="价格层级不存在", status_code=404)
|
||||
tier.deleted = 1
|
||||
session.commit()
|
||||
cache_delete_pattern("pricing:*")
|
||||
return success_payload({"deleted": True})
|
||||
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from backend.app.api.deps import require_permissions, require_roles
|
||||
from backend.app.db import get_db_session
|
||||
from backend.app.core.cache import cache_delete_pattern, cache_get, cache_set
|
||||
from backend.app.core.error_codes import ErrorCode
|
||||
from backend.app.core.exceptions import AppException
|
||||
from backend.app.schemas.common import success_payload
|
||||
@ -44,15 +45,21 @@ def salesman_dashboard(
|
||||
|
||||
权限要求:salesman、admin 角色,需 order:list 权限
|
||||
"""
|
||||
orders = order_service.list_orders(session, {"salesman_id": current_user.get("user_id")})
|
||||
reminders = reminder_service.list_reminders(session, {"receiver_user_id": current_user.get("user_id")})
|
||||
return success_payload(
|
||||
{
|
||||
"orders": orders[:20],
|
||||
"reminders": reminders.get("list", []) if isinstance(reminders, dict) else [],
|
||||
"summary": {
|
||||
"order_count": len(orders),
|
||||
"reminder_count": len(reminders.get("list", [])) if isinstance(reminders, dict) else 0,
|
||||
},
|
||||
}
|
||||
)
|
||||
user_id = current_user.get("user_id")
|
||||
cache_key = f"dashboard:{user_id}"
|
||||
cached = cache_get(cache_key)
|
||||
if cached is not None:
|
||||
return success_payload(cached)
|
||||
|
||||
orders = order_service.list_orders(session, {"salesman_id": user_id})
|
||||
reminders = reminder_service.list_reminders(session, {"receiver_user_id": user_id})
|
||||
result = {
|
||||
"orders": orders[:20],
|
||||
"reminders": reminders.get("list", []) if isinstance(reminders, dict) else [],
|
||||
"summary": {
|
||||
"order_count": len(orders),
|
||||
"reminder_count": len(reminders.get("list", [])) if isinstance(reminders, dict) else 0,
|
||||
},
|
||||
}
|
||||
cache_set(cache_key, result, ttl=30)
|
||||
return success_payload(result)
|
||||
|
||||
105
backend/app/core/cache.py
Normal file
105
backend/app/core/cache.py
Normal file
@ -0,0 +1,105 @@
|
||||
"""Redis 缓存工具模块。
|
||||
|
||||
提供 Redis 连接管理和缓存读写操作,所有操作在 Redis 不可用时静默降级。
|
||||
被 OrderService、CustomerService、pricing 路由等调用。
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
|
||||
import redis
|
||||
|
||||
from backend.app.core.config import get_settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_redis_client: redis.Redis | None = None
|
||||
|
||||
|
||||
def get_redis() -> redis.Redis | None:
|
||||
"""获取 Redis 连接单例,连接失败时返回 None。"""
|
||||
global _redis_client
|
||||
if _redis_client is not None:
|
||||
return _redis_client
|
||||
try:
|
||||
settings = get_settings()
|
||||
_redis_client = redis.Redis(
|
||||
host=settings.redis_host,
|
||||
port=settings.redis_port,
|
||||
db=settings.redis_db,
|
||||
password=settings.redis_password or None,
|
||||
decode_responses=True,
|
||||
socket_connect_timeout=2,
|
||||
socket_timeout=2,
|
||||
)
|
||||
_redis_client.ping()
|
||||
logger.info("Redis 连接成功: %s:%s/%s", settings.redis_host, settings.redis_port, settings.redis_db)
|
||||
return _redis_client
|
||||
except Exception as exc:
|
||||
logger.warning("Redis 连接失败,降级为无缓存模式: %s", exc)
|
||||
_redis_client = None
|
||||
return None
|
||||
|
||||
|
||||
def cache_get(key: str):
|
||||
"""读取缓存,返回反序列化后的 dict/list,未命中或异常返回 None。"""
|
||||
try:
|
||||
r = get_redis()
|
||||
if r is None:
|
||||
return None
|
||||
raw = r.get(key)
|
||||
if raw is None:
|
||||
return None
|
||||
return json.loads(raw)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def cache_set(key: str, data, ttl: int = 60) -> None:
|
||||
"""写入缓存,JSON 序列化后存储并设置 TTL(秒)。异常时静默跳过。"""
|
||||
try:
|
||||
r = get_redis()
|
||||
if r is None:
|
||||
return
|
||||
r.set(key, json.dumps(data, ensure_ascii=False, default=str), ex=ttl)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def cache_delete(key: str) -> None:
|
||||
"""精确删除单个缓存 key。"""
|
||||
try:
|
||||
r = get_redis()
|
||||
if r is not None:
|
||||
r.delete(key)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def cache_delete_pattern(pattern: str) -> None:
|
||||
"""按模式批量删除缓存(SCAN + DELETE),如 "order:list:*"。"""
|
||||
try:
|
||||
r = get_redis()
|
||||
if r is None:
|
||||
return
|
||||
cursor = 0
|
||||
while True:
|
||||
cursor, keys = r.scan(cursor=cursor, match=pattern, count=100)
|
||||
if keys:
|
||||
r.delete(*keys)
|
||||
if cursor == 0:
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def make_cache_key(prefix: str, **params) -> str:
|
||||
"""生成带参数哈希的缓存 key。
|
||||
|
||||
例: make_cache_key("order:list", user_id=1, status="draft")
|
||||
→ "order:list:a1b2c3d4"
|
||||
"""
|
||||
raw = json.dumps(params, sort_keys=True, ensure_ascii=False, default=str)
|
||||
suffix = hashlib.md5(raw.encode()).hexdigest()[:8]
|
||||
return f"{prefix}:{suffix}"
|
||||
@ -47,6 +47,10 @@ class Settings(BaseSettings):
|
||||
mysql_user: str = Field(default="order_user", alias="MYSQL_USER")
|
||||
mysql_password: str = Field(default="password", alias="MYSQL_PASSWORD")
|
||||
sql_echo: bool = Field(default=False, alias="SQL_ECHO")
|
||||
redis_host: str = Field(default="127.0.0.1", alias="REDIS_HOST")
|
||||
redis_port: int = Field(default=6379, alias="REDIS_PORT")
|
||||
redis_db: int = Field(default=0, alias="REDIS_DB")
|
||||
redis_password: str = Field(default="", alias="REDIS_PASSWORD")
|
||||
aliyun_oss_bucket: str = Field(default="bucket-name", alias="ALIYUN_OSS_BUCKET")
|
||||
aliyun_oss_endpoint: str = Field(default="oss-cn-hangzhou.aliyuncs.com", alias="ALIYUN_OSS_ENDPOINT")
|
||||
aliyun_oss_public_base_url: str = Field(default="https://cdn.example.com", alias="ALIYUN_OSS_PUBLIC_BASE_URL")
|
||||
|
||||
@ -11,6 +11,7 @@ from pathlib import Path
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from backend.app.core.cache import cache_delete_pattern, cache_get, cache_set, make_cache_key
|
||||
from backend.app.core.error_codes import ErrorCode
|
||||
from backend.app.core.exceptions import AppException
|
||||
from backend.app.repositories.customer_repository import CustomerRepository
|
||||
@ -44,9 +45,14 @@ class CustomerService:
|
||||
"""
|
||||
if session is None:
|
||||
raise AppException(code=ErrorCode.PARAM_ERROR, message="数据库会话未初始化", status_code=500)
|
||||
# 读缓存
|
||||
cache_key = make_cache_key("customer:list", **(filters or {}))
|
||||
cached = cache_get(cache_key)
|
||||
if cached is not None:
|
||||
return cached
|
||||
try:
|
||||
customers = self.repository.list_customers_by_filters(session, filters or {})
|
||||
return {
|
||||
result = {
|
||||
"total": len(customers),
|
||||
"page_no": 1,
|
||||
"page_size": 20,
|
||||
@ -67,6 +73,8 @@ class CustomerService:
|
||||
for item in customers
|
||||
],
|
||||
}
|
||||
cache_set(cache_key, result, ttl=60)
|
||||
return result
|
||||
except SQLAlchemyError as exc:
|
||||
raise AppException(code=ErrorCode.SYSTEM_ERROR, message="数据库操作失败", status_code=500) from exc
|
||||
|
||||
@ -136,6 +144,7 @@ class CustomerService:
|
||||
},
|
||||
)
|
||||
session.commit()
|
||||
cache_delete_pattern("customer:list:*")
|
||||
return {
|
||||
"customer_id": customer.id,
|
||||
"customer_name": customer.customer_name,
|
||||
@ -212,6 +221,7 @@ class CustomerService:
|
||||
},
|
||||
)
|
||||
session.commit()
|
||||
cache_delete_pattern("customer:list:*")
|
||||
return {"customer_id": customer.id, "customer_name": customer.customer_name, "mobile": customer.mobile}
|
||||
except AppException:
|
||||
session.rollback()
|
||||
@ -314,6 +324,7 @@ class CustomerService:
|
||||
},
|
||||
)
|
||||
session.commit()
|
||||
cache_delete_pattern("customer:list:*")
|
||||
return result
|
||||
except AppException:
|
||||
session.rollback()
|
||||
|
||||
@ -15,6 +15,7 @@ from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from backend.app.core.config import get_settings
|
||||
from backend.app.core.cache import cache_delete_pattern, cache_get, cache_set, make_cache_key
|
||||
from backend.app.core.error_codes import ErrorCode
|
||||
from backend.app.core.exceptions import AppException
|
||||
from backend.app.repositories.logistics_repository import LogisticsRepository
|
||||
@ -62,17 +63,23 @@ class LogisticsService:
|
||||
被调用路由: logistics.py - GET /logistics/tasks
|
||||
"""
|
||||
if session is not None:
|
||||
cache_key = make_cache_key("logistics:list", user_id=(current_user or {}).get("user_id"), **(filters or {}))
|
||||
cached = cache_get(cache_key)
|
||||
if cached is not None:
|
||||
return cached
|
||||
try:
|
||||
tasks = self.logistics_repository.list_tasks_by_filters(
|
||||
session,
|
||||
self.normalize_task_filters(filters, current_user),
|
||||
)
|
||||
return {
|
||||
result = {
|
||||
"total": len(tasks),
|
||||
"page_no": 1,
|
||||
"page_size": 20,
|
||||
"list": [self._map_task_summary(task, session) for task in tasks],
|
||||
}
|
||||
cache_set(cache_key, result, ttl=30)
|
||||
return result
|
||||
except SQLAlchemyError as exc:
|
||||
raise AppException(code=ErrorCode.INTERNAL_ERROR, message="数据库异常", status_code=500) from exc
|
||||
return {"total": 0, "page_no": 1, "page_size": 20, "list": []}
|
||||
@ -144,6 +151,7 @@ class LogisticsService:
|
||||
},
|
||||
)
|
||||
session.commit()
|
||||
cache_delete_pattern("logistics:*")
|
||||
return self._map_task_detail(task, session)
|
||||
except AppException:
|
||||
session.rollback()
|
||||
@ -258,6 +266,7 @@ class LogisticsService:
|
||||
},
|
||||
)
|
||||
session.commit()
|
||||
cache_delete_pattern("logistics:*")
|
||||
return {"task_id": task.id, "status": task.status, "order_id": task.order_id}
|
||||
except AppException:
|
||||
session.rollback()
|
||||
@ -342,6 +351,7 @@ class LogisticsService:
|
||||
},
|
||||
)
|
||||
session.commit()
|
||||
cache_delete_pattern("logistics:*")
|
||||
return {
|
||||
"task_id": task.id,
|
||||
"order_id": task.order_id,
|
||||
@ -554,6 +564,7 @@ class LogisticsService:
|
||||
},
|
||||
)
|
||||
session.commit()
|
||||
cache_delete_pattern("logistics:*")
|
||||
return {"order_id": order_id, "created": True}
|
||||
except ValueError as exc:
|
||||
session.rollback()
|
||||
@ -636,6 +647,7 @@ class LogisticsService:
|
||||
if target_status == "delivered":
|
||||
arrears_service.sync_order_arrears(session, order)
|
||||
session.commit()
|
||||
cache_delete_pattern("logistics:*")
|
||||
return {"task_id": task.id, "status": task.status, "order_id": task.order_id}
|
||||
except AppException:
|
||||
session.rollback()
|
||||
|
||||
@ -12,6 +12,7 @@ import json
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from backend.app.core.cache import cache_delete, cache_delete_pattern, cache_get, cache_set, make_cache_key
|
||||
from backend.app.core.error_codes import ErrorCode
|
||||
from backend.app.core.exceptions import AppException
|
||||
from backend.app.repositories.customer_repository import CustomerRepository
|
||||
@ -65,11 +66,16 @@ class OrderService:
|
||||
被调用路由: orders.py - GET /orders, salesman.py - GET /salesman/orders
|
||||
"""
|
||||
if session is not None:
|
||||
# 读缓存
|
||||
cache_key = make_cache_key("order:list", user_id=(current_user or {}).get("user_id"), **(filters or {}))
|
||||
cached = cache_get(cache_key)
|
||||
if cached is not None:
|
||||
return cached
|
||||
try:
|
||||
result = self.order_repository.list_orders_by_filters(session, filters or {})
|
||||
orders = result["list"]
|
||||
role_code = (current_user or {}).get("role_code")
|
||||
return [
|
||||
rows = [
|
||||
self._filter_order_row(
|
||||
{
|
||||
"order_id": item.id,
|
||||
@ -88,6 +94,9 @@ class OrderService:
|
||||
)
|
||||
for item in orders
|
||||
]
|
||||
# 写缓存
|
||||
cache_set(cache_key, rows, ttl=30)
|
||||
return rows
|
||||
except SQLAlchemyError as exc:
|
||||
raise AppException(code=ErrorCode.INTERNAL_ERROR, message="数据库异常", status_code=500) from exc
|
||||
return []
|
||||
@ -114,6 +123,11 @@ class OrderService:
|
||||
被调用路由: orders.py - GET /orders/{order_id}
|
||||
"""
|
||||
if session is not None:
|
||||
# 读缓存
|
||||
cache_key = f"order:detail:{order_id}"
|
||||
cached = cache_get(cache_key)
|
||||
if cached is not None:
|
||||
return self._filter_order_detail_by_role(cached, current_user)
|
||||
try:
|
||||
order = self.order_repository.get_order(session, order_id)
|
||||
if order is None:
|
||||
@ -213,6 +227,8 @@ class OrderService:
|
||||
"profit_alert_threshold": float(self._get_config_value(session, "profit_alert_threshold", "0")),
|
||||
"remark": order.remark,
|
||||
}
|
||||
# 写缓存(缓存完整结果,角色过滤在读取后执行)
|
||||
cache_set(cache_key, result, ttl=60)
|
||||
return self._filter_order_detail_by_role(result, current_user)
|
||||
except SQLAlchemyError as exc:
|
||||
raise AppException(code=ErrorCode.INTERNAL_ERROR, message="数据库异常", status_code=500) from exc
|
||||
@ -325,6 +341,7 @@ class OrderService:
|
||||
},
|
||||
)
|
||||
session.commit()
|
||||
self._invalidate_order_cache(order.id)
|
||||
return {
|
||||
"order_id": order.id,
|
||||
"order_no": order.order_no,
|
||||
@ -388,6 +405,7 @@ class OrderService:
|
||||
)
|
||||
self._notify_status_change(session, order, before_status, order.order_status, current_user.get("user_id") if current_user else None)
|
||||
session.commit()
|
||||
self._invalidate_order_cache(order.id)
|
||||
return {
|
||||
"order_id": order.id,
|
||||
"order_status": order.order_status,
|
||||
@ -487,6 +505,7 @@ class OrderService:
|
||||
)
|
||||
self._notify_status_change(session, order, previous_status, order.order_status, current_user.get("user_id") if current_user else None)
|
||||
session.commit()
|
||||
self._invalidate_order_cache(order.id)
|
||||
return {
|
||||
"order_id": order.id,
|
||||
"order_status": order.order_status,
|
||||
@ -567,6 +586,7 @@ class OrderService:
|
||||
)
|
||||
self._notify_status_change(session, order, previous_status, order.order_status, current_user.get("user_id") if current_user else None, {"approve_opinion": payload.get("approve_opinion")})
|
||||
session.commit()
|
||||
self._invalidate_order_cache(order.id)
|
||||
return {
|
||||
"order_id": order.id,
|
||||
"order_status": order.order_status,
|
||||
@ -655,6 +675,7 @@ class OrderService:
|
||||
)
|
||||
self._notify_status_change(session, order, previous_status, order.order_status, current_user.get("user_id") if current_user else None)
|
||||
session.commit()
|
||||
self._invalidate_order_cache(order.id)
|
||||
return {
|
||||
"order_id": order.id,
|
||||
"order_status": order.order_status,
|
||||
@ -782,6 +803,7 @@ class OrderService:
|
||||
},
|
||||
)
|
||||
session.commit()
|
||||
self._invalidate_order_cache(order.id)
|
||||
return {
|
||||
"order_id": order.id,
|
||||
"order_status": order.order_status,
|
||||
@ -864,6 +886,7 @@ class OrderService:
|
||||
)
|
||||
self._notify_status_change(session, order, before_status, order.order_status, current_user.get("user_id") if current_user else None)
|
||||
session.commit()
|
||||
self._invalidate_order_cache(order.id)
|
||||
return {
|
||||
"order_id": order.id,
|
||||
"order_status": order.order_status,
|
||||
@ -988,6 +1011,7 @@ class OrderService:
|
||||
},
|
||||
)
|
||||
session.commit()
|
||||
self._invalidate_order_cache(order.id)
|
||||
return {
|
||||
"order_id": order.id,
|
||||
"order_no": order.order_no,
|
||||
@ -1199,6 +1223,13 @@ class OrderService:
|
||||
return filtered
|
||||
return order
|
||||
|
||||
def _invalidate_order_cache(self, order_id: int | None = None) -> None:
|
||||
"""清除订单相关缓存。"""
|
||||
if order_id:
|
||||
cache_delete(f"order:detail:{order_id}")
|
||||
cache_delete_pattern("order:list:*")
|
||||
cache_delete_pattern("dashboard:*")
|
||||
|
||||
def _filter_order_row(self, row: dict, role_code: str | None) -> dict:
|
||||
"""根据用户角色过滤订单列表行中的敏感字段。
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from backend.app.core.cache import cache_delete_pattern, cache_get, cache_set, make_cache_key
|
||||
from backend.app.core.error_codes import ErrorCode
|
||||
from backend.app.core.exceptions import AppException
|
||||
from backend.app.repositories.product_repository import ProductRepository
|
||||
@ -97,6 +98,7 @@ class ProductService:
|
||||
},
|
||||
)
|
||||
session.commit()
|
||||
cache_delete_pattern("product:*")
|
||||
return {
|
||||
"category_id": category.id,
|
||||
"category_name": category.category_name,
|
||||
@ -140,6 +142,7 @@ class ProductService:
|
||||
raise AppException(code=ErrorCode.DUPLICATE, message="分类编码已存在", status_code=400)
|
||||
self.repository.update_category(session, category, payload)
|
||||
session.commit()
|
||||
cache_delete_pattern("product:*")
|
||||
return {"category_id": category_id, "updated": True}
|
||||
except AppException:
|
||||
session.rollback()
|
||||
@ -210,16 +213,22 @@ class ProductService:
|
||||
"""
|
||||
if session is None:
|
||||
raise AppException(code=ErrorCode.PARAM_ERROR, message="数据库会话未初始化", status_code=500)
|
||||
cache_key = make_cache_key("product:list", **(filters or {}))
|
||||
cached = cache_get(cache_key)
|
||||
if cached is not None:
|
||||
return cached
|
||||
try:
|
||||
products = self.repository.list_products(session, filters or {})
|
||||
categories = {category.id: category for category in self.repository.list_categories(session, {})}
|
||||
grouped_list = self._group_products(products, categories)
|
||||
return {
|
||||
result = {
|
||||
"total": len(grouped_list),
|
||||
"page_no": 1,
|
||||
"page_size": len(grouped_list) or 20,
|
||||
"list": grouped_list,
|
||||
}
|
||||
cache_set(cache_key, result, ttl=120)
|
||||
return result
|
||||
except SQLAlchemyError as exc:
|
||||
raise AppException(code=ErrorCode.SYSTEM_ERROR, message="数据库操作失败", status_code=500) from exc
|
||||
|
||||
@ -258,6 +267,7 @@ class ProductService:
|
||||
product.remark = payload.get("remark", product.remark)
|
||||
session.add(product)
|
||||
session.commit()
|
||||
cache_delete_pattern("product:*")
|
||||
return {"product_id": product.id, "product_name": product.product_name, "updated": True}
|
||||
except AppException:
|
||||
session.rollback()
|
||||
@ -309,6 +319,7 @@ class ProductService:
|
||||
product.remark = payload.get("remark", product.remark)
|
||||
session.add(product)
|
||||
session.commit()
|
||||
cache_delete_pattern("product:*")
|
||||
return {"product_id": product.id, "product_name": product.product_name, "updated": True}
|
||||
except AppException:
|
||||
session.rollback()
|
||||
@ -343,6 +354,7 @@ class ProductService:
|
||||
session.add(sibling)
|
||||
session.flush()
|
||||
session.commit()
|
||||
cache_delete_pattern("product:*")
|
||||
return {"product_id": product.id, "product_name": product.product_name, "is_default": True}
|
||||
except AppException:
|
||||
session.rollback()
|
||||
@ -425,6 +437,7 @@ class ProductService:
|
||||
},
|
||||
)
|
||||
session.commit()
|
||||
cache_delete_pattern("product:*")
|
||||
return {
|
||||
"product_name": payload["product_name"],
|
||||
"product_id": created_specs[0].id if created_specs else None,
|
||||
@ -465,6 +478,10 @@ class ProductService:
|
||||
"""
|
||||
if session is None:
|
||||
raise AppException(code=ErrorCode.PARAM_ERROR, message="数据库会话未初始化", status_code=500)
|
||||
cache_key = f"product:detail:{product_id}"
|
||||
cached = cache_get(cache_key)
|
||||
if cached is not None:
|
||||
return cached
|
||||
try:
|
||||
product = self.repository.get_product(session, product_id)
|
||||
if product is None:
|
||||
@ -473,6 +490,7 @@ class ProductService:
|
||||
categories = {category.id: category for category in self.repository.list_categories(session, {})}
|
||||
grouped = self._group_products(products, categories)
|
||||
if grouped:
|
||||
cache_set(cache_key, grouped[0], ttl=120)
|
||||
return grouped[0]
|
||||
raise AppException(code=ErrorCode.NOT_FOUND, message="产品不存在", status_code=404)
|
||||
except AppException:
|
||||
|
||||
@ -14,6 +14,7 @@ from datetime import datetime, timedelta
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from backend.app.core.cache import cache_delete_pattern, cache_get, cache_set, make_cache_key
|
||||
from backend.app.core.error_codes import ErrorCode
|
||||
from backend.app.core.exceptions import AppException
|
||||
from backend.app.repositories.config_repository import ConfigRepository
|
||||
@ -49,18 +50,24 @@ class ReminderService:
|
||||
if session is None:
|
||||
raise AppException(code=ErrorCode.INTERNAL_ERROR, message="数据库连接不可用", status_code=500)
|
||||
filters = filters or {}
|
||||
cache_key = make_cache_key("reminder:list", **filters)
|
||||
cached = cache_get(cache_key)
|
||||
if cached is not None:
|
||||
return cached
|
||||
try:
|
||||
reminders = self.repository.list_reminders(session, filters)
|
||||
page_no = filters.get("page_no", 1)
|
||||
page_size = filters.get("page_size", 20)
|
||||
start = max(page_no - 1, 0) * page_size
|
||||
page_list = reminders[start : start + page_size]
|
||||
return {
|
||||
result = {
|
||||
"total": len(reminders),
|
||||
"page_no": page_no,
|
||||
"page_size": page_size,
|
||||
"list": [self._map_reminder(item) for item in page_list],
|
||||
}
|
||||
cache_set(cache_key, result, ttl=30)
|
||||
return result
|
||||
except SQLAlchemyError as exc:
|
||||
raise AppException(code=ErrorCode.INTERNAL_ERROR, message="数据库异常", status_code=500) from exc
|
||||
|
||||
@ -101,6 +108,8 @@ class ReminderService:
|
||||
},
|
||||
)
|
||||
session.commit()
|
||||
cache_delete_pattern("reminder:*")
|
||||
cache_delete_pattern("dashboard:*")
|
||||
return {"reminder_id": reminder.id, "status": reminder.status}
|
||||
except AppException:
|
||||
session.rollback()
|
||||
@ -169,6 +178,8 @@ class ReminderService:
|
||||
created_count += 1
|
||||
|
||||
session.commit()
|
||||
cache_delete_pattern("reminder:*")
|
||||
cache_delete_pattern("dashboard:*")
|
||||
return {"checked": True, "created_count": created_count}
|
||||
except AppException:
|
||||
session.rollback()
|
||||
@ -238,6 +249,8 @@ class ReminderService:
|
||||
created_count += 1
|
||||
|
||||
session.commit()
|
||||
cache_delete_pattern("reminder:*")
|
||||
cache_delete_pattern("dashboard:*")
|
||||
return {"checked": True, "created_count": created_count}
|
||||
except AppException:
|
||||
session.rollback()
|
||||
@ -298,6 +311,8 @@ class ReminderService:
|
||||
created_count += 1
|
||||
|
||||
session.commit()
|
||||
cache_delete_pattern("reminder:*")
|
||||
cache_delete_pattern("dashboard:*")
|
||||
return {"checked": True, "created_count": created_count}
|
||||
except AppException:
|
||||
session.rollback()
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from backend.app.core.cache import cache_delete_pattern, cache_get, cache_set, make_cache_key
|
||||
from backend.app.core.error_codes import ErrorCode
|
||||
from backend.app.core.exceptions import AppException
|
||||
from backend.app.repositories.supplier_repository import SupplierRepository
|
||||
@ -39,9 +40,13 @@ class SupplierService:
|
||||
"""
|
||||
if session is None:
|
||||
raise AppException(code=ErrorCode.PARAM_ERROR, message="数据库会话未初始化", status_code=500)
|
||||
cache_key = make_cache_key("supplier:list", **(filters or {}))
|
||||
cached = cache_get(cache_key)
|
||||
if cached is not None:
|
||||
return cached
|
||||
try:
|
||||
suppliers = self.repository.list_suppliers(session, filters or {})
|
||||
return {
|
||||
result = {
|
||||
"total": len(suppliers),
|
||||
"page_no": 1,
|
||||
"page_size": len(suppliers) or 20,
|
||||
@ -60,6 +65,8 @@ class SupplierService:
|
||||
for supplier in suppliers
|
||||
],
|
||||
}
|
||||
cache_set(cache_key, result, ttl=120)
|
||||
return result
|
||||
except SQLAlchemyError as exc:
|
||||
raise AppException(code=ErrorCode.SYSTEM_ERROR, message="数据库操作失败", status_code=500) from exc
|
||||
|
||||
@ -108,6 +115,7 @@ class SupplierService:
|
||||
},
|
||||
)
|
||||
session.commit()
|
||||
cache_delete_pattern("supplier:*")
|
||||
return {
|
||||
"supplier_id": supplier.id,
|
||||
"supplier_name": supplier.supplier_name,
|
||||
@ -204,6 +212,7 @@ class SupplierService:
|
||||
},
|
||||
)
|
||||
session.commit()
|
||||
cache_delete_pattern("supplier:*")
|
||||
return {"supplier_id": supplier.id, "supplier_name": supplier.supplier_name}
|
||||
except AppException:
|
||||
session.rollback()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user