from __future__ import annotations import json from datetime import datetime from typing import Any, Dict, List, Optional from sqlalchemy.orm import Session from app.crud.child_row_sync import sync_child_rows from app.models.qc_param import BizQcItemParam def _row_to_dict(r: BizQcItemParam) -> Dict[str, Any]: return { "id": r.id, "paramName": r.param_name, "paramCode": r.param_code, "paramUnit": r.param_unit or "", "paramType": r.param_type or "text", "options": r.options or "", "defaultValue": r.default_value or "", "requiredFlag": r.required_flag or "0", "orderNum": r.order_num if r.order_num is not None else 0, } def _parse_options_list(options_raw: Any) -> List[str]: if not options_raw: return [] if isinstance(options_raw, list): arr = options_raw else: try: arr = json.loads(str(options_raw)) except json.JSONDecodeError: raise ValueError("单选选项格式无效") if not isinstance(arr, list): raise ValueError("单选选项格式无效") cleaned = [str(x).strip() for x in arr if str(x).strip()] return cleaned def _validate_param(p: Dict[str, Any], index: int) -> None: label = (p.get("paramName") or "").strip() or f"第{index + 1}个参数" param_type = (p.get("paramType") or "text").strip() if param_type != "select": return opts = _parse_options_list(p.get("options")) if not opts: raise ValueError(f"「{label}」为单选类型,请至少配置一个选项") if len(opts) != len(set(opts)): raise ValueError(f"「{label}」的选项不能重复") def list_params_by_item( db: Session, item_source: str, item_id: str, tenant_no: str, *, user_info=None ) -> List[Dict[str, Any]]: q = db.query(BizQcItemParam).filter( BizQcItemParam.item_source == item_source, BizQcItemParam.item_id == item_id, BizQcItemParam.tenant_no == tenant_no, BizQcItemParam.del_flag == 0, ) if user_info: from app.core.data_scope import apply_data_scope q = apply_data_scope(q, BizQcItemParam, user_info, db, tenant_no) rows = q.order_by(BizQcItemParam.order_num.asc()).all() return [_row_to_dict(r) for r in rows] def save_params_for_item( db: Session, item_source: str, item_id: str, params: List[Dict[str, Any]], tenant_no: str, user_id: Optional[str] = None, ) -> List[Dict[str, Any]]: for idx, p in enumerate(params): _validate_param(p, idx) now = datetime.now() def build_row(row_id): return BizQcItemParam( id=row_id, item_source=item_source, item_id=item_id, param_name="", param_code="", param_type="text", required_flag="0", tenant_no=tenant_no, create_by=user_id, create_dept_id="", create_time=now, ) def apply_row(row, p, idx, _now): param_type = (p.get("paramType") or "text").strip() options_val = None default_val = None if param_type == "select": opts = _parse_options_list(p.get("options")) options_val = json.dumps(opts, ensure_ascii=False) default_val = opts[0] if opts else None row.param_name = p.get("paramName", "") row.param_code = p.get("paramCode", "") row.param_unit = p.get("paramUnit") or None row.param_type = param_type row.options = options_val row.default_value = default_val row.required_flag = p.get("requiredFlag", "0") row.order_num = p.get("orderNum", idx) sync_child_rows( db, BizQcItemParam, parent_attr="item_id", parent_id=item_id, tenant_no=tenant_no, items=params, user_id=user_id, now=now, build_row=build_row, apply_fields=apply_row, natural_key=lambda item: (item.get("paramCode") or "").strip(), row_natural_key=lambda row: (row.param_code or "").strip(), duplicate_message="参数编码重复", foreign_message="参数不属于当前检测项目", extra_filters={"item_source": item_source}, reuse_deleted=True, ) db.commit() return list_params_by_item(db, item_source, item_id, tenant_no)