chore(config): 更新.gitignore文件以忽略数据库相关文件

添加了data/目录、SQLite数据库文件及相关临时文件到.gitignore中,
避免敏感数据和临时文件被提交到版本控制系统。
```
This commit is contained in:
2026-03-19 10:18:25 +08:00
parent bd46d8f907
commit d78dc655ee
10 changed files with 1367 additions and 1538 deletions

5
.gitignore vendored
View File

@@ -14,6 +14,11 @@ pnpm-debug.log*
# 配置文件(包含敏感信息) # 配置文件(包含敏感信息)
config.json config.json
data/
*.sqlite
*.sqlite-shm
*.sqlite-wal
*.migrated.bak
# 编辑器目录和文件 # 编辑器目录和文件
.vscode/ .vscode/

View File

@@ -1,582 +1,21 @@
{ {
"agent": { "agent": {
"baseUrl": "http://192.168.3.65:18777", "baseUrl": "http://192.168.3.65:18777",
"useBrowser": false,
"pollInterval": 3000, "pollInterval": 3000,
"timeout": 3600000 "timeout": 3600000
}, },
"tasks": [
{
"id": "task-1773213332763",
"city": "徐州市-建设工程-招标文件提前公示",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.zwb.xz.gov.cn/jyxx/003001/003001014/list.html\n1. 建设工程板块中的【招标文件提前公示】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": true,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773215122877",
"city": "徐州市-建设工程-招标公告/资审公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.zwb.xz.gov.cn/jyxx/003001/003001001/list.html\n1. 建设工程板块中的【招标公告/资审公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": true,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773215206067",
"city": "徐州市-交通工程-招标计划",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.zwb.xz.gov.cn/jyxx/003002/003002005/list.html\n1. 交通工程板块中的【招标计划】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": true,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773215248600",
"city": "徐州市-交通工程-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.zwb.xz.gov.cn/jyxx/003002/003002001/list.html\n1. 交通工程板块中的【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": true,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773215628722",
"city": "徐州市-水务工程-招标计划",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.zwb.xz.gov.cn/jyxx/003003/003003005/list.html\n1. 水务工程板块中的【招标计划】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": true,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773215674753",
"city": "徐州市-水务工程-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.zwb.xz.gov.cn/jyxx/003003/003003001/list.html\n1. 水务工程板块中的【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": true,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773217569819",
"city": "无锡市-建设工程-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzyjy.wuxi.gov.cn/wxsggzyjyzxzl/jyxx/jsgc/index.shtml\n1. 建设工程板块中的招标公告【工程类】【非工程类】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773217660649",
"city": "无锡市-水利工程-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzyjy.wuxi.gov.cn/wxsggzyjyzxzl/jyxx/slgc/index.shtml\n1. 水利工程板块中的【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773217743111",
"city": "无锡市-交通工程-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzyjy.wuxi.gov.cn/wxsggzyjyzxzl/jyxx/jtgc/index.shtml\n1. 交通工程板块中的【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773218603505",
"city": "南京市-房建市政-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://njggzy.nanjing.gov.cn/njweb/fjsz/buildService1.html\n1. 房建市政板块中的招标公告中的【工程类】、【服务类】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773218673384",
"city": "南京市-交通水务-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://njggzy.nanjing.gov.cn/njweb/jtsw/traffic.html\n1. 交通水务板块中的招标公告中的【交通】、【水务】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773220796068",
"city": "南通市-建设工程-招标计划",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzyjy.nantong.gov.cn/jyxx/003001/003001009/tradeInfo.html\n1. 建设工程板块中的【招标计划】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773220861223",
"city": "南通市-建设工程-招标公告/资审公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzyjy.nantong.gov.cn/jyxx/003001/003001001/tradeInfo.html\n1. 建设工程板块【招标公告/资审公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773222149540",
"city": "南通市-交通工程-招标公告/招标计划",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzyjy.nantong.gov.cn/jyxx/003005/003005001/tradeInfo.html\n1. 交通工程板块【招标公告/招标计划】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773222268523",
"city": "南通市-水利工程-招标公告/招标计划",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzyjy.nantong.gov.cn/jyxx/003006/003006001/tradeInfo.html\n1. 水利工程板块【招标公告/招标计划】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773222554722",
"city": "连云港市-建设工程-招标计划",
"prompt": "使用scrapling技能的fetch来爬取\nhttp://ggzy.lyg.gov.cn/lygweb/jyxx/001001/001001008/tradeInfo.html\n1. 建设工程板块【招标计划】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773222633782",
"city": "连云港市-建设工程-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttp://ggzy.lyg.gov.cn/lygweb/jyxx/001001/001001001/tradeInfo.html\n1. 建设工程板块【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773222806709",
"city": "连云港市-交通工程-招标计划",
"prompt": "使用scrapling技能的fetch来爬取\nhttp://ggzy.lyg.gov.cn/lygweb/jyxx/001002/001002004/tradeInfo.html\n1. 交通工程板块【招标计划】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773222852359",
"city": "连云港市-交通工程-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttp://ggzy.lyg.gov.cn/lygweb/jyxx/001002/001002001/tradeInfo.html\n1. 交通工程板块【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773222920195",
"city": "连云港市-水利工程-招标计划",
"prompt": "使用scrapling技能的fetch来爬取\nhttp://ggzy.lyg.gov.cn/lygweb/jyxx/001003/001003004/tradeInfo.html\n1. 水利工程板块【招标计划】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773222960973",
"city": "连云港市-水利工程-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttp://ggzy.lyg.gov.cn/lygweb/jyxx/001003/001003001/tradeInfo.html\n1. 水利工程板块【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773223258041",
"city": "淮安市-建设工程-招标计划",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.huaian.gov.cn/jyxx/001001/001001009/tradeInfo.html\n1. 建设工程板块【招标计划】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773223302642",
"city": "淮安市-建设工程-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.huaian.gov.cn/jyxx/001001/001001001/tradeInfo.html\n1. 建设工程板块【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773225146455",
"city": "淮安市-交通工程-招标计划",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.huaian.gov.cn/jyxx/001002/001002009/tradeInfo.html\n1. 交通工程板块【招标计划】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773225198650",
"city": "淮安市-交通工程-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.huaian.gov.cn/jyxx/001002/001002001/tradeInfo.html\n1. 交通工程板块【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773225266784",
"city": "淮安市-水利工程-招标计划",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.huaian.gov.cn/jyxx/001003/001003006/tradeInfo.html\n1. 水利工程板块【招标计划】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773225305718",
"city": "淮安市-水利工程-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.huaian.gov.cn/jyxx/001003/001003001/tradeInfo.html\n1. 水利工程板块【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773225383907",
"city": "淮安市-土地整治-招标计划",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.huaian.gov.cn/jyxx/001009/001009005/tradeInfo.html\n1. 土地整治板块【招标计划】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773225424486",
"city": "淮安市-土地整治-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.huaian.gov.cn/jyxx/001009/001009001/tradeInfo.html\n1. 土地整治板块【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773225496197",
"city": "淮安市-农田建设-招标计划",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.huaian.gov.cn/jyxx/001010/001010005/tradeInfo.html\n1. 农田建设板块【招标计划】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773225533513",
"city": "淮安市-农田建设-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.huaian.gov.cn/jyxx/001010/001010001/tradeInfo.html\n1. 农田建设板块【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773226587983",
"city": "扬州市-房建市政-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzyjyzx.yangzhou.gov.cn/jyxx/fjsz/zbgg/index.html\n1. 房建市政板块【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773227503951",
"city": "扬州市-交通工程-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzyjyzx.yangzhou.gov.cn/jyxx/jtgc/zbgg/index.html\n1. 交通工程板块【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773227565067",
"city": "扬州市-水利工程-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzyjyzx.yangzhou.gov.cn/jyxx/slgc/zbgg/index.html\n1. 水利工程板块【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773227623709",
"city": "扬州市-农业工程-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzyjyzx.yangzhou.gov.cn/jyxx/nygc/nyzbgg/index.html\n1. 农业工程板块【农业招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773227798306",
"city": "泰州市-建设工程-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttp://ggzy.taizhou.gov.cn/jyxx/001001/001001001/secondPagejyxx.html\n1. 建设工程板块【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773227859708",
"city": "泰州市-能源工程-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttp://ggzy.taizhou.gov.cn/jyxx/001013/001013001/secondPagejyxx.html\n1. 能源工程板块【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773227916651",
"city": "泰州市-交通工程-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttp://ggzy.taizhou.gov.cn/jyxx/001002/001002001/secondPagejyxx.html\n1. 交通工程板块【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773228017356",
"city": "泰州市-水利工程-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttp://ggzy.taizhou.gov.cn/jyxx/001003/001003001/secondPagejyxx.html\n1. 水利工程板块【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773228065067",
"city": "泰州市-农业工程-招标公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttp://ggzy.taizhou.gov.cn/jyxx/001012/001012001/secondPagejyxx.html\n1. 农业工程板块【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773228205941",
"city": "宿迁市-招标计划提前发布",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.xzspj.suqian.gov.cn/jyxx/001010/tradeInfo.html\n1. 招标计划提前发布板块项目信息\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773228260574",
"city": "宿迁市-建设工程-招标预公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.xzspj.suqian.gov.cn/jyxx/001001/001001010/tradeInfo.html\n1. 建设工程板块【招标预公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773228334419",
"city": "宿迁市-建设工程-招标公告/资审公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.xzspj.suqian.gov.cn/jyxx/001001/001001001/tradeInfo.html\n1. 建设工程板块【招标公告/资审公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773229198813",
"city": "宿迁市-交通工程-招标公告/资审公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.xzspj.suqian.gov.cn/jyxx/001002/001002001/tradeInfo.html\n1. 交通工程板块【招标公告/资审公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773229960773",
"city": "宿迁市-水利工程-招标公告/资审公告",
"prompt": "使用scrapling技能的fetch来爬取\nhttps://ggzy.xzspj.suqian.gov.cn/jyxx/001003/001003001/tradeInfo.html\n1. 水利工程板块【招标公告/资审公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n爬取思路\n 1. 先初步探索需要采集的内容\n 2. 根据实际情况,制定详细的采集计划,并将计划写入到plans.md文件中\n 3. 按照计划,委托给subagent去完成采集任务,不要自己去采集\n 4. 校验subagent的采集结果,复查计划进度\n 5. 计划完成后汇总结果,形成用户需要的数据",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773282112257",
"city": "盐城市-工程建设-招标计划",
"prompt": "爬取\nhttps://ycggzy.jszwfw.gov.cn/tradeInfor?secondId=19&secondCode=transactionInfo\n1. 工程建设板块中的【招标计划】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房建市政-招标文公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选工程建设 + 招标计划\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": true,
"mode": "glm-5"
},
{
"id": "task-1773282738792",
"city": "盐城市-工程建设-招标公告",
"prompt": "爬取\nhttps://ycggzy.jszwfw.gov.cn/tradeInfor?secondId=19&secondCode=transactionInfo\n1. 工程建设板块中的【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如工程建设-招标公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选工程建设 + 招标公告\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": true,
"mode": "glm-5"
},
{
"id": "task-1773282811404",
"city": "盐城市-交通工程-招标计划",
"prompt": "爬取https://ycggzy.jszwfw.gov.cn/tradeInfor?secondId=19&secondCode=transactionInfo\n1. 交通工程板块【招标计划】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如交通工程-招标计划\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选交通工程 + 招标计划\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": true,
"mode": "glm-5"
},
{
"id": "task-1773282843104",
"city": "盐城市-交通工程-招标公告",
"prompt": "爬取\nhttps://ycggzy.jszwfw.gov.cn/tradeInfor?secondId=19&secondCode=transactionInfo\n1. 交通工程板块【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如交通工程-招标公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选交通工程 + 招标公告\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": true,
"mode": "glm-5"
},
{
"id": "task-1773286430226",
"city": "盐城市-水利工程-招标计划",
"prompt": "爬取https://ycggzy.jszwfw.gov.cn/tradeInfor?secondId=19&secondCode=transactionInfo\n1. 水利工程板块【招标计划】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如水利工程-招标计划\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选水利工程-招标计划\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": true,
"mode": "glm-5"
},
{
"id": "task-1773286494770",
"city": "盐城市-水利工程-招标公告",
"prompt": "爬取https://ycggzy.jszwfw.gov.cn/tradeInfor?secondId=19&secondCode=transactionInfo\n1. 水利工程板块【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如水利工程-招标公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选水利工程 + 招标公告\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": true,
"mode": "glm-5"
},
{
"id": "task-1773286562021",
"city": "盐城市-农业农村-招标计划",
"prompt": "爬取https://ycggzy.jszwfw.gov.cn/tradeInfor?secondId=19&secondCode=transactionInfo\n1. 农业农村板块【招标计划】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如农业农村-招标计划\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选农业农村 + 招标计划\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": true,
"mode": "glm-5"
},
{
"id": "task-1773286599089",
"city": "盐城市-农业农村-招标公告",
"prompt": "爬取https://ycggzy.jszwfw.gov.cn/tradeInfor?secondId=19&secondCode=transactionInfo\n1. 农业农村板块【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如农业农村-招标公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选农业农村 + 招标公告\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": true,
"mode": "glm-5"
},
{
"id": "task-1773287068033",
"city": "苏州市-建设工程-招标预计划",
"prompt": "爬取http://ggzy.suzhou.gov.cn/jyxx/003001/tradeInfo.html\n1. 建设工程板块中的【招标预计划】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如建设工程-招标预计划\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选建设工程 + 招标预计划,筛选“发布时间”为“近三天”的\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": true,
"mode": "glm-5"
},
{
"id": "task-1773287206110",
"city": "苏州市-建设工程-招标公告/资审公告",
"prompt": "爬取http://ggzy.suzhou.gov.cn/jyxx/003001/tradeInfo.html\n1. 建设工程板块中的【招标公告/资审公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如建设工程-招标公告/资审公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选建设工程 + 招标公告/资审公告,筛选“发布时间”为“近三天”的\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": true,
"mode": "glm-5"
},
{
"id": "task-1773295605929",
"city": "苏州市-交通工程-招标预计划",
"prompt": "爬取http://ggzy.suzhou.gov.cn/jyxx/003001/tradeInfo.html\n1. 交通工程板块中的【招标预计划】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如交通工程-招标预计划\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选交通工程-招标预计划,筛选“发布时间”为“近三天”的\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": true,
"mode": "glm-5"
},
{
"id": "task-1773295651095",
"city": "苏州市-交通工程-招标公告",
"prompt": "爬取http://ggzy.suzhou.gov.cn/jyxx/003001/tradeInfo.html\n1. 交通工程板块中的【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如交通工程-招标公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选交通工程 + 招标公告,筛选“发布时间”为“近三天”的\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": true,
"mode": "glm-5"
},
{
"id": "task-1773295707567",
"city": "苏州市-水利工程-招标预计划",
"prompt": "爬取http://ggzy.suzhou.gov.cn/jyxx/003001/tradeInfo.html\n1. 水利工程板块中的【招标预计划】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如水利工程-招标预计划\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选水利工程-招标预计划, 筛选“发布时间”为“近三天”的\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": true,
"mode": "glm-5"
},
{
"id": "task-1773295748272",
"city": "苏州市-水利工程-招标公告",
"prompt": "爬取http://ggzy.suzhou.gov.cn/jyxx/003001/tradeInfo.html\n1. 水利工程板块中的【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如水利工程-招标公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选水利工程 + 招标公告,筛选“发布时间”为“近三天”\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": true,
"mode": "glm-5"
},
{
"id": "task-1773367789161",
"city": "省公共平台-建设工程-招标计划/招标计划变更公告",
"prompt": "爬取http://jsggzy.jszwfw.gov.cn/jyxx/tradeInfonew.html?type=jsgc\n1. 建设工程板块中的【招标计划/招标计划变更公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如建设工程-招标计划/招标计划变更公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选建设工程 + 招标计划/招标计划变更公告,筛选“发布时间”为“近三天”\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": true,
"mode": "glm-5"
},
{
"id": "task-1773368044054",
"city": "省公共平台-建设工程-招标公告/资审公告",
"prompt": "爬取http://jsggzy.jszwfw.gov.cn/jyxx/tradeInfonew.html?type=jsgc\n1. 建设工程板块中的【招标公告/资审公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如建设工程-招标公告/资审公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选建设工程 + 招标公告/资审公告,筛选“发布时间”为“近三天”\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": true,
"mode": "glm-5"
},
{
"id": "task-1773369115954",
"city": "省公共平台-交通工程-招标计划/招标计划变更公告",
"prompt": "爬取http://jsggzy.jszwfw.gov.cn/jyxx/tradeInfonew.html?type=jsgc\n1. 交通工程板块中的【招标计划/招标计划变更公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如交通工程-招标计划/招标计划变更公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选交通工程 + 招标计划/招标计划变更公告,筛选“发布时间”为“近三天”\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": true,
"mode": "glm-5"
},
{
"id": "task-1773369189083",
"city": "省公共平台-交通工程-招标公告",
"prompt": "爬取http://jsggzy.jszwfw.gov.cn/jyxx/tradeInfonew.html?type=jsgc\n1. 交通工程板块中的【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如交通工程-招标公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选交通工程 + 招标公告,筛选“发布时间”为“近三天”\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": true,
"mode": "glm-5"
},
{
"id": "task-1773369264817",
"city": "省公共平台-水利工程-招标计划/招标计划变更公告",
"prompt": "爬取http://jsggzy.jszwfw.gov.cn/jyxx/tradeInfonew.html?type=jsgc\n1. 水利工程板块中的【招标计划/招标计划变更公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如水利工程-招标计划/招标计划变更公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选水利工程 + 招标计划/招标计划变更公告,筛选“发布时间”为“近三天”\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": true,
"mode": "glm-5"
},
{
"id": "task-1773369324882",
"city": "省公共平台-水利工程-招标公告",
"prompt": "爬取http://jsggzy.jszwfw.gov.cn/jyxx/tradeInfonew.html?type=jsgc\n1. 水利工程板块中的【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如水利工程-招标公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选水利工程 + 招标公告,筛选“发布时间”为“近三天”\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": true,
"mode": "glm-5"
},
{
"id": "task-1773369457387",
"city": "省建设平台-房屋建筑施工-招标公告",
"prompt": "爬取http://www.jszb.com.cn/JSZB/YW_info/ZhaoBiaoGG/MoreInfo_ZBGG.aspx?Type=%B7%BF%CE%DD%BD%A8%D6%FE%CA%A9%B9%A4\n1. 房屋建筑施工板块中的【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如房屋建筑施工-招标公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选房屋建筑施工-招标公告\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773369549735",
"city": "省建设平台-市政工程施工-招标公告",
"prompt": "爬取http://www.jszb.com.cn/JSZB/YW_info/ZhaoBiaoGG/MoreInfo_ZBGG.aspx?Type=%CA%D0%D5%FE%B9%A4%B3%CC%CA%A9%B9%A4\n1. 市政工程施工板块中的【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如市政工程施工-招标公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选市政工程施工-招标公告\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": false,
"mode": "qwen3.5-plus"
},
{
"id": "task-1773369738593",
"city": "省建设平台-单独装饰装修施工-招标公告",
"prompt": "爬取http://www.jszb.com.cn/JSZB/YW_info/ZhaoBiaoGG/MoreInfo_ZBGG.aspx?Type=%B5%A5%B6%C0%D7%B0%CA%CE%D7%B0%D0%DE%CA%A9%B9%A4\n1. 单独装饰装修施工板块中的【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如单独装饰装修施工-招标公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选单独装饰装修施工-招标公告\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773369993193",
"city": "省建设平台-设计-招标公告",
"prompt": "爬取http://www.jszb.com.cn/JSZB/YW_info/ZhaoBiaoGG/MoreInfo_ZBGG.aspx?Type=%C9%E8%BC%C6\n1. 设计板块中的【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如设计-招标公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选设计-招标公告\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
},
{
"id": "task-1773370032974",
"city": "省建设平台-水利-招标公告",
"prompt": "爬取http://www.jszb.com.cn/JSZB/YW_info/ZhaoBiaoGG/MoreInfo_ZBGG.aspx?Type=%CB%AE%C0%FB\n1. 水利板块中的【招标公告】模块\n\n按照以下要求进行\n【要求一】获取列表数据\n如果当前列表没有当天的数据默认获取最新的1条数据。如果当前列表有多条当天数据请获取当天的全部数据。将获取到的列表数据先存下来\n【要求二】列表数据处理\n1项目名称必须要使用列表中的项目名称而且必须先记住列表中的项目名称不可以用其他项目名称来替代记录到“project_name”字段中。\n2如果列表已经有金额依据表头里的金额单位比如“万元”需要转为元不需要打开详情页面去爬取项目金额记录到“amount_yuan”字段中。\n如果列表没有金额打开详情页去查看项目金额。记录到“amount_yuan”字段中。\n3项目金额可能为合同预估价/最高投标限价等等。不要试图写代码去正则匹配金额因为金额的表述很多样优先获取到详情md文件或者html使用`takeMoney`工具进行获取提取金额。\n4项目名称和项目金额必须对应如果获取不到金额将金额直接设置为null值。禁止联想生成项目金额。\n\n【要求三】\n1必须记录下每个项目的详情链接记录到\"target_link\"字段中。\n\n输出结果为jsonjson结构如下\n```\n{\n \"results\": [\n\n {\n \"type\": \"比如水利-招标公告\",\n\t\t\"project_name\": \"\",\n\t\t\"amount_yuan\": 0,\n\t\t\"date\": \"yyyy-MM-dd\",\n\t\t\"target_link\": \"http...\"\n\t }\n ]\n}\n```\n## 采集方法\n1. 使用 agent-browser 工具访问页\n2. 筛选水利-招标公告\n3. 点击项目进入详情页\n4. 获取详情页URL\n5. 保存详情页内容到文件\n6. 使用 takeMoney 工具提取金额",
"enabled": false,
"useBrowser": false,
"mode": "glm-5"
}
],
"scheduler": { "scheduler": {
"enabled": false, "enabled": true,
"cronTime": "0 9 * * *", "cronTime": "0 9 * * *",
"description": "每天9点采集当日项目" "threshold": 100000,
"description": "每天9点采集当日项目",
"timeRange": "thisMonth"
}, },
"email": { "email": {
"smtpHost": "smtp.qq.com", "smtpHost": "smtp.qq.com",
"smtpPort": 587, "smtpPort": 587,
"smtpUser": "1076597680@qq.com", "smtpUser": "1076597680@qq.com",
"smtpPass": "nfrjdiraqddsjeeh", "smtpPass": "nfrjdiraqddsjeeh",
"recipients": "5482498@qq.com" "recipients": "1650243281@qq.com"
} }
} }

717
package-lock.json generated
View File

@@ -8,14 +8,14 @@
"name": "njggzy-scraper", "name": "njggzy-scraper",
"version": "2.0.0", "version": "2.0.0",
"dependencies": { "dependencies": {
"@mendable/firecrawl-js": "^4.15.2", "@mendable/firecrawl-js": "latest",
"cheerio": "^1.0.0-rc.12", "better-sqlite3": "^12.8.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^17.2.3", "dotenv": "^17.2.3",
"express": "^5.2.1", "express": "^5.2.1",
"node-cron": "^4.2.1", "node-cron": "^4.2.1",
"nodemailer": "^7.0.11", "nodemailer": "^7.0.11",
"zod": "^4.3.6" "zod": "^3.24.2"
} }
}, },
"node_modules/@mendable/firecrawl-js": { "node_modules/@mendable/firecrawl-js": {
@@ -33,15 +33,6 @@
"node": ">=22.0.0" "node": ">=22.0.0"
} }
}, },
"node_modules/@mendable/firecrawl-js/node_modules/zod": {
"version": "3.25.76",
"resolved": "https://registry.npmmirror.com/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
},
"node_modules/accepts": { "node_modules/accepts": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmmirror.com/accepts/-/accepts-2.0.0.tgz", "resolved": "https://registry.npmmirror.com/accepts/-/accepts-2.0.0.tgz",
@@ -97,6 +88,60 @@
"proxy-from-env": "^1.1.0" "proxy-from-env": "^1.1.0"
} }
}, },
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/better-sqlite3": {
"version": "12.8.0",
"resolved": "https://registry.npmmirror.com/better-sqlite3/-/better-sqlite3-12.8.0.tgz",
"integrity": "sha512-RxD2Vd96sQDjQr20kdP+F+dK/1OUNiVOl200vKBZY8u0vTwysfolF6Hq+3ZK2+h8My9YvZhHsF+RSGZW2VYrPQ==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"bindings": "^1.5.0",
"prebuild-install": "^7.1.1"
},
"engines": {
"node": "20.x || 22.x || 23.x || 24.x || 25.x"
}
},
"node_modules/bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmmirror.com/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"license": "MIT",
"dependencies": {
"file-uri-to-path": "1.0.0"
}
},
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"license": "MIT",
"dependencies": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
}
},
"node_modules/body-parser": { "node_modules/body-parser": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-2.2.1.tgz", "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-2.2.1.tgz",
@@ -137,11 +182,29 @@
"url": "https://opencollective.com/express" "url": "https://opencollective.com/express"
} }
}, },
"node_modules/boolbase": { "node_modules/buffer": {
"version": "1.0.0", "version": "5.7.1",
"resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz", "resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"license": "ISC" "funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
}, },
"node_modules/bytes": { "node_modules/bytes": {
"version": "3.1.2", "version": "3.1.2",
@@ -181,47 +244,11 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/cheerio": { "node_modules/chownr": {
"version": "1.1.2", "version": "1.1.4",
"resolved": "https://registry.npmmirror.com/cheerio/-/cheerio-1.1.2.tgz", "resolved": "https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
"license": "MIT", "license": "ISC"
"dependencies": {
"cheerio-select": "^2.1.0",
"dom-serializer": "^2.0.0",
"domhandler": "^5.0.3",
"domutils": "^3.2.2",
"encoding-sniffer": "^0.2.1",
"htmlparser2": "^10.0.0",
"parse5": "^7.3.0",
"parse5-htmlparser2-tree-adapter": "^7.1.0",
"parse5-parser-stream": "^7.1.2",
"undici": "^7.12.0",
"whatwg-mimetype": "^4.0.0"
},
"engines": {
"node": ">=20.18.1"
},
"funding": {
"url": "https://github.com/cheeriojs/cheerio?sponsor=1"
}
},
"node_modules/cheerio-select": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/cheerio-select/-/cheerio-select-2.1.0.tgz",
"integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
"license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0",
"css-select": "^5.1.0",
"css-what": "^6.1.0",
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3",
"domutils": "^3.0.1"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
}, },
"node_modules/combined-stream": { "node_modules/combined-stream": {
"version": "1.0.8", "version": "1.0.8",
@@ -288,34 +315,6 @@
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/css-select": {
"version": "5.2.2",
"resolved": "https://registry.npmmirror.com/css-select/-/css-select-5.2.2.tgz",
"integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
"license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^6.1.0",
"domhandler": "^5.0.2",
"domutils": "^3.0.1",
"nth-check": "^2.0.1"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/css-what": {
"version": "6.2.2",
"resolved": "https://registry.npmmirror.com/css-what/-/css-what-6.2.2.tgz",
"integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
"license": "BSD-2-Clause",
"engines": {
"node": ">= 6"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/debug": { "node_modules/debug": {
"version": "4.4.3", "version": "4.4.3",
"resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz",
@@ -333,6 +332,30 @@
} }
} }
}, },
"node_modules/decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"license": "MIT",
"dependencies": {
"mimic-response": "^3.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmmirror.com/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"license": "MIT",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/delayed-stream": { "node_modules/delayed-stream": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -351,59 +374,13 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/dom-serializer": { "node_modules/detect-libc": {
"version": "2.0.0", "version": "2.1.2",
"resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-2.0.0.tgz", "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"license": "MIT", "license": "Apache-2.0",
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.2",
"entities": "^4.2.0"
},
"funding": {
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
"node_modules/domelementtype": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz",
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"license": "BSD-2-Clause"
},
"node_modules/domhandler": {
"version": "5.0.3",
"resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-5.0.3.tgz",
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
"license": "BSD-2-Clause",
"dependencies": {
"domelementtype": "^2.3.0"
},
"engines": { "engines": {
"node": ">= 4" "node": ">=8"
},
"funding": {
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/domutils": {
"version": "3.2.2",
"resolved": "https://registry.npmmirror.com/domutils/-/domutils-3.2.2.tgz",
"integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
"license": "BSD-2-Clause",
"dependencies": {
"dom-serializer": "^2.0.0",
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3"
},
"funding": {
"url": "https://github.com/fb55/domutils?sponsor=1"
} }
}, },
"node_modules/dotenv": { "node_modules/dotenv": {
@@ -447,29 +424,13 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/encoding-sniffer": { "node_modules/end-of-stream": {
"version": "0.2.1", "version": "1.4.5",
"resolved": "https://registry.npmmirror.com/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", "resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz",
"integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"iconv-lite": "^0.6.3", "once": "^1.4.0"
"whatwg-encoding": "^3.1.1"
},
"funding": {
"url": "https://github.com/fb55/encoding-sniffer?sponsor=1"
}
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
} }
}, },
"node_modules/es-define-property": { "node_modules/es-define-property": {
@@ -532,6 +493,15 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/expand-template": {
"version": "2.0.3",
"resolved": "https://registry.npmmirror.com/expand-template/-/expand-template-2.0.3.tgz",
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
"license": "(MIT OR WTFPL)",
"engines": {
"node": ">=6"
}
},
"node_modules/express": { "node_modules/express": {
"version": "5.2.1", "version": "5.2.1",
"resolved": "https://registry.npmmirror.com/express/-/express-5.2.1.tgz", "resolved": "https://registry.npmmirror.com/express/-/express-5.2.1.tgz",
@@ -600,6 +570,12 @@
"url": "https://opencollective.com/express" "url": "https://opencollective.com/express"
} }
}, },
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"license": "MIT"
},
"node_modules/finalhandler": { "node_modules/finalhandler": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-2.1.1.tgz", "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-2.1.1.tgz",
@@ -675,6 +651,12 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
"license": "MIT"
},
"node_modules/function-bind": { "node_modules/function-bind": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
@@ -721,6 +703,12 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/github-from-package": {
"version": "0.0.0",
"resolved": "https://registry.npmmirror.com/github-from-package/-/github-from-package-0.0.0.tgz",
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
"license": "MIT"
},
"node_modules/gopd": { "node_modules/gopd": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
@@ -772,37 +760,6 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/htmlparser2": {
"version": "10.0.0",
"resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-10.0.0.tgz",
"integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==",
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"license": "MIT",
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3",
"domutils": "^3.2.1",
"entities": "^6.0.0"
}
},
"node_modules/htmlparser2/node_modules/entities": {
"version": "6.0.1",
"resolved": "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz",
"integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/http-errors": { "node_modules/http-errors": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.1.tgz", "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.1.tgz",
@@ -823,17 +780,25 @@
"url": "https://opencollective.com/express" "url": "https://opencollective.com/express"
} }
}, },
"node_modules/iconv-lite": { "node_modules/ieee754": {
"version": "0.6.3", "version": "1.2.1",
"resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"license": "MIT", "funding": [
"dependencies": { {
"safer-buffer": ">= 2.1.2 < 3.0.0" "type": "github",
"url": "https://github.com/sponsors/feross"
}, },
"engines": { {
"node": ">=0.10.0" "type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
} }
],
"license": "BSD-3-Clause"
}, },
"node_modules/inherits": { "node_modules/inherits": {
"version": "2.0.4", "version": "2.0.4",
@@ -841,6 +806,12 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/ini": {
"version": "1.3.8",
"resolved": "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
"license": "ISC"
},
"node_modules/ipaddr.js": { "node_modules/ipaddr.js": {
"version": "1.9.1", "version": "1.9.1",
"resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -907,12 +878,45 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/mimic-response": {
"version": "3.1.0",
"resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/mkdirp-classic": {
"version": "0.5.3",
"resolved": "https://registry.npmmirror.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
"license": "MIT"
},
"node_modules/ms": { "node_modules/ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/napi-build-utils": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==",
"license": "MIT"
},
"node_modules/negotiator": { "node_modules/negotiator": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-1.0.0.tgz", "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-1.0.0.tgz",
@@ -922,6 +926,18 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/node-abi": {
"version": "3.89.0",
"resolved": "https://registry.npmmirror.com/node-abi/-/node-abi-3.89.0.tgz",
"integrity": "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==",
"license": "MIT",
"dependencies": {
"semver": "^7.3.5"
},
"engines": {
"node": ">=10"
}
},
"node_modules/node-cron": { "node_modules/node-cron": {
"version": "4.2.1", "version": "4.2.1",
"resolved": "https://registry.npmmirror.com/node-cron/-/node-cron-4.2.1.tgz", "resolved": "https://registry.npmmirror.com/node-cron/-/node-cron-4.2.1.tgz",
@@ -940,18 +956,6 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/nth-check": {
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz",
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
"license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0"
},
"funding": {
"url": "https://github.com/fb55/nth-check?sponsor=1"
}
},
"node_modules/object-assign": { "node_modules/object-assign": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
@@ -994,55 +998,6 @@
"wrappy": "1" "wrappy": "1"
} }
}, },
"node_modules/parse5": {
"version": "7.3.0",
"resolved": "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz",
"integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
"license": "MIT",
"dependencies": {
"entities": "^6.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"node_modules/parse5-htmlparser2-tree-adapter": {
"version": "7.1.0",
"resolved": "https://registry.npmmirror.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz",
"integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==",
"license": "MIT",
"dependencies": {
"domhandler": "^5.0.3",
"parse5": "^7.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"node_modules/parse5-parser-stream": {
"version": "7.1.2",
"resolved": "https://registry.npmmirror.com/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz",
"integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==",
"license": "MIT",
"dependencies": {
"parse5": "^7.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"node_modules/parse5/node_modules/entities": {
"version": "6.0.1",
"resolved": "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz",
"integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/parseurl": { "node_modules/parseurl": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz", "resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz",
@@ -1062,6 +1017,32 @@
"url": "https://opencollective.com/express" "url": "https://opencollective.com/express"
} }
}, },
"node_modules/prebuild-install": {
"version": "7.1.2",
"resolved": "https://registry.npmmirror.com/prebuild-install/-/prebuild-install-7.1.2.tgz",
"integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==",
"license": "MIT",
"dependencies": {
"detect-libc": "^2.0.0",
"expand-template": "^2.0.3",
"github-from-package": "0.0.0",
"minimist": "^1.2.3",
"mkdirp-classic": "^0.5.3",
"napi-build-utils": "^1.0.1",
"node-abi": "^3.3.0",
"pump": "^3.0.0",
"rc": "^1.2.7",
"simple-get": "^4.0.0",
"tar-fs": "^2.0.0",
"tunnel-agent": "^0.6.0"
},
"bin": {
"prebuild-install": "bin.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/proxy-addr": { "node_modules/proxy-addr": {
"version": "2.0.7", "version": "2.0.7",
"resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz", "resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -1081,6 +1062,16 @@
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/pump": {
"version": "3.0.4",
"resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.4.tgz",
"integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
"license": "MIT",
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"node_modules/qs": { "node_modules/qs": {
"version": "6.14.0", "version": "6.14.0",
"resolved": "https://registry.npmmirror.com/qs/-/qs-6.14.0.tgz", "resolved": "https://registry.npmmirror.com/qs/-/qs-6.14.0.tgz",
@@ -1136,6 +1127,35 @@
"url": "https://opencollective.com/express" "url": "https://opencollective.com/express"
} }
}, },
"node_modules/rc": {
"version": "1.2.8",
"resolved": "https://registry.npmmirror.com/rc/-/rc-1.2.8.tgz",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
"dependencies": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
"minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
},
"bin": {
"rc": "cli.js"
}
},
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/router": { "node_modules/router": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmmirror.com/router/-/router-2.2.0.tgz", "resolved": "https://registry.npmmirror.com/router/-/router-2.2.0.tgz",
@@ -1152,12 +1172,44 @@
"node": ">= 18" "node": ">= 18"
} }
}, },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/safer-buffer": { "node_modules/safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/semver": {
"version": "7.7.4",
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/send": { "node_modules/send": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmmirror.com/send/-/send-1.2.0.tgz", "resolved": "https://registry.npmmirror.com/send/-/send-1.2.0.tgz",
@@ -1298,6 +1350,51 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/simple-get": {
"version": "4.0.1",
"resolved": "https://registry.npmmirror.com/simple-get/-/simple-get-4.0.1.tgz",
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"dependencies": {
"decompress-response": "^6.0.0",
"once": "^1.3.1",
"simple-concat": "^1.0.0"
}
},
"node_modules/statuses": { "node_modules/statuses": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.2.tgz", "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.2.tgz",
@@ -1307,6 +1404,52 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/tar-fs": {
"version": "2.1.4",
"resolved": "https://registry.npmmirror.com/tar-fs/-/tar-fs-2.1.4.tgz",
"integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
"license": "MIT",
"dependencies": {
"chownr": "^1.1.1",
"mkdirp-classic": "^0.5.2",
"pump": "^3.0.0",
"tar-stream": "^2.1.4"
}
},
"node_modules/tar-stream": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"license": "MIT",
"dependencies": {
"bl": "^4.0.3",
"end-of-stream": "^1.4.1",
"fs-constants": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/toidentifier": { "node_modules/toidentifier": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz", "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz",
@@ -1316,6 +1459,18 @@
"node": ">=0.6" "node": ">=0.6"
} }
}, },
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmmirror.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
"license": "Apache-2.0",
"dependencies": {
"safe-buffer": "^5.0.1"
},
"engines": {
"node": "*"
}
},
"node_modules/type-is": { "node_modules/type-is": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmmirror.com/type-is/-/type-is-2.0.1.tgz", "resolved": "https://registry.npmmirror.com/type-is/-/type-is-2.0.1.tgz",
@@ -1361,15 +1516,6 @@
"integrity": "sha512-TvkrTUpv7gCPlcnSoEwUVUBwsdheKm+HF5u2tPAKubkIGMfovdSizCTaZRY/NhR8+Ijy8iZZUapbVQAsNrkFrw==", "integrity": "sha512-TvkrTUpv7gCPlcnSoEwUVUBwsdheKm+HF5u2tPAKubkIGMfovdSizCTaZRY/NhR8+Ijy8iZZUapbVQAsNrkFrw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/undici": {
"version": "7.16.0",
"resolved": "https://registry.npmmirror.com/undici/-/undici-7.16.0.tgz",
"integrity": "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==",
"license": "MIT",
"engines": {
"node": ">=20.18.1"
}
},
"node_modules/unpipe": { "node_modules/unpipe": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz", "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz",
@@ -1379,6 +1525,12 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
"node_modules/vary": { "node_modules/vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz",
@@ -1388,27 +1540,6 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/whatwg-encoding": {
"version": "3.1.1",
"resolved": "https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
"integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
"license": "MIT",
"dependencies": {
"iconv-lite": "0.6.3"
},
"engines": {
"node": ">=18"
}
},
"node_modules/whatwg-mimetype": {
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
"integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/wrappy": { "node_modules/wrappy": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
@@ -1416,9 +1547,9 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/zod": { "node_modules/zod": {
"version": "4.3.6", "version": "3.25.76",
"resolved": "https://registry.npmmirror.com/zod/-/zod-4.3.6.tgz", "resolved": "https://registry.npmmirror.com/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"url": "https://github.com/sponsors/colinhacks" "url": "https://github.com/sponsors/colinhacks"

View File

@@ -9,6 +9,7 @@
}, },
"dependencies": { "dependencies": {
"@mendable/firecrawl-js": "latest", "@mendable/firecrawl-js": "latest",
"better-sqlite3": "^12.8.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^17.2.3", "dotenv": "^17.2.3",
"express": "^5.2.1", "express": "^5.2.1",

View File

@@ -534,6 +534,13 @@
<div style="font-size:13px;color:#888;" id="taskSummary"></div> <div style="font-size:13px;color:#888;" id="taskSummary"></div>
</div> </div>
<div class="toolbar" style="margin-bottom: 14px;">
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;width:100%;">
<input type="text" id="taskFilterCity" placeholder="搜索城市" oninput="onTaskFilterChange()" style="max-width:220px;">
<input type="text" id="taskFilterPlate" placeholder="搜索板块" oninput="onTaskFilterChange()" style="max-width:220px;">
</div>
</div>
<table> <table>
<thead> <thead>
<tr> <tr>
@@ -542,17 +549,18 @@
<th>提示词</th> <th>提示词</th>
<th>模型</th> <th>模型</th>
<th>状态</th> <th>状态</th>
<th>浏览器</th>
<th>操作</th> <th>操作</th>
</tr> </tr>
</thead> </thead>
<tbody id="tasksTbody"> <tbody id="tasksTbody">
<tr> <tr>
<td colspan="7" class="empty-state">加载中...</td> <td colspan="6" class="empty-state">加载中...</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<div id="tasksPagination" style="text-align:center;margin-top:16px;"></div>
<div id="batchRunStatus" class="status-box status-info" style="display:none;"></div> <div id="batchRunStatus" class="status-box status-info" style="display:none;"></div>
</div> </div>
@@ -582,13 +590,6 @@
<label>服务地址 (baseUrl)</label> <label>服务地址 (baseUrl)</label>
<input type="text" id="cfgBaseUrl" placeholder="http://192.168.3.65:18625"> <input type="text" id="cfgBaseUrl" placeholder="http://192.168.3.65:18625">
</div> </div>
<div class="form-group">
<label>useBrowser</label>
<select id="cfgUseBrowser">
<option value="false">false</option>
<option value="true">true</option>
</select>
</div>
</div> </div>
<div class="form-row"> <div class="form-row">
<div class="form-group"> <div class="form-group">
@@ -698,11 +699,6 @@
<input type="checkbox" id="taskEnabled" checked> 启用 <input type="checkbox" id="taskEnabled" checked> 启用
</label> </label>
</div> </div>
<div class="form-group">
<label>
<input type="checkbox" id="taskUseBrowser"> 打开浏览器
</label>
</div>
<div style="display:flex;gap:10px;justify-content:flex-end;"> <div style="display:flex;gap:10px;justify-content:flex-end;">
<button type="button" class="btn btn-secondary" onclick="closeTaskModal()">取消</button> <button type="button" class="btn btn-secondary" onclick="closeTaskModal()">取消</button>
<button type="submit" class="btn btn-primary">保存</button> <button type="submit" class="btn btn-primary">保存</button>
@@ -713,6 +709,8 @@
<script> <script>
let tasksList = []; let tasksList = [];
let currentTasksPage = 1;
const tasksPageSize = 10;
let currentResultsPage = 1; let currentResultsPage = 1;
// ===== Tab 切换 ===== // ===== Tab 切换 =====
@@ -733,30 +731,58 @@
const res = await fetch('/api/tasks'); const res = await fetch('/api/tasks');
const json = await res.json(); const json = await res.json();
tasksList = json.data || []; tasksList = json.data || [];
currentTasksPage = 1;
renderTasks(); renderTasks();
} catch (e) { } catch (e) {
console.error('加载任务失败:', e); console.error('加载任务失败:', e);
} }
} }
function getFilteredTasks() {
const cityKeyword = document.getElementById('taskFilterCity').value.trim().toLowerCase();
const plateKeyword = document.getElementById('taskFilterPlate').value.trim().toLowerCase();
return tasksList.filter(t => {
const city = (t.city || '').toLowerCase();
const plate = (t.plateName || '').toLowerCase();
const matchCity = !cityKeyword || city.includes(cityKeyword);
const matchPlate = !plateKeyword || plate.includes(plateKeyword);
return matchCity && matchPlate;
});
}
function onTaskFilterChange() {
currentTasksPage = 1;
renderTasks();
}
function renderTasks() { function renderTasks() {
const tbody = document.getElementById('tasksTbody'); const tbody = document.getElementById('tasksTbody');
const enabled = tasksList.filter(t => t.enabled).length; const enabled = tasksList.filter(t => t.enabled).length;
document.getElementById('taskSummary').textContent = `${tasksList.length} 个任务,${enabled} 个启用`; const filtered = getFilteredTasks();
const totalPages = Math.max(1, Math.ceil(filtered.length / tasksPageSize));
if (currentTasksPage > totalPages) currentTasksPage = totalPages;
if (tasksList.length === 0) { const startIdx = (currentTasksPage - 1) * tasksPageSize;
tbody.innerHTML = '<tr><td colspan="7" class="empty-state">暂无任务,点击「新增任务」添加</td></tr>'; const pageData = filtered.slice(startIdx, startIdx + tasksPageSize);
document.getElementById('taskSummary').textContent =
`${tasksList.length} 个任务,${enabled} 个启用,筛选后 ${filtered.length}`;
if (filtered.length === 0) {
const hasKeyword = document.getElementById('taskFilterCity').value.trim() || document.getElementById('taskFilterPlate').value.trim();
tbody.innerHTML = `<tr><td colspan="6" class="empty-state">${hasKeyword ? '没有匹配的任务' : '暂无任务,点击「新增任务」添加'}</td></tr>`;
document.getElementById('tasksPagination').innerHTML = '';
return; return;
} }
tbody.innerHTML = tasksList.map(t => ` tbody.innerHTML = pageData.map(t => `
<tr> <tr>
<td><strong>${t.city || '-'}</strong></td> <td><strong>${t.city || '-'}</strong></td>
<td>${t.plateName || '-'}</td> <td>${t.plateName || '-'}</td>
<td class="prompt-cell" title="${(t.prompt || '').replace(/"/g, '&quot;')}">${t.prompt || '-'}</td> <td class="prompt-cell" title="${(t.prompt || '').replace(/"/g, '&quot;')}">${t.prompt || '-'}</td>
<td><code style="font-family:'Fira Code','Cascadia Code',monospace;font-size:12px;">${t.mode || 'qwen3.5-plus'}</code></td> <td><code style="font-family:'Fira Code','Cascadia Code',monospace;font-size:12px;">${t.mode || 'qwen3.5-plus'}</code></td>
<td><span class="tag ${t.enabled ? 'tag-on' : 'tag-off'}">${t.enabled ? '启用' : '禁用'}</span></td> <td><span class="tag ${t.enabled ? 'tag-on' : 'tag-off'}">${t.enabled ? '启用' : '禁用'}</span></td>
<td><span class="tag ${t.useBrowser === true ? 'tag-on' : (t.useBrowser === false ? 'tag-off' : '')}">${t.useBrowser === true ? '打开' : (t.useBrowser === false ? '关闭' : '继承全局')}</span></td>
<td> <td>
<button class="btn btn-primary btn-sm" onclick="openTaskModal('${t.id}')">编辑</button> <button class="btn btn-primary btn-sm" onclick="openTaskModal('${t.id}')">编辑</button>
<button class="btn btn-success btn-sm" onclick="runSingleTask('${t.id}', this)">运行</button> <button class="btn btn-success btn-sm" onclick="runSingleTask('${t.id}', this)">运行</button>
@@ -765,11 +791,59 @@
</td> </td>
</tr> </tr>
`).join(''); `).join('');
renderTasksPagination(filtered.length, totalPages);
}
function renderTasksPagination(totalFiltered, totalPages) {
const paginationDiv = document.getElementById('tasksPagination');
if (totalPages <= 1) {
paginationDiv.innerHTML = totalFiltered > 0
? `<span style="font-size:13px;color:#888;">共 ${totalFiltered} 条</span>`
: '';
return;
}
let html = '';
html += `<button class="btn btn-secondary btn-sm" onclick="goTasksPage(${currentTasksPage - 1})" ${currentTasksPage <= 1 ? 'disabled' : ''}>上一页</button>`;
const maxVisiblePages = 7;
let startPage = Math.max(1, currentTasksPage - Math.floor(maxVisiblePages / 2));
let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1);
if (endPage - startPage < maxVisiblePages - 1) {
startPage = Math.max(1, endPage - maxVisiblePages + 1);
}
if (startPage > 1) {
html += `<button class="btn btn-sm btn-secondary" onclick="goTasksPage(1)" style="margin:0 2px;">1</button>`;
if (startPage > 2) html += `<span style="color:#888;font-size:13px;margin:0 4px;">...</span>`;
}
for (let i = startPage; i <= endPage; i++) {
html += `<button class="btn btn-sm ${i === currentTasksPage ? 'btn-primary' : 'btn-secondary'}" onclick="goTasksPage(${i})" style="margin:0 2px;">${i}</button>`;
}
if (endPage < totalPages) {
if (endPage < totalPages - 1) html += `<span style="color:#888;font-size:13px;margin:0 4px;">...</span>`;
html += `<button class="btn btn-sm btn-secondary" onclick="goTasksPage(${totalPages})" style="margin:0 2px;">${totalPages}</button>`;
}
html += `<button class="btn btn-secondary btn-sm" onclick="goTasksPage(${currentTasksPage + 1})" ${currentTasksPage >= totalPages ? 'disabled' : ''}>下一页</button>`;
html += `<span style="font-size:13px;color:#888;margin-left:8px;">第 ${currentTasksPage}/${totalPages} 页,共 ${totalFiltered} 条</span>`;
paginationDiv.innerHTML = html;
}
function goTasksPage(page) {
const totalPages = Math.max(1, Math.ceil(getFilteredTasks().length / tasksPageSize));
if (page < 1 || page > totalPages) return;
currentTasksPage = page;
renderTasks();
} }
function openTaskModal(id) { function openTaskModal(id) {
const item = id ? tasksList.find(t => t.id === id) : null; const item = id ? tasksList.find(t => t.id === id) : null;
const globalUseBrowser = document.getElementById('cfgUseBrowser').value === 'true';
document.getElementById('taskModalTitle').textContent = item ? '编辑任务' : '新增任务'; document.getElementById('taskModalTitle').textContent = item ? '编辑任务' : '新增任务';
document.getElementById('taskEditId').value = item ? item.id : ''; document.getElementById('taskEditId').value = item ? item.id : '';
document.getElementById('taskCity').value = item ? item.city : ''; document.getElementById('taskCity').value = item ? item.city : '';
@@ -785,9 +859,6 @@
} }
modeSelect.value = modeValue; modeSelect.value = modeValue;
document.getElementById('taskEnabled').checked = item ? item.enabled : true; document.getElementById('taskEnabled').checked = item ? item.enabled : true;
document.getElementById('taskUseBrowser').checked = item
? (typeof item.useBrowser === 'boolean' ? item.useBrowser : globalUseBrowser)
: globalUseBrowser;
document.getElementById('taskModal').classList.add('show'); document.getElementById('taskModal').classList.add('show');
} }
@@ -804,7 +875,6 @@
prompt: document.getElementById('taskPrompt').value.trim(), prompt: document.getElementById('taskPrompt').value.trim(),
mode: document.getElementById('taskMode').value.trim() || 'qwen3.5-plus', mode: document.getElementById('taskMode').value.trim() || 'qwen3.5-plus',
enabled: document.getElementById('taskEnabled').checked, enabled: document.getElementById('taskEnabled').checked,
useBrowser: document.getElementById('taskUseBrowser').checked,
}; };
try { try {
const url = id ? `/api/tasks/${id}` : '/api/tasks'; const url = id ? `/api/tasks/${id}` : '/api/tasks';
@@ -1067,7 +1137,6 @@
// Agent // Agent
document.getElementById('cfgBaseUrl').value = cfg.agent?.baseUrl || ''; document.getElementById('cfgBaseUrl').value = cfg.agent?.baseUrl || '';
document.getElementById('cfgUseBrowser').value = String(cfg.agent?.useBrowser ?? false);
document.getElementById('cfgPollInterval').value = cfg.agent?.pollInterval || 3000; document.getElementById('cfgPollInterval').value = cfg.agent?.pollInterval || 3000;
document.getElementById('cfgTimeout').value = cfg.agent?.timeout || 300000; document.getElementById('cfgTimeout').value = cfg.agent?.timeout || 300000;
@@ -1098,19 +1167,12 @@
async function saveSettings() { async function saveSettings() {
try { try {
// 先获取当前完整配置(保留 tasks
const curRes = await fetch('/api/config');
const curJson = await curRes.json();
const curCfg = curJson.data || {};
const cfg = { const cfg = {
agent: { agent: {
baseUrl: document.getElementById('cfgBaseUrl').value.trim(), baseUrl: document.getElementById('cfgBaseUrl').value.trim(),
useBrowser: document.getElementById('cfgUseBrowser').value === 'true',
pollInterval: parseInt(document.getElementById('cfgPollInterval').value) || 3000, pollInterval: parseInt(document.getElementById('cfgPollInterval').value) || 3000,
timeout: parseInt(document.getElementById('cfgTimeout').value) || 300000, timeout: parseInt(document.getElementById('cfgTimeout').value) || 300000,
}, },
tasks: curCfg.tasks || [],
scheduler: { scheduler: {
enabled: document.getElementById('cfgSchedulerEnabled').value === 'true', enabled: document.getElementById('cfgSchedulerEnabled').value === 'true',
cronTime: document.getElementById('cfgCronTime').value.trim() || '0 9 * * *', cronTime: document.getElementById('cfgCronTime').value.trim() || '0 9 * * *',

View File

@@ -1,266 +0,0 @@
[
{
"taskId": "task-1710000000001",
"city": "南京市",
"scrapedAt": "2026-03-10T08:12:18.355Z",
"data": {
"results": [
{
"type": "房建市政",
"project_name": "星甸污水处理厂提标改造工程(一期)- 施工",
"amount_yuan": 5205328.1,
"date": "2026-03-10",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/fjsz/068001/068001002/20260310/84ce20ef-3ccf-414e-ad95-969296ae19a6.html"
},
{
"type": "交通水务",
"project_name": "南京港华燃气 sueper 大客户管理系统项目",
"amount_yuan": 8820000,
"date": "2026-03-10",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/jtsw/069002/20260310/4c99f020-5b79-dffb-e063-3345c40a2f88.html"
},
{
"type": "政府采购",
"project_name": "南京市公安局交通管理局机动车驾驶人考试中心考场服务项目",
"amount_yuan": 11130000,
"date": "2026-03-10",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/zfcg/055001/055001001/20260310/4c99f020-5c2c-dffb-e063-3345c40a2f88.html"
}
],
"total": 3
},
"id": "result-1773130338356-w41hf"
},
{
"taskId": "task-1710000000001",
"city": "南京市",
"scrapedAt": "2026-03-10T07:53:50.177Z",
"data": {
"results": [
{
"type": "房建市政",
"project_name": "星甸污水处理厂提标改造工程(一期)",
"amount_yuan": 2776800,
"date": "2026-03-10",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/fjsz/068001/068001002/20260310/84ce20ef-3ccf-414e-ad95-969296ae19a6.html"
},
{
"type": "房建市政",
"project_name": "【澄清公告】麒麟科创园具身智能训练场装修项目",
"amount_yuan": 833000,
"date": "2026-03-10",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/fjsz/068001/068001002/20260310/945533de-a0ed-4118-a30e-ec831a4e337b.html"
},
{
"type": "房建市政",
"project_name": "【澄清公告】轻质耐热合金制造基地项目",
"amount_yuan": 100000000,
"date": "2026-03-09",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/fjsz/068001/068001002/20260309/fb9e7e41-d809-458c-9cd4-59b90e84fbc5.html"
},
{
"type": "工程货物",
"project_name": "JNFJ2500777-04HW-GHa01",
"amount_yuan": 66000000,
"date": "2026-03-07",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/gchw/070001/20260307/041138ff-870c-4240-b78c-f6861f242594.html"
},
{
"type": "工程货物",
"project_name": "NJFJ2500863-03HWGH",
"amount_yuan": 484057073,
"date": "2026-03-06",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/gchw/070001/20260306/a547fb75-c984-4479-948b-b5800829742a.html"
},
{
"type": "工程货物",
"project_name": "JNFJ2500777-06QTGH",
"amount_yuan": 535784084,
"date": "2026-03-06",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/gchw/070001/20260306/cab7606b-df8f-419e-be5a-4ce5213ef5af.html"
},
{
"type": "交通水务",
"project_name": "(市交易中心) 八卦洲街道CBA3上坝子堤路等农村公路改善提升工程施工招标公告",
"amount_yuan": 338600000,
"date": "2026-03-04",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/jtsw/069001/069001001/20260304/3d25610f-239d-4e1c-9923-0d22820c711a.html"
},
{
"type": "交通水务",
"project_name": "(市交易中心) 南京地铁1号线通信信号系统设备更新改造项目通信系统安装施工招标公告",
"amount_yuan": 1693000000,
"date": "2026-02-14",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/jtsw/069001/069001001/20260214/4ddc9129-c508-4ae9-b103-f52851ff2ee2.html"
},
{
"type": "交通水务",
"project_name": "(市交易中心) 356省道浦口西江路至苏皖省界段建设工程起点至龙港路段供电管线迁改工程招标公告",
"amount_yuan": 2000000,
"date": "2026-02-12",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/jtsw/069001/069001001/20260212/5ae97a66-ca0d-40e4-991c-f91d6164661f.html"
},
{
"type": "政府采购",
"project_name": "JSZC-320111-JZCG-G2026-0013",
"amount_yuan": 204000,
"date": "2026-03-10",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/zfcg/067001/067001001/20260310/adb322241a19459cbaaf158e4ad87500.html"
},
{
"type": "政府采购",
"project_name": "JSZC-320100-JZCG-C2026-0026",
"amount_yuan": 1020000,
"date": "2026-03-10",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/zfcg/067001/067001001/20260310/a801f9ba484741f9b373207edcbc0320.html"
},
{
"type": "政府采购",
"project_name": "JSZC-320100-JZCG-C2026-0030",
"amount_yuan": 50000,
"date": "2026-03-10",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/zfcg/067001/067001001/20260310/19c6d526a57c451fbbbe2c1b7a779c02.html"
},
{
"type": "产权交易",
"project_name": "北京北汽福斯特股份有限公司1.3884%股权138.8373万股)股权转让",
"amount_yuan": 66000000,
"date": "2026-04-20",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/gycq/071001/071001002/20260309/64b16240-07de-417c-8248-c44967c508f9.html"
},
{
"type": "产权交易",
"project_name": "北京北汽九龙出租汽车股份有限公司1.3884%91.6327万股)股权转让",
"amount_yuan": 484057073,
"date": "2026-04-20",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/gycq/071001/071001002/20260309/57c7f45a-b28e-4ba9-b11b-62cca6f6218e.html"
},
{
"type": "产权交易",
"project_name": "南京鑫一汇企业管理有限公司20%股权转让",
"amount_yuan": 2000000,
"date": "2026-02-25",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/gycq/071001/071001002/20260108/442d7bf6-f11d-49f5-9409-aef8acd4fbf6.html"
},
{
"type": "土地矿产",
"project_name": "南京市规划和自然资源局六合分局国有土地使用权挂牌出让公告(宁六工[2026]第5号)",
"amount_yuan": 664000,
"date": "2026-03-06",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/tdkc/072001/20260306/10586876.html"
},
{
"type": "土地矿产",
"project_name": "南京市规划和自然资源局六合分局国有土地使用权挂牌出让公告(宁六工[2026]第4号)",
"amount_yuan": 3320000,
"date": "2026-03-04",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/tdkc/072001/20260304/10561567.html"
},
{
"type": "土地矿产",
"project_name": "南京市规划和自然资源局国有土地使用权挂牌出让公告(宁出[2026]01号)",
"amount_yuan": 50000,
"date": "2026-02-28",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/tdkc/072001/20260228/10528773.html"
},
{
"type": "铁路航运",
"project_name": "【施工类】暂无最新项目信息",
"amount_yuan": 0,
"date": "2026-03-02",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/tlhy/073001/073001001/20240702/3a9b3413-3931-4d51-af72-78e11162ed74.html"
},
{
"type": "农村产权",
"project_name": "栖霞区龙潭街道马渡村新桥组12.01亩土地出租通知公告主要信息",
"amount_yuan": 28824,
"date": "2026-03-09",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/nccq/102001/20260309/4c99efee-79f1-e35e-e063-3345c40a7178.html"
},
{
"type": "农村产权",
"project_name": "栖霞区龙潭街道马渡村新春组18.93亩土地出租通知公告主要信息",
"amount_yuan": 18015,
"date": "2026-03-09",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/nccq/102001/20260309/4c99efee-79ed-e35e-e063-3345c40a7178.html"
},
{
"type": "农村产权",
"project_name": "栖霞区龙潭街道马渡村厢林组47.37亩土地出租通知公告主要信息",
"amount_yuan": 113688,
"date": "2026-03-09",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/nccq/102001/20260309/4c99efee-79e9-e35e-e063-3345c40a7178.html"
}
],
"total": 22
},
"id": "result-1773129230177-5tjno"
},
{
"taskId": "task-1710000000001",
"city": "南京市",
"scrapedAt": "2026-03-10T07:34:51.928Z",
"data": {
"results": [
{
"type": "房建市政",
"project_name": "星甸污水处理厂提标改造工程(一期)",
"amount_yuan": 5205328.1,
"date": "2026-03-10",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/fjsz/068001/068001002/20260310/84ce20ef-3ccf-414e-ad95-969296ae19a6.html"
},
{
"type": "工程货物",
"project_name": "【澄清公告】NO.2024G39地块建设项目 - 电梯设备采购及相关服务(暂估价)",
"amount_yuan": 4069576.31,
"date": "2026-03-07",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/gchw/070001/20260307/041138ff-870c-4240-b78c-f6861f242594.html"
},
{
"type": "交通水务",
"project_name": "【施工】八卦洲街道CBA3上坝子堤路等农村公路改善提升工程施工招标公告",
"amount_yuan": 4020000,
"date": "2026-03-04",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/jtsw/069001/069001001/20260304/3d25610f-239d-4e1c-9923-0d22820c711a.html"
},
{
"type": "政府采购",
"project_name": "JSZC-320111-JZCG-G2026-0013 政府采购公告",
"amount_yuan": 11130000,
"date": "2026-03-10",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/zfcg/067001/067001001/20260310/adb322241a19459cbaaf158e4ad87500.html"
},
{
"type": "产权交易",
"project_name": "2026320100CA00003 产权交易公告",
"amount_yuan": 2776800,
"date": "2026-04-20",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/gycq/071001/071001002/20260309/64b16240-07de-417c-8248-c44967c508f9.html"
},
{
"type": "土地矿产",
"project_name": "南京市规划和自然资源局六合分局国有土地使用权挂牌出让公告(宁六工[2026]第5号)",
"amount_yuan": 664000,
"date": "2026-03-06",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/tdkc/072001/20260306/10586876.html"
},
{
"type": "铁路航运",
"project_name": "【施工类】暂无最新项目信息",
"amount_yuan": 0,
"date": "2026-03-02",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/tlhy/073001/073001001/20240702/3a9b3413-3931-4d51-af72-78e11162ed74.html"
},
{
"type": "农村产权",
"project_name": "栖霞区龙潭街道马渡村新桥组12.01亩土地出租通知公告",
"amount_yuan": 9608,
"date": "2026-03-09",
"target_link": "https://njggzy.nanjing.gov.cn/njweb/nccq/102001/20260309/4c99efee-79f1-e35e-e063-3345c40a7178.html"
}
],
"total": 8
},
"id": "result-1773128091930-edg4p"
}
]

View File

@@ -49,7 +49,6 @@ async function fetchWithRetry(url, fetchOptions, retries = MAX_FETCH_RETRIES, lo
*/ */
async function createTask(prompt, options = {}) { async function createTask(prompt, options = {}) {
const baseUrl = options.baseUrl || DEFAULT_BASE_URL; const baseUrl = options.baseUrl || DEFAULT_BASE_URL;
const useBrowser = options.useBrowser ?? false;
const mode = normalizeMode(options.mode); const mode = normalizeMode(options.mode);
const taskId = generateTaskId(); const taskId = generateTaskId();
const logPrefix = options.logPrefix || '[Agent]'; const logPrefix = options.logPrefix || '[Agent]';
@@ -57,7 +56,7 @@ async function createTask(prompt, options = {}) {
const res = await fetchWithRetry(`${baseUrl}/agent/createTask`, { const res = await fetchWithRetry(`${baseUrl}/agent/createTask`, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ taskId, prompt, useBrowser, mode }), body: JSON.stringify({ taskId, prompt, mode }),
}, MAX_FETCH_RETRIES, logPrefix); }, MAX_FETCH_RETRIES, logPrefix);
if (!res.ok) { if (!res.ok) {
@@ -104,7 +103,6 @@ async function checkTask(taskId, options = {}) {
*/ */
export async function runAgentTask(prompt, options = {}) { export async function runAgentTask(prompt, options = {}) {
const baseUrl = options.baseUrl || DEFAULT_BASE_URL; const baseUrl = options.baseUrl || DEFAULT_BASE_URL;
const useBrowser = options.useBrowser ?? false;
const mode = normalizeMode(options.mode); const mode = normalizeMode(options.mode);
const pollInterval = options.pollInterval || DEFAULT_POLL_INTERVAL; const pollInterval = options.pollInterval || DEFAULT_POLL_INTERVAL;
const timeout = options.timeout || DEFAULT_TIMEOUT; const timeout = options.timeout || DEFAULT_TIMEOUT;
@@ -112,7 +110,7 @@ export async function runAgentTask(prompt, options = {}) {
console.log(`${logPrefix} 创建任务...`); console.log(`${logPrefix} 创建任务...`);
console.log(`${logPrefix} 使用 mode: ${mode}`); console.log(`${logPrefix} 使用 mode: ${mode}`);
const { taskId } = await createTask(prompt, { baseUrl, useBrowser, mode, logPrefix }); const { taskId } = await createTask(prompt, { baseUrl, mode, logPrefix });
console.log(`${logPrefix} 任务已创建: ${taskId}`); console.log(`${logPrefix} 任务已创建: ${taskId}`);
const startTime = Date.now(); const startTime = Date.now();

471
src/resultStore.js Normal file
View File

@@ -0,0 +1,471 @@
import Database from 'better-sqlite3';
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const DB_PATH =
process.env.APP_DB_PATH ||
process.env.RESULTS_DB_PATH ||
join(__dirname, '..', 'data', 'results.sqlite');
const CONFIG_PATH = join(__dirname, '..', 'config.json');
const MAX_RESULT_RECORDS = 500;
const DEFAULT_TASK_MODE = 'qwen3.5-plus';
const TASK_COLUMNS = ['id', 'city', 'plate_name', 'prompt', 'enabled', 'mode', 'created_at', 'updated_at'];
let db = null;
let initialized = false;
function clone(value) {
return JSON.parse(JSON.stringify(value));
}
function generateResultId() {
return `result-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
}
function generateTaskId() {
return `task-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
}
function getDefaultJsonConfig() {
return {
agent: {
baseUrl: '',
pollInterval: 3000,
timeout: 300000,
},
scheduler: {
enabled: false,
cronTime: '0 9 * * *',
threshold: 100000,
description: '',
timeRange: 'thisMonth',
},
email: {
smtpHost: '',
smtpPort: 587,
smtpUser: '',
smtpPass: '',
recipients: '',
},
};
}
function normalizeJsonConfig(input = {}) {
const defaults = getDefaultJsonConfig();
const agent = input.agent || {};
const scheduler = input.scheduler || {};
const email = input.email || {};
return {
agent: {
baseUrl: typeof agent.baseUrl === 'string' ? agent.baseUrl : defaults.agent.baseUrl,
pollInterval: Number.isFinite(agent.pollInterval) ? agent.pollInterval : defaults.agent.pollInterval,
timeout: Number.isFinite(agent.timeout) ? agent.timeout : defaults.agent.timeout,
},
scheduler: {
enabled: scheduler.enabled === true,
cronTime: typeof scheduler.cronTime === 'string' && scheduler.cronTime.trim()
? scheduler.cronTime
: defaults.scheduler.cronTime,
threshold: Number.isFinite(scheduler.threshold) ? scheduler.threshold : defaults.scheduler.threshold,
description: typeof scheduler.description === 'string' ? scheduler.description : defaults.scheduler.description,
timeRange: typeof scheduler.timeRange === 'string' && scheduler.timeRange.trim()
? scheduler.timeRange
: defaults.scheduler.timeRange,
},
email: {
smtpHost: typeof email.smtpHost === 'string' ? email.smtpHost : defaults.email.smtpHost,
smtpPort: Number.isFinite(email.smtpPort) ? email.smtpPort : defaults.email.smtpPort,
smtpUser: typeof email.smtpUser === 'string' ? email.smtpUser : defaults.email.smtpUser,
smtpPass: typeof email.smtpPass === 'string' ? email.smtpPass : defaults.email.smtpPass,
recipients: typeof email.recipients === 'string' ? email.recipients : defaults.email.recipients,
},
};
}
function normalizeTaskMode(value) {
if (typeof value === 'string' && value.trim()) return value.trim();
return DEFAULT_TASK_MODE;
}
function buildTaskRecord(task = {}) {
return {
id: task.id || generateTaskId(),
city: task.city || '',
plateName: task.plateName || '',
prompt: task.prompt || '',
enabled: task.enabled !== false,
mode: normalizeTaskMode(task.mode),
};
}
function buildResultRecord(result = {}) {
return {
id: result.id || generateResultId(),
taskId: result.taskId || null,
city: result.city || null,
scrapedAt: result.scrapedAt || new Date().toISOString(),
error: result.error || null,
data: result.data ?? null,
};
}
function parseTaskRow(row) {
return {
id: row.id,
city: row.city,
plateName: row.plate_name,
prompt: row.prompt,
enabled: row.enabled === 1,
mode: normalizeTaskMode(row.mode),
};
}
function parseResultRow(row) {
return {
id: row.id,
taskId: row.task_id,
city: row.city,
scrapedAt: row.scraped_at,
error: row.error,
data: row.data_json ? JSON.parse(row.data_json) : null,
};
}
function getDb() {
if (db) return db;
mkdirSync(dirname(DB_PATH), { recursive: true });
db = new Database(DB_PATH);
db.pragma('journal_mode = WAL');
return db;
}
function ensureSchema() {
getDb().exec(`
CREATE TABLE IF NOT EXISTS results (
id TEXT PRIMARY KEY,
task_id TEXT,
city TEXT,
scraped_at TEXT NOT NULL,
error TEXT,
data_json TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_results_scraped_at ON results (scraped_at DESC);
CREATE INDEX IF NOT EXISTS idx_results_city ON results (city);
CREATE INDEX IF NOT EXISTS idx_results_task_id ON results (task_id);
CREATE TABLE IF NOT EXISTS tasks (
id TEXT PRIMARY KEY,
city TEXT,
plate_name TEXT,
prompt TEXT,
enabled INTEGER NOT NULL DEFAULT 1,
mode TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
`);
}
function ensureTasksTableShape() {
const columns = getDb().prepare(`PRAGMA table_info(tasks)`).all();
const columnNames = columns.map((column) => column.name);
const hasLegacyBrowserColumn = columnNames.includes('use_browser');
const matchesExpectedShape =
columnNames.length === TASK_COLUMNS.length &&
TASK_COLUMNS.every((column, index) => columnNames[index] === column);
if (!hasLegacyBrowserColumn && matchesExpectedShape) return;
getDb().exec(`
BEGIN;
ALTER TABLE tasks RENAME TO tasks_legacy;
CREATE TABLE tasks (
id TEXT PRIMARY KEY,
city TEXT,
plate_name TEXT,
prompt TEXT,
enabled INTEGER NOT NULL DEFAULT 1,
mode TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
INSERT INTO tasks (id, city, plate_name, prompt, enabled, mode, created_at, updated_at)
SELECT
id,
city,
plate_name,
prompt,
COALESCE(enabled, 1),
COALESCE(mode, '${DEFAULT_TASK_MODE}'),
COALESCE(created_at, datetime('now')),
COALESCE(updated_at, datetime('now'))
FROM tasks_legacy;
DROP TABLE tasks_legacy;
COMMIT;
`);
}
function trimResults(limit = MAX_RESULT_RECORDS) {
getDb().prepare(`
DELETE FROM results
WHERE id NOT IN (
SELECT id
FROM results
ORDER BY scraped_at DESC, rowid DESC
LIMIT ?
)
`).run(limit);
}
function readJsonIfExists(filePath) {
if (!existsSync(filePath)) return null;
return JSON.parse(readFileSync(filePath, 'utf-8'));
}
function stripTasksFromConfig(config) {
if (!config || typeof config !== 'object') return getDefaultJsonConfig();
const { agent, scheduler, email } = config;
return normalizeJsonConfig({ agent, scheduler, email });
}
function ensureJsonConfigExists() {
if (existsSync(CONFIG_PATH)) return;
writeFileSync(CONFIG_PATH, JSON.stringify(getDefaultJsonConfig(), null, 2), 'utf-8');
}
function queryBaseRows({ city, taskId }) {
const clauses = [];
const params = [];
if (city) {
clauses.push('city = ?');
params.push(city);
}
if (taskId) {
clauses.push('task_id = ?');
params.push(taskId);
}
const whereSql = clauses.length > 0 ? `WHERE ${clauses.join(' AND ')}` : '';
const sql = `
SELECT id, task_id, city, scraped_at, error, data_json
FROM results
${whereSql}
ORDER BY scraped_at DESC, rowid DESC
`;
return getDb().prepare(sql).all(...params).map(parseResultRow);
}
function matchSection(record, section) {
if (!section) return true;
if (record.section === section || record.subsection === section) return true;
const items = record.data?.results || [];
return items.some((item) => item.section === section || item.subsection === section);
}
function matchType(record, type) {
if (!type) return true;
if (record.type === type) return true;
const items = record.data?.results || [];
return items.some((item) => item.type === type);
}
export function initResultsStore() {
if (initialized) return;
ensureSchema();
ensureTasksTableShape();
ensureJsonConfigExists();
initialized = true;
}
export function loadConfig() {
initResultsStore();
const jsonConfig = normalizeJsonConfig(readJsonIfExists(CONFIG_PATH) || getDefaultJsonConfig());
return {
...clone(jsonConfig),
tasks: listTasks(),
};
}
export function saveConfig(config) {
initResultsStore();
const jsonConfig = stripTasksFromConfig(config);
writeFileSync(CONFIG_PATH, JSON.stringify(jsonConfig, null, 2), 'utf-8');
return {
...clone(jsonConfig),
tasks: listTasks(),
};
}
export function listTasks() {
initResultsStore();
return getDb()
.prepare(`
SELECT id, city, plate_name, prompt, enabled, mode
FROM tasks
ORDER BY rowid DESC
`)
.all()
.map(parseTaskRow);
}
export function getTaskById(id) {
initResultsStore();
const row = getDb()
.prepare(`
SELECT id, city, plate_name, prompt, enabled, mode
FROM tasks
WHERE id = ?
`)
.get(id);
return row ? parseTaskRow(row) : null;
}
export function createTask(task) {
initResultsStore();
const record = buildTaskRecord(task);
getDb().prepare(`
INSERT INTO tasks (id, city, plate_name, prompt, enabled, mode, updated_at)
VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
`).run(
record.id,
record.city,
record.plateName,
record.prompt,
record.enabled ? 1 : 0,
record.mode,
);
return record;
}
export function updateTask(id, patch) {
initResultsStore();
const current = getTaskById(id);
if (!current) return null;
const next = buildTaskRecord({ ...current, ...patch, id });
getDb().prepare(`
UPDATE tasks
SET city = ?,
plate_name = ?,
prompt = ?,
enabled = ?,
mode = ?,
updated_at = datetime('now')
WHERE id = ?
`).run(
next.city,
next.plateName,
next.prompt,
next.enabled ? 1 : 0,
next.mode,
id,
);
return next;
}
export function deleteTaskById(id) {
initResultsStore();
const result = getDb().prepare('DELETE FROM tasks WHERE id = ?').run(id);
return result.changes > 0;
}
export function appendResult(result) {
initResultsStore();
const record = buildResultRecord(result);
getDb().prepare(`
INSERT INTO results (id, task_id, city, scraped_at, error, data_json)
VALUES (?, ?, ?, ?, ?, ?)
`).run(
record.id,
record.taskId,
record.city,
record.scrapedAt,
record.error,
record.data === null ? null : JSON.stringify(record.data),
);
trimResults();
return record;
}
export function listResults({ city, section, type, taskId, page = 1, pageSize = 20 } = {}) {
initResultsStore();
let results = queryBaseRows({ city, taskId });
if (section) results = results.filter((record) => matchSection(record, section));
if (type) results = results.filter((record) => matchType(record, type));
const normalizedPage = Math.max(1, parseInt(page, 10) || 1);
const normalizedPageSize = Math.max(1, parseInt(pageSize, 10) || 20);
const start = (normalizedPage - 1) * normalizedPageSize;
return {
total: results.length,
page: normalizedPage,
pageSize: normalizedPageSize,
data: results.slice(start, start + normalizedPageSize),
};
}
export function deleteResultById(id) {
initResultsStore();
const result = getDb().prepare('DELETE FROM results WHERE id = ?').run(id);
return result.changes > 0;
}
export function clearResults() {
initResultsStore();
getDb().prepare('DELETE FROM results').run();
}
export function getResultFilters() {
initResultsStore();
const rows = queryBaseRows({});
const cities = [...new Set(rows.map((row) => row.city).filter(Boolean))];
const sections = new Set();
const types = new Set();
for (const row of rows) {
if (row.section) sections.add(row.section);
if (row.subsection) sections.add(row.subsection);
if (row.type) types.add(row.type);
for (const item of row.data?.results || []) {
if (item.section) sections.add(item.section);
if (item.subsection) sections.add(item.subsection);
if (item.type) types.add(item.type);
}
}
return {
cities,
sections: [...sections],
types: [...types],
};
}
export function getResultsDbPath() {
return DB_PATH;
}

View File

@@ -1,208 +1,177 @@
import 'dotenv/config'; import 'dotenv/config';
import cron from 'node-cron'; import cron from 'node-cron';
import { readFileSync, writeFileSync, existsSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { sendScraperResultsEmail } from './emailService.js'; import { sendScraperResultsEmail } from './emailService.js';
import { runAgentTask } from './agentService.js'; import { runAgentTask } from './agentService.js';
import { initResultsStore, loadConfig, appendResult } from './resultStore.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const RESULTS_PATH = join(__dirname, '..', 'results.json');
const DEFAULT_TASK_MODE = 'qwen3.5-plus'; const DEFAULT_TASK_MODE = 'qwen3.5-plus';
let currentScheduledTask = null;
function normalizeTaskMode(value) { function normalizeTaskMode(value) {
if (typeof value === 'string' && value.trim()) return value.trim(); if (typeof value === 'string' && value.trim()) return value.trim();
return DEFAULT_TASK_MODE; return DEFAULT_TASK_MODE;
} }
function loadConfig() {
try {
const configPath = join(__dirname, '..', 'config.json');
return JSON.parse(readFileSync(configPath, 'utf-8'));
} catch (error) {
console.error('加载配置文件失败:', error.message);
return null;
}
}
// ========== 结果存取 ==========
function readResults() {
if (!existsSync(RESULTS_PATH)) return [];
try {
return JSON.parse(readFileSync(RESULTS_PATH, 'utf-8'));
} catch (e) {
return [];
}
}
function saveResults(results) {
writeFileSync(RESULTS_PATH, JSON.stringify(results, null, 2), 'utf-8');
}
function appendResult(result) {
const results = readResults();
results.unshift({ ...result, id: `result-${Date.now()}-${Math.random().toString(36).slice(2, 7)}` });
if (results.length > 500) results.splice(500);
saveResults(results);
}
// ========== 任务执行 ==========
async function runTask(task, agentCfg) { async function runTask(task, agentCfg) {
const useBrowser = typeof task.useBrowser === 'boolean' ? task.useBrowser : agentCfg.useBrowser;
const mode = normalizeTaskMode(task.mode); const mode = normalizeTaskMode(task.mode);
console.log(`[定时任务][Agent] ${task.city}:开始执行`);
console.log(`[定时任务][Agent] ${task.city}mode=${mode}`); console.log(`[Scheduler][Agent] ${task.city}: start`);
console.log(`[Scheduler][Agent] ${task.city}: mode=${mode}`);
const { results } = await runAgentTask(task.prompt, { const { results } = await runAgentTask(task.prompt, {
baseUrl: agentCfg.baseUrl, baseUrl: agentCfg.baseUrl,
useBrowser,
mode, mode,
pollInterval: agentCfg.pollInterval, pollInterval: agentCfg.pollInterval,
timeout: agentCfg.timeout, timeout: agentCfg.timeout,
logPrefix: `[定时任务][Agent][${task.city}]`, logPrefix: `[Scheduler][Agent][${task.city}]`,
}); });
console.log(`[定时任务][Agent] ${task.city}:获取到 ${results.length} 条结果`);
const record = { console.log(`[Scheduler][Agent] ${task.city}: ${results.length} results`);
return appendResult({
taskId: task.id, taskId: task.id,
city: task.city, city: task.city,
scrapedAt: new Date().toISOString(), scrapedAt: new Date().toISOString(),
data: { results, total: results.length }, data: { results, total: results.length },
}; });
appendResult(record);
return record;
} }
// ========== 定时任务执行函数 ==========
async function executeScheduledTask(config) { async function executeScheduledTask(config) {
try { try {
console.log('========================================'); console.log('========================================');
console.log('定时任务开始执行'); console.log('Scheduler started');
console.log('执行时间:', new Date().toLocaleString('zh-CN')); console.log('Time:', new Date().toLocaleString('zh-CN'));
console.log('========================================'); console.log('========================================');
const tasks = (config.tasks || []).filter(t => t.enabled); const tasks = (config.tasks || []).filter((task) => task.enabled);
const agentCfg = config.agent || {}; const agentCfg = config.agent || {};
if (tasks.length === 0) { if (tasks.length === 0) {
console.log('没有已启用的任务,跳过'); console.log('No enabled tasks, skip');
return; return;
} }
console.log(` ${tasks.length} 个已启用的任务`); console.log(`Enabled tasks: ${tasks.length}`);
const results = []; const results = [];
for (const task of tasks) { for (const task of tasks) {
try { try {
console.log(`\n---------- 任务: ${task.city} ----------`); console.log(`\n---------- Task: ${task.city} ----------`);
const r = await runTask(task, agentCfg); const record = await runTask(task, agentCfg);
results.push(r); results.push(record);
console.log(`✓ 执行成功`); console.log('Task completed');
} catch (err) { } catch (error) {
console.error(`✗ 执行失败: ${err.message}`); console.error(`Task failed: ${error.message}`);
const errRecord = { const errorRecord = appendResult({
taskId: task.id, taskId: task.id,
city: task.city, city: task.city,
scrapedAt: new Date().toISOString(), scrapedAt: new Date().toISOString(),
error: err.message, error: error.message,
data: null, data: null,
}; });
appendResult(errRecord); results.push(errorRecord);
results.push(errRecord);
} }
} }
const successCount = results.filter(r => !r.error).length; const successCount = results.filter((item) => !item.error).length;
const failCount = results.filter(r => r.error).length; const failCount = results.filter((item) => item.error).length;
console.log(`\n========== 执行完成 ==========`); console.log('\n========== Scheduler finished ==========');
console.log(`成功: ${successCount},失败: ${failCount}`); console.log(`Success: ${successCount}, Failed: ${failCount}`);
if (successCount === 0) { if (successCount === 0) {
console.log('没有成功的结果,不发送邮件'); console.log('No successful results, skip email');
return; return;
} }
if (config.email?.smtpHost && config.email?.smtpUser) { if (config.email?.smtpHost && config.email?.smtpUser) {
console.log('\n正在发送结果邮件...'); console.log('\nSending email...');
try { try {
const emailResult = await sendScraperResultsEmail(config.email, results); const emailResult = await sendScraperResultsEmail(config.email, results);
console.log('邮件发送成功! MessageId:', emailResult.messageId); console.log('Email sent:', emailResult.messageId);
} catch (emailErr) { } catch (error) {
console.error('邮件发送失败:', emailErr.message); console.error('Email failed:', error.message);
} }
} else { } else {
console.log('邮件配置不完整,跳过邮件发送'); console.log('Email config incomplete, skip email');
} }
console.log('========================================'); console.log('========================================');
} catch (error) { } catch (error) {
console.error('========================================'); console.error('========================================');
console.error('定时任务执行失败:', error.message); console.error('Scheduler failed:', error.message);
console.error(error.stack); console.error(error.stack);
console.error('========================================'); console.error('========================================');
} }
} }
// 存储当前的定时任务
let currentScheduledTask = null;
export function initScheduler() { export function initScheduler() {
initResultsStore();
const config = loadConfig(); const config = loadConfig();
if (!config) { console.error('无法启动定时任务: 配置文件加载失败'); return; } if (!config.scheduler?.enabled) {
if (!config.scheduler?.enabled) { console.log('定时任务已禁用'); return; } console.log('Scheduler disabled');
return;
}
const cronTime = config.scheduler.cronTime || '0 9 * * *'; const cronTime = config.scheduler.cronTime || '0 9 * * *';
const enabledCount = (config.tasks || []).filter(t => t.enabled).length; const enabledCount = (config.tasks || []).filter((task) => task.enabled).length;
console.log('========================================');
console.log('定时任务已启动,执行计划:', cronTime);
console.log(`已启用的任务: ${enabledCount}`);
if (config.email?.recipients) console.log('收件人:', config.email.recipients);
console.log('========================================');
if (currentScheduledTask) { currentScheduledTask.stop(); } console.log('========================================');
console.log('Scheduler enabled:', cronTime);
currentScheduledTask = cron.schedule(cronTime, () => { console.log(`Enabled tasks: ${enabledCount}`);
const latestConfig = loadConfig(); if (config.email?.recipients) {
if (latestConfig) { console.log('Recipients:', config.email.recipients);
executeScheduledTask(latestConfig);
} }
}, { timezone: 'Asia/Shanghai' }); console.log('========================================');
if (currentScheduledTask) {
currentScheduledTask.stop();
}
currentScheduledTask = cron.schedule(
cronTime,
() => {
executeScheduledTask(loadConfig());
},
{ timezone: 'Asia/Shanghai' },
);
} }
export function reloadScheduler() { export function reloadScheduler() {
console.log('重新加载定时任务配置...'); console.log('Reloading scheduler...');
if (currentScheduledTask) { currentScheduledTask.stop(); currentScheduledTask = null; } if (currentScheduledTask) {
currentScheduledTask.stop();
currentScheduledTask = null;
}
initScheduler(); initScheduler();
} }
export function stopScheduler() { export function stopScheduler() {
if (currentScheduledTask) { if (!currentScheduledTask) return false;
currentScheduledTask.stop(); currentScheduledTask = null;
console.log('定时任务已停止'); return true; currentScheduledTask.stop();
} currentScheduledTask = null;
return false; console.log('Scheduler stopped');
return true;
} }
export function getSchedulerStatus() { export function getSchedulerStatus() {
const config = loadConfig(); const config = loadConfig();
const enabledTasks = (config?.tasks || []).filter(t => t.enabled).length; const enabledTasks = (config.tasks || []).filter((task) => task.enabled).length;
return { return {
isRunning: currentScheduledTask !== null, isRunning: currentScheduledTask !== null,
enabledTasks, enabledTasks,
config: config ? { config: {
enabled: config.scheduler?.enabled || false, enabled: config.scheduler?.enabled || false,
cronTime: config.scheduler?.cronTime || '0 9 * * *', cronTime: config.scheduler?.cronTime || '0 9 * * *',
description: config.scheduler?.description || '', description: config.scheduler?.description || '',
} : null, },
}; };
} }
export async function runTaskNow() { export async function runTaskNow() {
const config = loadConfig(); initResultsStore();
if (!config) throw new Error('配置文件加载失败'); await executeScheduledTask(loadConfig());
await executeScheduledTask(config);
} }

View File

@@ -1,263 +1,272 @@
import 'dotenv/config'; import 'dotenv/config';
import express from 'express'; import express from 'express';
import cors from 'cors'; import cors from 'cors';
import { readFileSync, writeFileSync, existsSync } from 'fs'; import {
import { fileURLToPath } from 'url'; initResultsStore,
import { dirname, join } from 'path'; loadConfig,
saveConfig,
listTasks,
getTaskById,
createTask,
updateTask,
deleteTaskById,
listResults,
deleteResultById,
clearResults,
getResultFilters,
appendResult,
} from './resultStore.js';
import { initScheduler, runTaskNow, reloadScheduler, getSchedulerStatus } from './scheduler.js'; import { initScheduler, runTaskNow, reloadScheduler, getSchedulerStatus } from './scheduler.js';
import { runAgentTask } from './agentService.js'; import { runAgentTask } from './agentService.js';
const app = express(); const app = express();
const PORT = process.env.PORT || 5000; const PORT = process.env.PORT || 5000;
const DEFAULT_TASK_MODE = 'qwen3.5-plus';
const MASKED_PASSWORD = '***已配置***';
app.use(cors()); app.use(cors());
app.use(express.json({ limit: '50mb' })); app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ limit: '50mb', extended: true })); app.use(express.urlencoded({ limit: '50mb', extended: true }));
app.use(express.static('public')); app.use(express.static('public'));
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const CONFIG_PATH = join(__dirname, '..', 'config.json');
const RESULTS_PATH = join(__dirname, '..', 'results.json');
const DEFAULT_TASK_MODE = 'qwen3.5-plus';
function readConfig() {
return JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'));
}
function saveConfig(cfg) {
writeFileSync(CONFIG_PATH, JSON.stringify(cfg, null, 2), 'utf-8');
}
function normalizeUseBrowser(value) {
return value === true || value === 'true';
}
function normalizeTaskMode(value) { function normalizeTaskMode(value) {
if (typeof value === 'string' && value.trim()) return value.trim(); if (typeof value === 'string' && value.trim()) return value.trim();
return DEFAULT_TASK_MODE; return DEFAULT_TASK_MODE;
} }
// ========== 抓取结果存取 ========== function buildTaskPayload(body = {}, { partial = false } = {}) {
const payload = {};
function readResults() { if (!partial || Object.prototype.hasOwnProperty.call(body, 'city')) {
if (!existsSync(RESULTS_PATH)) return []; payload.city = body.city || '';
try {
return JSON.parse(readFileSync(RESULTS_PATH, 'utf-8'));
} catch (e) {
return [];
}
} }
function saveResults(results) { if (!partial || Object.prototype.hasOwnProperty.call(body, 'plateName')) {
writeFileSync(RESULTS_PATH, JSON.stringify(results, null, 2), 'utf-8'); payload.plateName = body.plateName || '';
} }
function appendResult(result) { if (!partial || Object.prototype.hasOwnProperty.call(body, 'prompt')) {
const results = readResults(); payload.prompt = body.prompt || '';
results.unshift({ ...result, id: `result-${Date.now()}-${Math.random().toString(36).slice(2, 7)}` });
if (results.length > 500) results.splice(500);
saveResults(results);
} }
// 查询结果(支持分页与筛选) if (!partial || Object.prototype.hasOwnProperty.call(body, 'enabled')) {
app.get('/api/results', (req, res) => { payload.enabled = body.enabled !== false;
try {
const { city, type, page = 1, pageSize = 20, taskId } = req.query;
let results = readResults();
if (city) results = results.filter(r => r.city === city);
if (type) results = results.filter(r => {
const items = r.data?.results || [];
return items.some(item => item.type === type);
});
if (taskId) results = results.filter(r => r.taskId === taskId);
const total = results.length;
const start = (parseInt(page) - 1) * parseInt(pageSize);
const data = results.slice(start, start + parseInt(pageSize));
res.json({ success: true, total, page: parseInt(page), pageSize: parseInt(pageSize), data });
} catch (e) {
res.status(500).json({ success: false, error: e.message });
} }
});
// 删除单条结果 if (!partial || Object.prototype.hasOwnProperty.call(body, 'mode')) {
app.delete('/api/results/:id', (req, res) => { payload.mode = normalizeTaskMode(body.mode);
try {
const results = readResults();
const before = results.length;
const updated = results.filter(r => r.id !== req.params.id);
if (updated.length === before) return res.status(404).json({ success: false, error: '未找到' });
saveResults(updated);
res.json({ success: true });
} catch (e) {
res.status(500).json({ success: false, error: e.message });
} }
});
// 清空所有结果 return payload;
app.delete('/api/results', (_req, res) => {
try {
saveResults([]);
res.json({ success: true, message: '已清空所有结果' });
} catch (e) {
res.status(500).json({ success: false, error: e.message });
} }
});
// 获取结果的筛选选项 function maskConfigSecrets(config) {
app.get('/api/results/filters', (req, res) => { const next = { ...config };
try { if (config.email) {
const results = readResults(); next.email = {
const cities = [...new Set(results.map(r => r.city).filter(Boolean))]; ...config.email,
const types = new Set(); smtpPass: config.email.smtpPass ? MASKED_PASSWORD : '',
for (const r of results) {
for (const item of (r.data?.results || [])) {
if (item.type) types.add(item.type);
}
}
res.json({ success: true, data: { cities, types: [...types] } });
} catch (e) {
res.status(500).json({ success: false, error: e.message });
}
});
// ========== 任务配置 CRUD ==========
app.get('/api/tasks', (req, res) => {
try {
const cfg = readConfig();
const tasks = (cfg.tasks || []).map(t => ({ ...t, mode: normalizeTaskMode(t.mode) }));
res.json({ success: true, data: tasks });
} catch (e) {
res.status(500).json({ success: false, error: e.message });
}
});
app.post('/api/tasks', (req, res) => {
try {
const cfg = readConfig();
if (!cfg.tasks) cfg.tasks = [];
const item = {
id: `task-${Date.now()}`,
city: req.body.city || '',
prompt: req.body.prompt || '',
enabled: req.body.enabled !== false,
useBrowser: normalizeUseBrowser(req.body.useBrowser),
mode: normalizeTaskMode(req.body.mode),
}; };
cfg.tasks.push(item);
saveConfig(cfg);
res.json({ success: true, data: item });
} catch (e) {
res.status(500).json({ success: false, error: e.message });
} }
}); return next;
}
app.put('/api/tasks/:id', (req, res) => { function mergeConfigWithExistingSecrets(incoming = {}) {
try { const current = loadConfig();
const cfg = readConfig(); const next = {
const idx = (cfg.tasks || []).findIndex(t => t.id === req.params.id); ...current,
if (idx === -1) return res.status(404).json({ success: false, error: '未找到该配置' }); ...incoming,
const patch = { ...req.body }; agent: { ...(current.agent || {}), ...(incoming.agent || {}) },
if (Object.prototype.hasOwnProperty.call(patch, 'useBrowser')) { scheduler: { ...(current.scheduler || {}), ...(incoming.scheduler || {}) },
patch.useBrowser = normalizeUseBrowser(patch.useBrowser); email: { ...(current.email || {}), ...(incoming.email || {}) },
} };
if (Object.prototype.hasOwnProperty.call(patch, 'mode')) {
patch.mode = normalizeTaskMode(patch.mode);
}
cfg.tasks[idx] = { ...cfg.tasks[idx], ...patch, id: req.params.id };
saveConfig(cfg);
res.json({ success: true, data: cfg.tasks[idx] });
} catch (e) {
res.status(500).json({ success: false, error: e.message });
}
});
app.delete('/api/tasks/:id', (req, res) => { if (next.email?.smtpPass === MASKED_PASSWORD) {
try { next.email.smtpPass = current.email?.smtpPass || '';
const cfg = readConfig();
const before = (cfg.tasks || []).length;
cfg.tasks = (cfg.tasks || []).filter(t => t.id !== req.params.id);
if (cfg.tasks.length === before) return res.status(404).json({ success: false, error: '未找到' });
saveConfig(cfg);
res.json({ success: true });
} catch (e) {
res.status(500).json({ success: false, error: e.message });
} }
});
// ========== 任务执行(异步 + 串行锁) ========== return next;
}
let isRunning = false; let isRunning = false;
let runningStatus = null; // { taskId, city, startTime, current, total, finished, error } let runningStatus = null;
async function runTask(task) { async function runTask(task) {
const cfg = readConfig(); const config = loadConfig();
const agentCfg = cfg.agent || {}; const agentCfg = config.agent || {};
const useBrowser = typeof task.useBrowser === 'boolean' ? task.useBrowser : agentCfg.useBrowser;
const mode = normalizeTaskMode(task.mode); const mode = normalizeTaskMode(task.mode);
console.log(`[Agent] ${task.city}:开始执行`); console.log(`[Agent] ${task.city}: start`);
console.log(`[Agent] ${task.city}mode=${mode}`); console.log(`[Agent] ${task.city}: mode=${mode}`);
const { results } = await runAgentTask(task.prompt, { const { results } = await runAgentTask(task.prompt, {
baseUrl: agentCfg.baseUrl, baseUrl: agentCfg.baseUrl,
useBrowser,
mode, mode,
pollInterval: agentCfg.pollInterval, pollInterval: agentCfg.pollInterval,
timeout: agentCfg.timeout, timeout: agentCfg.timeout,
logPrefix: `[Agent][${task.city}]`, logPrefix: `[Agent][${task.city}]`,
}); });
const record = { return appendResult({
taskId: task.id, taskId: task.id,
city: task.city, city: task.city,
scrapedAt: new Date().toISOString(), scrapedAt: new Date().toISOString(),
data: { results, total: results.length }, data: { results, total: results.length },
}; });
appendResult(record);
return record;
} }
// 后台执行单个任务
function runTaskInBackground(task) { function runTaskInBackground(task) {
runningStatus = { taskId: task.id, city: task.city, startTime: Date.now(), current: 0, total: 1, finished: false, error: null }; runningStatus = {
runTask(task).then(record => { taskId: task.id,
city: task.city,
startTime: Date.now(),
current: 0,
total: 1,
finished: false,
error: null,
};
runTask(task)
.then((record) => {
runningStatus = { ...runningStatus, finished: true, result: record, current: 1 }; runningStatus = { ...runningStatus, finished: true, result: record, current: 1 };
}).catch(err => { })
console.error('任务执行失败:', err.message); .catch((error) => {
runningStatus = { ...runningStatus, finished: true, error: err.message, current: 1 }; console.error('任务执行失败:', error.message);
}).finally(() => { runningStatus = { ...runningStatus, finished: true, error: error.message, current: 1 };
})
.finally(() => {
isRunning = false; isRunning = false;
}); });
} }
// 后台执行批量任务
function runTasksInBackground(tasks) { function runTasksInBackground(tasks) {
runningStatus = { taskId: null, city: null, startTime: Date.now(), current: 0, total: tasks.length, finished: false, error: null, results: [] }; runningStatus = {
taskId: null,
city: null,
startTime: Date.now(),
current: 0,
total: tasks.length,
finished: false,
error: null,
results: [],
};
(async () => { (async () => {
for (const task of tasks) { for (const task of tasks) {
runningStatus = { ...runningStatus, taskId: task.id, city: task.city }; runningStatus = { ...runningStatus, taskId: task.id, city: task.city };
try { try {
const r = await runTask(task); const record = await runTask(task);
runningStatus.results.push(r); runningStatus.results.push(record);
} catch (err) { } catch (error) {
const errRecord = { taskId: task.id, city: task.city, scrapedAt: new Date().toISOString(), error: err.message, data: null }; const errorRecord = appendResult({
appendResult(errRecord); taskId: task.id,
runningStatus.results.push(errRecord); city: task.city,
scrapedAt: new Date().toISOString(),
error: error.message,
data: null,
});
runningStatus.results.push(errorRecord);
} }
runningStatus.current++;
runningStatus.current += 1;
} }
runningStatus.finished = true; runningStatus.finished = true;
})().catch(err => { })()
runningStatus = { ...runningStatus, finished: true, error: err.message }; .catch((error) => {
}).finally(() => { runningStatus = { ...runningStatus, finished: true, error: error.message };
})
.finally(() => {
isRunning = false; isRunning = false;
}); });
} }
// 查询运行状态 app.get('/api/results', (req, res) => {
try {
const { city, section, type, page = 1, pageSize = 20, taskId } = req.query;
const result = listResults({ city, section, type, page, pageSize, taskId });
res.json({ success: true, ...result });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
app.delete('/api/results/:id', (req, res) => {
try {
const deleted = deleteResultById(req.params.id);
if (!deleted) {
return res.status(404).json({ success: false, error: '未找到结果' });
}
res.json({ success: true });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
app.delete('/api/results', (_req, res) => {
try {
clearResults();
res.json({ success: true, message: '已清空所有结果' });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
app.get('/api/results/filters', (_req, res) => {
try {
res.json({ success: true, data: getResultFilters() });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
app.get('/api/tasks', (_req, res) => {
try {
res.json({ success: true, data: listTasks() });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
app.post('/api/tasks', (req, res) => {
try {
const task = createTask(buildTaskPayload(req.body));
res.json({ success: true, data: task });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
app.put('/api/tasks/:id', (req, res) => {
try {
const task = updateTask(req.params.id, buildTaskPayload(req.body, { partial: true }));
if (!task) {
return res.status(404).json({ success: false, error: '未找到该任务' });
}
res.json({ success: true, data: task });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
app.delete('/api/tasks/:id', (req, res) => {
try {
const deleted = deleteTaskById(req.params.id);
if (!deleted) {
return res.status(404).json({ success: false, error: '未找到该任务' });
}
res.json({ success: true });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
app.get('/api/tasks/status', (_req, res) => { app.get('/api/tasks/status', (_req, res) => {
if (!runningStatus) return res.json({ success: true, data: { isRunning: false } }); if (!runningStatus) {
return res.json({ success: true, data: { isRunning: false } });
}
const elapsed = Math.round((Date.now() - runningStatus.startTime) / 1000); const elapsed = Math.round((Date.now() - runningStatus.startTime) / 1000);
res.json({ res.json({
success: true, success: true,
@@ -269,32 +278,46 @@ app.get('/api/tasks/status', (_req, res) => {
total: runningStatus.total, total: runningStatus.total,
finished: runningStatus.finished, finished: runningStatus.finished,
error: runningStatus.error, error: runningStatus.error,
results: runningStatus.finished ? (runningStatus.results || (runningStatus.result ? [runningStatus.result] : [])) : undefined, results: runningStatus.finished
} ? (runningStatus.results || (runningStatus.result ? [runningStatus.result] : []))
: undefined,
},
}); });
}); });
// 运行单个任务(立即返回)
app.post('/api/tasks/:id/run', (req, res) => { app.post('/api/tasks/:id/run', (req, res) => {
if (isRunning) return res.status(409).json({ success: false, error: '有任务正在运行中,请等待完成后再试' }); if (isRunning) {
const cfg = readConfig(); return res.status(409).json({ success: false, error: '当前已有任务在运行,请稍后再试' });
const task = (cfg.tasks || []).find(t => t.id === req.params.id); }
if (!task) return res.status(404).json({ success: false, error: '未找到该配置' });
try {
const task = getTaskById(req.params.id);
if (!task) {
return res.status(404).json({ success: false, error: '未找到该任务' });
}
isRunning = true; isRunning = true;
runTaskInBackground(task); runTaskInBackground(task);
res.json({ success: true, message: `任务${task.city}已开始执行` }); res.json({ success: true, message: `任务${task.city}已开始执行` });
} catch (error) {
isRunning = false;
res.status(500).json({ success: false, error: error.message });
}
}); });
// 批量运行任务(立即返回)
app.post('/api/tasks/run', (req, res) => { app.post('/api/tasks/run', (req, res) => {
if (isRunning) return res.status(409).json({ success: false, error: '有任务正在运行中,请等待完成后再试' }); if (isRunning) {
const cfg = readConfig(); return res.status(409).json({ success: false, error: '当前已有任务在运行,请稍后再试' });
let tasks = cfg.tasks || []; }
if (req.body.ids && req.body.ids.length > 0) { try {
tasks = tasks.filter(t => req.body.ids.includes(t.id)); let tasks = listTasks();
if (Array.isArray(req.body?.ids) && req.body.ids.length > 0) {
const idSet = new Set(req.body.ids);
tasks = tasks.filter((task) => idSet.has(task.id));
} else { } else {
tasks = tasks.filter(t => t.enabled); tasks = tasks.filter((task) => task.enabled);
} }
if (tasks.length === 0) { if (tasks.length === 0) {
@@ -304,74 +327,70 @@ app.post('/api/tasks/run', (req, res) => {
isRunning = true; isRunning = true;
runTasksInBackground(tasks); runTasksInBackground(tasks);
res.json({ success: true, message: `${tasks.length} 个任务已开始执行` }); res.json({ success: true, message: `${tasks.length} 个任务已开始执行` });
} catch (error) {
isRunning = false;
res.status(500).json({ success: false, error: error.message });
}
}); });
// ========== 配置管理 ========== app.get('/api/config', (_req, res) => {
app.get('/api/config', (req, res) => {
try { try {
const cfg = readConfig(); res.json({ success: true, data: maskConfigSecrets(loadConfig()) });
if (cfg.email?.smtpPass) cfg.email.smtpPass = '***已配置***'; } catch (error) {
res.json({ success: true, data: cfg }); res.status(500).json({ success: false, error: error.message });
} catch (e) {
res.status(500).json({ success: false, error: e.message });
} }
}); });
app.post('/api/config', (req, res) => { app.post('/api/config', (req, res) => {
try { try {
const newCfg = req.body; saveConfig(mergeConfigWithExistingSecrets(req.body));
const oldCfg = readConfig();
if (newCfg.email?.smtpPass === '***已配置***') {
newCfg.email.smtpPass = oldCfg.email?.smtpPass || '';
}
saveConfig(newCfg);
reloadScheduler(); reloadScheduler();
res.json({ success: true, message: '配置已保存' }); res.json({ success: true, message: '配置已保存' });
} catch (e) { } catch (error) {
res.status(500).json({ success: false, error: e.message }); res.status(500).json({ success: false, error: error.message });
} }
}); });
// ========== 邮件 ==========
app.post('/api/send-email', async (req, res) => { app.post('/api/send-email', async (req, res) => {
try { try {
const { emailConfig, report } = req.body; const { emailConfig, report } = req.body;
if (!emailConfig?.smtpHost || !emailConfig?.smtpUser || !emailConfig?.smtpPass) if (!emailConfig?.smtpHost || !emailConfig?.smtpUser || !emailConfig?.smtpPass) {
return res.status(400).json({ success: false, error: '邮件配置不完整' }); return res.status(400).json({ success: false, error: '邮件配置不完整' });
if (!emailConfig.recipients?.trim()) }
if (!emailConfig.recipients?.trim()) {
return res.status(400).json({ success: false, error: '请指定收件人' }); return res.status(400).json({ success: false, error: '请指定收件人' });
if (!report) }
if (!report) {
return res.status(400).json({ success: false, error: '没有报告数据' }); return res.status(400).json({ success: false, error: '没有报告数据' });
}
const { sendReportEmail } = await import('./emailService.js'); const { sendReportEmail } = await import('./emailService.js');
const result = await sendReportEmail(emailConfig, report); const result = await sendReportEmail(emailConfig, report);
res.json({ success: true, message: '邮件发送成功', messageId: result.messageId }); res.json({ success: true, message: '邮件发送成功', messageId: result.messageId });
} catch (e) { } catch (error) {
res.status(500).json({ success: false, error: e.message }); res.status(500).json({ success: false, error: error.message });
} }
}); });
// ========== 定时任务 ========== app.get('/api/scheduler/status', (_req, res) => {
app.get('/api/scheduler/status', (req, res) => {
try { try {
res.json({ success: true, data: getSchedulerStatus() }); res.json({ success: true, data: getSchedulerStatus() });
} catch (e) { } catch (error) {
res.status(500).json({ success: false, error: e.message }); res.status(500).json({ success: false, error: error.message });
} }
}); });
app.post('/api/run-scheduled-task', (req, res) => { app.post('/api/run-scheduled-task', (_req, res) => {
try { try {
runTaskNow().catch(err => console.error('定时任务执行失败:', err)); runTaskNow().catch((error) => console.error('定时任务执行失败:', error));
res.json({ success: true, message: '定时任务已在后台触发' }); res.json({ success: true, message: '定时任务已在后台触发' });
} catch (e) { } catch (error) {
res.status(500).json({ success: false, error: e.message }); res.status(500).json({ success: false, error: error.message });
} }
}); });
initResultsStore();
app.listen(PORT, () => { app.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}`); console.log(`Server running at http://localhost:${PORT}`);
initScheduler(); initScheduler();