Files
track/backend/app/core/config.py
duxingchen 551819e0e3 feat(scrap): Track 侧生产报废 —— 提交、回查、金额
料领到产线后在生产中报废,要在 MOM 里走报废流程并能统计金额。

- mom_scrap_client:Track **唯一**一处主动写 MOM 的通道。读仍走直连只读库
  (MOM 查询接口有权限与行级隔离),写必须走接口(跨库直写会绕过 MOM 的
  全部业务校验、权限与审批)。
- product_scrap_service:归属校验是关键 —— 可见范围是整台设备、不是「谁领的」,
  不能靠隐藏来防,必须在写入前确认这条 mom_line_id 就挂在这台设备上。
  申请人直接用当前登录人(Track 的 sub 就是 MOM sys_user.id),
  MOM 里显示的就是本人,不需要服务账号也不会串人。
- 幂等:track_ref 由前端在打开弹层时生成一次、重试复用;网络超时后重试
  不该在 MOM 里多报一张单。
- 状态与金额**实时回查 MOM**,不在本地存副本:报废没有回调,本地那份立刻
  就过期;且金额取决于执行时的实际扫码量(MOM 允许少扫),受理量 ≠ 执行量。
  ⚠️ 未执行时 total_loss 是 null 不是 0 —— 0 会让人以为「这东西不值钱」。
- MOM_INTERNAL_API_KEY 走环境变量且不给默认值:未配置时报废提交 503,
  而不是让一个写接口在生产上默默开着。
2026-09-23 15:18:06 +08:00

94 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""核心配置 — Pydantic Settings 自动从 .env 读取"""
import json
from pydantic import model_validator
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
# ---- 数据库 ----
DATABASE_URL: str = "postgresql+asyncpg://track:track_prod_2026@localhost:5433/track_production"
# ---- JWT ----
SECRET_KEY: str = "change-me-in-production"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 120 # Access Token: 2 小时
REFRESH_TOKEN_EXPIRE_DAYS: int = 7 # Refresh Token: 7 天
# ---- 调试 ----
DEBUG: bool = True
# ---- 应用元信息 ----
APP_VERSION: str = "1.0.0"
# ---- 日志 ----
LOG_LEVEL: str = "INFO"
LOG_JSON: bool = True # 生产保持 True便于采集本地调试可设 False 换可读格式
# ---- 错误追踪(可选,不装 sentry-sdk 则自动跳过)----
SENTRY_DSN: str | None = None
SENTRY_TRACES_SAMPLE_RATE: float = 0.0
# ---- CORS 跨域白名单JSON 数组字符串,直接从 .env 的 CORS_ORIGINS 读取) ----
CORS_ORIGINS: str = '["http://localhost:1420", "tauri://localhost"]'
# ---- MOM 仓储系统回调 WebhookTrack 作为接收方,验签用) ----
TRACK_WEBHOOK_KEY: str | None = None # MOM 回调 POST 时 Header X-API-Key 须等于此值
# ---- MOM 内部接口Track 作为**调用方**,发起生产报废) ----
# ⚠️ 这是 Track **唯一**一处主动写 MOM 的通道。
# 读数据一律继续走直连 MOM 库app/core/mom_database.py—— 不要因为有了
# 这个客户端就把「读」也搬过来MOM 的查询接口要 JWT + permission_required
# 且对非特权账号按 consumer_name 做行级隔离,服务账号只能拿到自己名下的数据。
# 而「写」必须走接口:跨库直写会绕过 MOM 的全部业务校验、权限与审批。
MOM_INTERNAL_API_URL: str = "http://inventory_api:8000"
# 请求头 X-API-Key 的值,须与 MOM 侧 config.MOM_INTERNAL_API_KEY 一致。
# ⚠️ 未配置 → 报废提交直接 503Fail-Closed**不静默降级**:报废是写操作,
# 静默失败会让用户以为报上去了,实际 MOM 里什么都没有。
MOM_INTERNAL_API_KEY: str | None = None
# ---- 组织隔离 ----
# 同一套代码部署给不同部门时,只需改这两个值(+ compose 里的项目名/容器名/端口)。
# 全仓库的部门过滤点只有四处登录、人员列表、物料选择器、MOM 出库单查询。
ORG_DEPARTMENT: str = "IRIS" # MOM sys_user.department 的取值
MATERIAL_CATEGORY_PREFIX: str = "IRIS/" # MOM material_base.category 的部门前缀
# ---- MOM 出库单的跨部门领用人例外 ----
# 出库单的公司隔离靠 material_base.category 前缀(见上)。
# 但这几个领用人MOM trans_outbound.consumer_name**纯姓名**,不带账号后缀)
# 经手的单据,即使物料分类不属于本部门,本实例也要能看见 —— 他们跨两个部门
# 领料,只按物料前缀过滤会把他们的单整批漏掉。
# ⚠️ 这是**放行**条件SQL 里是 OR与界面筛选AND只收窄方向相反
# 两者的集合运算必须分开写,混在一起就变成范围放大。
# ⚠️ 留空即关闭该例外,退化成纯前缀过滤。
EXTRA_VISIBLE_CONSUMERS: str = "依锐思,石利LICA"
@property
def EXTRA_VISIBLE_CONSUMERS_LIST(self) -> list[str]:
"""将逗号分隔的字符串解析为姓名 list去空白、丢空项"""
return [n.strip() for n in self.EXTRA_VISIBLE_CONSUMERS.split(",") if n.strip()]
@property
def CORS_ORIGINS_LIST(self) -> list[str]:
"""将 JSON 字符串解析为 Python list供 CORSMiddleware 使用"""
try:
return json.loads(self.CORS_ORIGINS)
except (json.JSONDecodeError, TypeError):
return ["http://localhost:1420", "tauri://localhost"]
@model_validator(mode="after")
def _validate_production_secret(self):
"""生产环境强制校验SECRET_KEY 禁止使用默认值"""
if not self.DEBUG and self.SECRET_KEY == "change-me-in-production":
raise ValueError(
"生产环境 (DEBUG=False) 禁止使用默认 SECRET_KEY。"
"请在 .env 中设置 SECRET_KEY 为至少 32 字符的随机值。"
"示例: python -c \"import secrets; print(secrets.token_urlsafe(32))\""
)
return self
class Config:
env_file = ".env"
extra = "ignore"
settings = Settings()