"""商机产线数据能力:从数据爬取平台拉取招标数据(只读)。 所有函数签名统一 (sor, **params),返回 (ok, payload|错误信息)。 薄封装:查询条件透传给爬虫平台,本层不做二次加工(热点排序由爬虫平台算)。 """ from .opp_common import crawler_get async def search_tenders(sor, keyword="", days=7, source="", only_it=True, limit=50): """关键词检索招标信息。""" params = {"days": days, "limit": limit} if keyword: params["keyword"] = keyword if source: params["source"] = source params["only_it"] = "1" if only_it else "0" return await crawler_get(sor, "/api/tenders", params) async def tender_detail(sor, tender_id): """单条招标详情(含公告全文、来源 url)。""" return await crawler_get(sor, "/api/tenders/%s" % tender_id) async def daily_ai_tenders(sor, target_date=""): """每日 AI/Agent 软件类招标展示数据。""" params = {} if target_date: params["date"] = target_date return await crawler_get(sor, "/api/ai_tenders", params) async def hot_software(sor, days=30, top=10): """热点软件主题排名(数量+预算+热点地区+样例)。""" return await crawler_get(sor, "/api/hot_software", {"days": days, "top": top}) async def category_references(sor, category, days=30, limit=500, record_type="tender"): """某软件主题的全部分项(references 可查证)。 每条带公告原文/详情页 url + 采集来源 source_url,供界面逐条查证。 record_type: tender=招标(默认) / demand=众包需求发布(猪八戒/一品威客)。 """ return await crawler_get(sor, "/api/category_references", {"category": category, "days": days, "limit": limit, "record_type": record_type}) async def crawler_stats(sor): """爬虫平台采集健康度。""" return await crawler_get(sor, "/api/stats") async def demands(sor, keyword="", days=30, source="", limit=50): """众包平台软件需求发布列表(国内:猪八戒/一品威客;海外:Freelancer/PeoplePerHour)。 区别于招标:这些是甲方在众包平台直接发布的开发需求, 反映市场真实需求热度与预算分布。每条带详情页 url(可查证)。 """ params = {"days": days, "limit": limit} if keyword: params["keyword"] = keyword if source: params["source"] = source return await crawler_get(sor, "/api/demands", params) async def hot_demands(sor, days=30, top=10): """众包需求热点主题排名(需求侧热度)。""" return await crawler_get(sor, "/api/hot_demands", {"days": days, "top": top}) async def hot_overseas(sor, days=30, top=10): """海外众包软件需求热点主题排名(Freelancer/PeoplePerHour,英文主题)。""" return await crawler_get(sor, "/api/hot_overseas", {"days": days, "top": top}) async def overseas_references(sor, category, days=30, limit=500): """某海外需求主题的全部分项(references 可查证)。 每条带海外平台详情页 url,供界面逐条查证。 """ return await crawler_get(sor, "/api/overseas_references", {"category": category, "days": days, "limit": limit}) async def query_tenders(sor, keyword="", region="", buyer="", notice_type="", source="", min_budget="", max_budget="", min_ai="", days=90, group_by="", limit=50, only_it=True): """条件检索 + 汇总(用户口述条件 → LLM 映射为参数)。 过滤:keyword(标题多词AND)/region/buyer 模糊,notice_type/source 精确, min_budget/max_budget(万元)区间,min_ai 评分下限,days 回溯窗口,only_it。 汇总:group_by=region|notice_type|category|source|month → 分组统计+样例。 """ params: dict = {"days": days, "limit": limit} for k, v in (("keyword", keyword), ("region", region), ("buyer", buyer), ("notice_type", notice_type), ("source", source), ("min_budget", min_budget), ("max_budget", max_budget), ("min_ai", min_ai), ("group_by", group_by)): if v not in (None, ""): params[k] = v params["only_it"] = "1" if only_it else "0" return await crawler_get(sor, "/api/tenders_query", params)