任务资格要求:最低信用分/要求勋章/要求认证(0061),can_accept 校验,发布端点接收

This commit is contained in:
Pine
2026-09-05 11:33:37 +08:00
parent e131bba599
commit 9b1df88bf0
5 changed files with 84 additions and 1 deletions
+3
View File
@@ -250,6 +250,9 @@ async def hall_publish_task(
"escrow_ratio": int(body.get("escrowRatio") or body.get("escrow_ratio") or 100),
"invoice_req": json.dumps(body.get("invoiceReq") or {}, ensure_ascii=False),
"milestone_json": json.dumps(body.get("milestones") or [], ensure_ascii=False),
"min_credit_score": int(body.get("minCreditScore") or body.get("min_credit_score") or 0),
"required_badges": json.dumps(body.get("requiredBadges") or body.get("required_badges") or [], ensure_ascii=False),
"required_certifications": json.dumps(body.get("requiredCertifications") or body.get("required_certifications") or [], ensure_ascii=False),
"status": status,
"published_at": utcnow_iso() if status == "published" else "",
"publisher_id": user.get("id"),
+3
View File
@@ -288,6 +288,9 @@ class Task(Base):
win_quota: Mapped[int] = mapped_column(Integer, default=0) # 竞标中标人数(默认1)
register_quota: Mapped[int] = mapped_column(Integer, default=0) # 报名模式人数上限(0=不限)(0035)
eligibility: Mapped[str] = mapped_column(Text, default="{}") # 接单资格条件(JSON 集)
min_credit_score: Mapped[int] = mapped_column(Integer, default=0) # 最低信用分要求(0=不限制)
required_badges: Mapped[str] = mapped_column(Text, default="[]") # 要求徽章 code 列表(JSON)
required_certifications: Mapped[str] = mapped_column(Text, default="[]") # 要求认证 code 列表(JSON)
settle_mode: Mapped[str] = mapped_column(String, default="operator_escrow") # 资金托管方式
# ── 任务系统 v2 扩展字段(0056)──
resource_support_json: Mapped[str] = mapped_column(Text, default="{}") # 提供的支持(算力/数据/场地/工具/对接/资金)
+11 -1
View File
@@ -1321,6 +1321,9 @@ class TaskRepository:
"bid_quota": t.bid_quota, "win_quota": t.win_quota,
"register_quota": t.register_quota,
"eligibility": t.eligibility, "settle_mode": t.settle_mode,
"min_credit_score": t.min_credit_score,
"required_badges": t.required_badges,
"required_certifications": t.required_certifications,
"created_at": t.created_at, "updated_at": t.updated_at,
}
@@ -1381,6 +1384,9 @@ class TaskRepository:
win_quota=fields.get("win_quota", 0),
register_quota=fields.get("register_quota", 0),
eligibility=fields.get("eligibility", "{}"),
min_credit_score=fields.get("min_credit_score", 0),
required_badges=fields.get("required_badges", "[]"),
required_certifications=fields.get("required_certifications", "[]"),
settle_mode=fields.get("settle_mode", "operator_escrow"),
resource_support_json=fields.get("resource_support_json", "{}"),
raw_materials_json=fields.get("raw_materials_json", "[]"),
@@ -1409,7 +1415,8 @@ class TaskRepository:
"publisher_role", "visibility", "c_visible", "park_public",
"assign_type", "assign_park_id", "assign_opc_id", "park_id",
"park_released", "bid_quota", "win_quota", "register_quota",
"eligibility", "settle_mode",
"eligibility", "min_credit_score", "required_badges", "required_certifications",
"settle_mode",
"resource_support_json", "raw_materials_json", "deliver_way",
"deliver_items", "accept_standard", "price_type", "pay_type",
"escrow_ratio", "invoice_req", "milestone_json",
@@ -1597,6 +1604,9 @@ class TaskRepository:
"bid_quota": t.bid_quota, "win_quota": t.win_quota,
"register_quota": t.register_quota,
"eligibility": t.eligibility, "settle_mode": t.settle_mode,
"min_credit_score": t.min_credit_score,
"required_badges": t.required_badges,
"required_certifications": t.required_certifications,
"resource_support_json": t.resource_support_json,
"raw_materials_json": t.raw_materials_json,
"deliver_way": t.deliver_way, "deliver_items": t.deliver_items,
+41
View File
@@ -98,10 +98,28 @@ class TaskService:
return {}
full = (await self.db.users.get_by_id(uid)) or {}
credit = await self.db.credit_scores.get(uid)
# 勋章(active
badges: list[str] = []
try:
for ub in await self.db.user_badges.list_active(uid):
if ub.get("badge_code"):
badges.append(ub["badge_code"])
except Exception: # noqa: BLE001
badges = []
# 认证(approved/active
certs: list[str] = []
try:
for c in await self.db.certifications.list_by_user(uid):
if c.get("status") in ("approved", "active") and c.get("cert_type"):
certs.append(c["cert_type"])
except Exception: # noqa: BLE001
certs = []
return {
**full,
"credit_level": (credit or {}).get("level", "L1"),
"credit_score": (credit or {}).get("total_score", 0),
"badges": badges,
"certifications": certs,
}
async def can_accept(self, task: dict | None, actor: dict | None) -> dict:
@@ -169,6 +187,29 @@ class TaskService:
if cond.get("deposit") and int(cond["deposit"] or 0) > 0:
reasons.append("需缴纳接单保证金")
# ── 独立资格字段(0061):最低信用分 / 要求勋章 / 要求认证 ──
min_score = int(task.get("min_credit_score") or 0)
if min_score > 0 and int(full.get("credit_score") or 0) < min_score:
reasons.append(f"信用分需 ≥ {min_score}(当前 {full.get('credit_score', 0)}")
try:
req_badges = json.loads(task.get("required_badges") or "[]")
except Exception: # noqa: BLE001
req_badges = []
if isinstance(req_badges, list) and req_badges:
have_badges = set(full.get("badges") or [])
missing_b = [b for b in req_badges if b not in have_badges]
if missing_b:
reasons.append(f"需持有徽章:{''.join(missing_b)}")
try:
req_certs = json.loads(task.get("required_certifications") or "[]")
except Exception: # noqa: BLE001
req_certs = []
if isinstance(req_certs, list) and req_certs:
have_certs = set(full.get("certifications") or [])
missing_c = [c for c in req_certs if c not in have_certs]
if missing_c:
reasons.append(f"需完成认证:{''.join(missing_c)}")
# 软性提示(不阻断)
if cond.get("price_range") and isinstance(cond["price_range"], dict):
pr = cond["price_range"]