test: 26项功能落地性复查(综合冒烟52项+23页巡检, 修复6处缺陷)
This commit is contained in:
@@ -24,9 +24,10 @@ class AiRenderBody(BaseModel):
|
||||
mode: str = Field(default="quick", pattern="^(quick|photo)$")
|
||||
style: str = Field(default="modern", pattern="^(modern|chinese|european|nordic)$")
|
||||
time: str = Field(default="day", pattern="^(day|dusk|night)$")
|
||||
# quick: indoor/outdoor/studio; photo: render/install/erase/indoor/villa/fence/blank
|
||||
# quick: indoor/outdoor/studio; photo: render/install/erase/indoor/villa/fence/blank/sunroom-*
|
||||
scene: str = "indoor"
|
||||
photo_b64: str | None = Field(default=None, description="照片模式参考图(base64, 不带 dataURL 前缀)")
|
||||
rect: dict | None = Field(default=None, description="照片模式框选窗洞 {x,y,w,h}(原始照片像素坐标)")
|
||||
|
||||
|
||||
async def _resolve_drawing(db, body: AiRenderBody) -> str | dict | None:
|
||||
@@ -68,7 +69,26 @@ async def generate(body: AiRenderBody, user: CurrentUser, db: DbSession):
|
||||
return fail("照片模式需要上传参考照片")
|
||||
try:
|
||||
info = describe_window(drawing)
|
||||
prompt = build_prompt(drawing, body.style, body.time, body.scene)
|
||||
# 照片模式: 把框选洞口位置转成提示词方位描述, 引导 AI 装窗位置
|
||||
rect_hint = ""
|
||||
if body.mode == "photo" and body.rect:
|
||||
try:
|
||||
r = body.rect
|
||||
# 由前端提供照片宽高与洞口坐标(像素) → 相对方位词
|
||||
ph = float(r.get("photo_h") or 0)
|
||||
pw = float(r.get("photo_w") or 0)
|
||||
if pw > 0 and ph > 0:
|
||||
cx = (float(r["x"]) + float(r["w"]) / 2) / pw
|
||||
cy = (float(r["y"]) + float(r["h"]) / 2) / ph
|
||||
horiz = "left" if cx < 0.37 else ("right" if cx > 0.63 else "center")
|
||||
vert = "upper" if cy < 0.37 else ("lower" if cy > 0.63 else "middle")
|
||||
area_pct = round((float(r["w"]) * float(r["h"])) / (pw * ph) * 100)
|
||||
rect_hint = (f"Install the window into the existing window opening located in the "
|
||||
f"{vert}-{horiz} part of the image (opening covers about {area_pct}% of the frame), "
|
||||
f"replacing whatever old window or opening is there.")
|
||||
except Exception:
|
||||
rect_hint = ""
|
||||
prompt = build_prompt(drawing, body.style, body.time, body.scene, rect_hint=rect_hint)
|
||||
size = pick_size(info["width_mm"], info["height_mm"])
|
||||
except Exception:
|
||||
return fail("图纸数据解析失败")
|
||||
@@ -80,10 +100,12 @@ async def generate(body: AiRenderBody, user: CurrentUser, db: DbSession):
|
||||
async with httpx.AsyncClient(timeout=timeout) as client:
|
||||
if body.mode == "photo":
|
||||
img_bytes = base64.b64decode(body.photo_b64.split(",")[-1])
|
||||
mime = "image/jpeg" if img_bytes[:2] == b"\xff\xd8" else "image/png"
|
||||
ext = "jpg" if mime == "image/jpeg" else "png"
|
||||
resp = await client.post(
|
||||
f"{base}/v1/images/edits", headers=headers,
|
||||
data={"model": settings.ai_model, "prompt": prompt, "size": size, "n": "1"},
|
||||
files={"image[]": ("photo.png", img_bytes, "image/png")})
|
||||
files={"image[]": (f"photo.{ext}", img_bytes, mime)})
|
||||
else:
|
||||
resp = await client.post(
|
||||
f"{base}/v1/images/generations", headers=headers,
|
||||
|
||||
@@ -11,7 +11,7 @@ from huamenchuang.models.extend import OrderFormat
|
||||
|
||||
router = APIRouter(prefix="/order-formats", tags=["打印模板"])
|
||||
|
||||
BLOCK_TYPES = ("title", "info", "image", "table", "sign")
|
||||
BLOCK_TYPES = ("title", "info", "image", "table", "sign", "barcode")
|
||||
TABLE_KINDS = ("process", "bom")
|
||||
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ async def recommend(body: RecommendBody, user: CurrentUser, db: DbSession):
|
||||
|
||||
target_ratio = body.width_mm / body.height_mm
|
||||
scored = []
|
||||
for t in templates[:80]: # 打分上限控制计算量(前 80 套按 id 序,已覆盖六大类)
|
||||
for t in templates: # 全量 254 套打分(重放开销可接受,避免分类偏倚)
|
||||
if not t.drawing_data:
|
||||
continue
|
||||
try:
|
||||
|
||||
@@ -137,20 +137,27 @@ async def pickout(body: PickoutBody, user: CurrentUser, db: DbSession):
|
||||
picked, shortage = [], []
|
||||
for prep in preparation:
|
||||
need = int(prep["bars"])
|
||||
# 同长度且数量足够的库存扣减
|
||||
# 同长度库存累计扣减(可跨多个批次)
|
||||
stmt = (select(WarehouseItem).where(
|
||||
WarehouseItem.company_id == user.company_id,
|
||||
WarehouseItem.material == "profile",
|
||||
WarehouseItem.length == prep["stock_length"],
|
||||
WarehouseItem.count >= need).order_by(WarehouseItem.count.desc()))
|
||||
item = (await db.execute(stmt)).scalars().first()
|
||||
if item is None:
|
||||
shortage.append(f"{prep['stock_label']} 缺 {need} 根")
|
||||
continue
|
||||
item.count = float(item.count) - need
|
||||
db.add(StockMove(item_id=item.id, direction=-1, count=need,
|
||||
note=f"领料-{body.note or '车间切割'}", user_id=user.id))
|
||||
picked.append({"item": item.name, "length": prep["stock_length"], "qty": need})
|
||||
WarehouseItem.count > 0).order_by(WarehouseItem.count.desc()))
|
||||
items = list((await db.execute(stmt)).scalars())
|
||||
remaining = need
|
||||
for item in items:
|
||||
take = min(int(float(item.count)), remaining)
|
||||
if take <= 0:
|
||||
continue
|
||||
item.count = float(item.count) - take
|
||||
db.add(StockMove(item_id=item.id, direction=-1, count=take,
|
||||
note=f"领料-{body.note or '车间切割'}", user_id=user.id))
|
||||
picked.append({"item": item.name, "length": prep["stock_length"], "qty": take})
|
||||
remaining -= take
|
||||
if remaining <= 0:
|
||||
break
|
||||
if remaining > 0:
|
||||
shortage.append(f"{prep['stock_label']} 缺 {remaining} 根")
|
||||
if shortage:
|
||||
return fail(";".join(shortage) + f"(已领 {len(picked)} 项未提交,请先补库或采购)")
|
||||
return ok(data={"picked": picked}, message=f"领料完成:{len(picked)} 项出库")
|
||||
|
||||
@@ -101,7 +101,8 @@ async def list_assignments(user: CurrentUser, db: DbSession, page: int = 1, page
|
||||
@router.get("/scan/{order_no}")
|
||||
async def scan_order(order_no: str, user: CurrentUser, db: DbSession):
|
||||
"""扫码跟踪(scan_tracking):按订单号取订单与进度(对齐环节扫码)。"""
|
||||
order = (await db.execute(select(Order).where(
|
||||
from sqlalchemy.orm import selectinload
|
||||
order = (await db.execute(select(Order).options(selectinload(Order.processes)).where(
|
||||
Order.company_id == user.company_id, Order.order_no == order_no))).scalar_one_or_none()
|
||||
if order is None:
|
||||
return fail(f"未找到订单 {order_no}")
|
||||
|
||||
Reference in New Issue
Block a user