Files
shanxia2.0/backend/app/crud/sample_code.py
T
2026-05-17 08:36:37 +08:00

342 lines
9.8 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.
"""样本编码池 CRUD(biz_sample_code)"""
from __future__ import annotations
import math
from datetime import datetime
from typing import Any, Dict, List, Optional, Tuple
from sqlalchemy import and_, func, or_
from sqlalchemy.orm import Session
from app.core.utils import generate_uuid
from app.models.models import BizSampleCode
def _row_to_dict(r: BizSampleCode) -> Dict[str, Any]:
return {
"id": r.id,
"sampleCode": r.sample_code,
"sampleCodeType": r.sample_code_type or "",
"issued": int(r.issued or 0),
"issuedUserId": r.issued_user_id or "",
"issuedUserName": r.issued_user_name or "",
"issuedTime": r.issued_time.strftime("%Y-%m-%d %H:%M:%S") if r.issued_time else "",
"tenantNo": r.tenant_no,
"orderNum": r.order_num if r.order_num is not None else 0,
"remark": r.remark or "",
"createTime": r.create_time.strftime("%Y-%m-%d %H:%M:%S") if r.create_time else "",
"updateTime": r.update_time.strftime("%Y-%m-%d %H:%M:%S") if r.update_time else "",
}
def sample_code_exists(db: Session, code: str, tenant_no: str, exclude_id: Optional[str] = None) -> bool:
v = (code or "").strip()
if not v:
return False
q = db.query(BizSampleCode).filter(
and_(BizSampleCode.sample_code == v, BizSampleCode.tenant_no == tenant_no, BizSampleCode.del_flag == 0)
)
if exclude_id:
q = q.filter(BizSampleCode.id != exclude_id)
return q.first() is not None
def list_sample_codes(
db: Session,
*,
page: int = 1,
page_size: int = 10,
keyword: Optional[str] = None,
tenant_no: Optional[str] = None,
) -> Tuple[List[Dict[str, Any]], int]:
def _filtered():
q0 = db.query(BizSampleCode).filter(BizSampleCode.del_flag == 0)
if tenant_no:
q0 = q0.filter(BizSampleCode.tenant_no == tenant_no)
kw = (keyword or "").strip()
if kw:
like = f"%{kw}%"
q0 = q0.filter(or_(BizSampleCode.sample_code.like(like), BizSampleCode.remark.like(like)))
return q0
total = _filtered().order_by(None).with_entities(func.count(BizSampleCode.id)).scalar() or 0
page = max(1, page)
page_size = min(max(1, page_size), 100)
rows = (
_filtered()
.order_by(BizSampleCode.create_time.desc())
.offset((page - 1) * page_size)
.limit(page_size)
.all()
)
return [_row_to_dict(x) for x in rows], int(total)
def get_sample_code(db: Session, row_id: str, tenant_no: Optional[str] = None) -> Optional[BizSampleCode]:
q = db.query(BizSampleCode).filter(and_(BizSampleCode.id == row_id, BizSampleCode.del_flag == 0))
if tenant_no:
q = q.filter(BizSampleCode.tenant_no == tenant_no)
return q.first()
def create_sample_code(
db: Session,
*,
sample_code: str,
issued: int = 0,
tenant_no: str = "zkxw",
order_num: Optional[int] = None,
remark: Optional[str] = None,
user_id: Optional[str] = None,
) -> BizSampleCode:
sc = (sample_code or "").strip()
if not sc:
raise ValueError("样本编码不能为空")
if sample_code_exists(db, sc, tenant_no):
raise ValueError("样本编码已存在")
obj = BizSampleCode(
id=generate_uuid(),
sample_code=sc,
issued=1 if issued else 0,
tenant_no=tenant_no,
order_num=order_num if order_num is not None else 0,
remark=(remark or "").strip() or None,
create_by=user_id,
create_dept_id="",
create_time=datetime.now(),
update_by=user_id,
update_time=datetime.now(),
del_flag=0,
)
db.add(obj)
db.commit()
db.refresh(obj)
return obj
def update_sample_code(
db: Session,
row_id: str,
*,
sample_code: Optional[str] = None,
issued: Optional[int] = None,
order_num: Optional[int] = None,
remark: Optional[str] = None,
user_id: Optional[str] = None,
tenant_no: Optional[str] = None,
issued_user_id: Optional[str] = None,
issued_user_name: Optional[str] = None,
issued_time: Optional[str] = None,
) -> BizSampleCode:
obj = get_sample_code(db, row_id, tenant_no=tenant_no)
if not obj:
raise ValueError("记录不存在")
if sample_code is not None:
sc = sample_code.strip()
if not sc:
raise ValueError("样本编码不能为空")
if sc != obj.sample_code and sample_code_exists(db, sc, obj.tenant_no, exclude_id=row_id):
raise ValueError("样本编码已存在")
obj.sample_code = sc
if issued is not None:
obj.issued = 1 if issued else 0
if order_num is not None:
obj.order_num = order_num
if remark is not None:
obj.remark = remark.strip() or None
if issued_user_id is not None:
obj.issued_user_id = issued_user_id or None
if issued_user_name is not None:
obj.issued_user_name = issued_user_name or None
if issued_time is not None:
obj.issued_time = issued_time or None
obj.update_by = user_id
obj.update_time = datetime.now()
db.add(obj)
db.commit()
db.refresh(obj)
return obj
def soft_delete_sample_code(db: Session, row_id: str, tenant_no: Optional[str] = None) -> None:
obj = get_sample_code(db, row_id, tenant_no=tenant_no)
if not obj:
raise ValueError("记录不存在")
obj.del_flag = 1
obj.update_time = datetime.now()
db.add(obj)
db.commit()
def batch_generate_sample_codes(
db: Session,
*,
sample_code_type: str,
middle: str,
seq_start: int,
seq_end: int,
digit_count: int = 6,
tenant_no: str = "zkxw",
user_id: Optional[str] = None,
) -> Dict[str, Any]:
"""批量生成样本编码: 类型前缀 + 中间段 + 补零序号"""
t = (sample_code_type or "").strip()
if t not in ("zxbm", "qdbm"):
raise ValueError("样本编码类型无效,仅支持 zxbm/qdbm")
m = (middle or "").strip()
if not m:
raise ValueError("中间段不能为空")
if seq_start < 0 or seq_end < 0:
raise ValueError("序号不能为负数")
if seq_start > seq_end:
raise ValueError("开始序号不能大于结束序号")
if digit_count < 1:
raise ValueError("序号位数不能小于1")
count = seq_end - seq_start + 1
if count > 10000:
raise ValueError("单次批量生成不能超过10000条")
prefix_map = {"zxbm": "ZKZX", "qdbm": "ZKQD"}
prefix = prefix_map[t]
generated_codes = []
for i in range(seq_start, seq_end + 1):
seq_str = str(i).zfill(digit_count)
full_code = f"{prefix}{m}{seq_str}"
generated_codes.append(full_code)
existing = set(
row.sample_code
for row in db.query(BizSampleCode.sample_code)
.filter(
BizSampleCode.tenant_no == tenant_no,
BizSampleCode.del_flag == 0,
BizSampleCode.sample_code.in_(generated_codes),
)
.all()
)
now = datetime.now()
created = []
skipped = []
for code in generated_codes:
if code in existing:
skipped.append(code)
continue
obj = BizSampleCode(
id=generate_uuid(),
sample_code=code,
sample_code_type=t,
issued=0,
tenant_no=tenant_no,
order_num=0,
remark=None,
create_by=user_id,
create_dept_id="",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
created.append(obj)
if created:
db.bulk_save_objects(created)
db.commit()
return {
"created": len(created),
"skipped": len(skipped),
"skippedCodes": skipped,
}
def batch_issue_sample_codes(
db: Session,
*,
ids: List[str],
issued_user_id: str,
issued_user_name: Optional[str] = None,
tenant_no: Optional[str] = None,
user_id: Optional[str] = None,
) -> Dict[str, Any]:
"""批量下发样本编码"""
if not ids:
raise ValueError("请选择要下发的样本编码")
uid = (issued_user_id or "").strip()
if not uid:
raise ValueError("请选择下发用户")
now = datetime.now()
issued_count = 0
already_issued = []
for row_id in ids:
obj = get_sample_code(db, row_id, tenant_no=tenant_no)
if not obj:
continue
if obj.issued == 1:
already_issued.append(obj.sample_code)
continue
obj.issued = 1
obj.issued_user_id = uid
obj.issued_user_name = (issued_user_name or "").strip() or None
obj.issued_time = now
obj.update_by = user_id
obj.update_time = now
db.add(obj)
issued_count += 1
if issued_count > 0:
db.commit()
return {
"issuedCount": issued_count,
"alreadyIssued": already_issued,
}
def batch_revoke_sample_codes(
db: Session,
*,
ids: List[str],
tenant_no: Optional[str] = None,
user_id: Optional[str] = None,
) -> Dict[str, Any]:
if not ids:
raise ValueError("请选择要收回的样本编码")
now = datetime.now()
revoked_count = 0
not_issued = []
for row_id in ids:
obj = get_sample_code(db, row_id, tenant_no=tenant_no)
if not obj:
continue
if obj.issued != 1:
not_issued.append(obj.sample_code)
continue
obj.issued = 0
obj.issued_user_id = None
obj.issued_user_name = None
obj.issued_time = None
obj.update_by = user_id
obj.update_time = now
db.add(obj)
revoked_count += 1
if revoked_count > 0:
db.commit()
return {
"revokedCount": revoked_count,
"notIssued": not_issued,
}
def total_pages(total: int, page_size: int) -> int:
if total <= 0:
return 0
return int(math.ceil(total / float(page_size)))