爬虫实现,修改class转样式,前端渲染
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
新华网搜索爬虫命令行工具
|
新华网特别推荐爬虫命令行工具
|
||||||
用法: python RmrbSearch.py --key "关键词" --total 10 --type 0
|
用法: python XhwCommend.py --output "输出文件路径"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -20,7 +20,7 @@ from loguru import logger
|
|||||||
def main():
|
def main():
|
||||||
"""主函数"""
|
"""主函数"""
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description='新华网新闻搜索工具',
|
description='新华网特别推荐爬虫工具',
|
||||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
epilog="""
|
epilog="""
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
新华网搜索爬虫命令行工具
|
新华网热点爬虫命令行工具
|
||||||
用法: python RmrbSearch.py --key "关键词" --total 10 --type 0
|
用法: python XhwHotPoint.py --output "输出文件路径"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -20,7 +20,7 @@ from loguru import logger
|
|||||||
def main():
|
def main():
|
||||||
"""主函数"""
|
"""主函数"""
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description='新华网新闻搜索工具',
|
description='新华网热点爬虫工具',
|
||||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
epilog="""
|
epilog="""
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -251,6 +251,56 @@ class XxqgCrawler(BaseCrawler):
|
|||||||
# 相对路径,补全域名
|
# 相对路径,补全域名
|
||||||
return self.config.base_url + url
|
return self.config.base_url + url
|
||||||
|
|
||||||
|
def _extract_inline_style(self, element) -> str:
|
||||||
|
"""
|
||||||
|
提取元素的计算样式并转换为inline style
|
||||||
|
|
||||||
|
Args:
|
||||||
|
element: Selenium WebElement
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
inline style 字符串
|
||||||
|
"""
|
||||||
|
# 需要提取的CSS属性列表
|
||||||
|
css_properties = [
|
||||||
|
'text-align',
|
||||||
|
'text-indent',
|
||||||
|
'margin',
|
||||||
|
'margin-top',
|
||||||
|
'margin-bottom',
|
||||||
|
'margin-left',
|
||||||
|
'margin-right',
|
||||||
|
'padding',
|
||||||
|
'padding-top',
|
||||||
|
'padding-bottom',
|
||||||
|
'padding-left',
|
||||||
|
'padding-right',
|
||||||
|
'font-size',
|
||||||
|
'font-weight',
|
||||||
|
'font-style',
|
||||||
|
'color',
|
||||||
|
'background-color',
|
||||||
|
'line-height',
|
||||||
|
'letter-spacing',
|
||||||
|
'word-spacing'
|
||||||
|
]
|
||||||
|
|
||||||
|
styles = []
|
||||||
|
for prop in css_properties:
|
||||||
|
try:
|
||||||
|
value = element.value_of_css_property(prop)
|
||||||
|
# 过滤默认值和空值
|
||||||
|
if value and value not in ['none', 'normal', 'auto', '0px', 'rgba(0, 0, 0, 0)', 'transparent']:
|
||||||
|
# 对于 margin/padding,如果都是 0px 就跳过
|
||||||
|
if 'margin' in prop or 'padding' in prop:
|
||||||
|
if value == '0px' or value == '0':
|
||||||
|
continue
|
||||||
|
styles.append(f"{prop}: {value}")
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
return "; ".join(styles) if styles else ""
|
||||||
|
|
||||||
def parse_news_detail(self, url: str) -> NewsItem:
|
def parse_news_detail(self, url: str) -> NewsItem:
|
||||||
news_item = NewsItem(title='', contentRows=[], url=url)
|
news_item = NewsItem(title='', contentRows=[], url=url)
|
||||||
if self.driver is None:
|
if self.driver is None:
|
||||||
@@ -355,11 +405,21 @@ class XxqgCrawler(BaseCrawler):
|
|||||||
text_content = child.text.strip()
|
text_content = child.text.strip()
|
||||||
# 过滤空内容
|
# 过滤空内容
|
||||||
if text_content:
|
if text_content:
|
||||||
|
# 提取计算样式并转换为inline style
|
||||||
|
inline_style = self._extract_inline_style(child)
|
||||||
|
tag_name = child.tag_name
|
||||||
|
|
||||||
|
# 构建新的HTML标签(用inline style替代class)
|
||||||
|
if inline_style:
|
||||||
|
content_html = f'<{tag_name} style="{inline_style}">{child.get_attribute("innerHTML")}</{tag_name}>'
|
||||||
|
else:
|
||||||
|
content_html = f'<{tag_name}>{child.get_attribute("innerHTML")}</{tag_name}>'
|
||||||
|
|
||||||
news_item.contentRows.append({
|
news_item.contentRows.append({
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"content": text_content
|
"content": content_html
|
||||||
})
|
})
|
||||||
# logger.debug(f"提取文字: {text_content[:50]}...")
|
logger.debug(f"提取文字(转换样式): {text_content[:50]}...")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"处理内容元素失败: {str(e)}")
|
logger.warning(f"处理内容元素失败: {str(e)}")
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
-- 1. 关键字搜索爬取
|
-- 1. 关键字搜索爬取
|
||||||
INSERT INTO `tb_crontab_task_meta` (
|
INSERT INTO `tb_crontab_task_meta` (
|
||||||
`id`, `meta_id`, `name`, `description`, `category`,
|
`id`, `meta_id`, `name`, `description`, `category`,
|
||||||
`bean_name`, `method_name`, `script_path`, `param_schema`,
|
`bean_name`, `method_name`, `script_path`, `param_schema`, `auto_publish`,
|
||||||
`sort_order`, `creator`, `create_time`
|
`sort_order`, `creator`, `create_time`
|
||||||
) VALUES (
|
) VALUES (
|
||||||
'1',
|
'1',
|
||||||
@@ -22,19 +22,22 @@ INSERT INTO `tb_crontab_task_meta` (
|
|||||||
{
|
{
|
||||||
"name": "query",
|
"name": "query",
|
||||||
"description": "搜索关键字",
|
"description": "搜索关键字",
|
||||||
"type": "String",
|
"type": "Input",
|
||||||
|
"valueType": "String",
|
||||||
"value": "",
|
"value": "",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "total",
|
"name": "total",
|
||||||
"description": "总新闻数量",
|
"description": "总新闻数量",
|
||||||
"type": "Integer",
|
"type": "InputNumber",
|
||||||
|
"valueType": "Integer",
|
||||||
"value": 10,
|
"value": 10,
|
||||||
"required": true
|
"required": true
|
||||||
}
|
}
|
||||||
]',
|
]',
|
||||||
1,
|
1,
|
||||||
|
1,
|
||||||
'system',
|
'system',
|
||||||
NOW()
|
NOW()
|
||||||
);
|
);
|
||||||
@@ -42,7 +45,7 @@ INSERT INTO `tb_crontab_task_meta` (
|
|||||||
-- 2. 排行榜爬取
|
-- 2. 排行榜爬取
|
||||||
INSERT INTO `tb_crontab_task_meta` (
|
INSERT INTO `tb_crontab_task_meta` (
|
||||||
`id`, `meta_id`, `name`, `description`, `category`,
|
`id`, `meta_id`, `name`, `description`, `category`,
|
||||||
`bean_name`, `method_name`, `script_path`, `param_schema`,
|
`bean_name`, `method_name`, `script_path`, `param_schema`, `auto_publish`,
|
||||||
`sort_order`, `creator`, `create_time`
|
`sort_order`, `creator`, `create_time`
|
||||||
) VALUES (
|
) VALUES (
|
||||||
'2',
|
'2',
|
||||||
@@ -54,6 +57,7 @@ INSERT INTO `tb_crontab_task_meta` (
|
|||||||
'execute',
|
'execute',
|
||||||
'crawler/rmrb/RmrbHotPoint.py',
|
'crawler/rmrb/RmrbHotPoint.py',
|
||||||
'[]',
|
'[]',
|
||||||
|
1,
|
||||||
2,
|
2,
|
||||||
'system',
|
'system',
|
||||||
NOW()
|
NOW()
|
||||||
@@ -62,7 +66,7 @@ INSERT INTO `tb_crontab_task_meta` (
|
|||||||
-- 3. 往日精彩头条爬取
|
-- 3. 往日精彩头条爬取
|
||||||
INSERT INTO `tb_crontab_task_meta` (
|
INSERT INTO `tb_crontab_task_meta` (
|
||||||
`id`, `meta_id`, `name`, `description`, `category`,
|
`id`, `meta_id`, `name`, `description`, `category`,
|
||||||
`bean_name`, `method_name`, `script_path`, `param_schema`,
|
`bean_name`, `method_name`, `script_path`, `param_schema`, `auto_publish`,
|
||||||
`sort_order`, `creator`, `create_time`
|
`sort_order`, `creator`, `create_time`
|
||||||
) VALUES (
|
) VALUES (
|
||||||
'3',
|
'3',
|
||||||
@@ -75,28 +79,199 @@ INSERT INTO `tb_crontab_task_meta` (
|
|||||||
'crawler/rmrb/RmrbTrending.py',
|
'crawler/rmrb/RmrbTrending.py',
|
||||||
'[
|
'[
|
||||||
{
|
{
|
||||||
"name": "startDate",
|
"name": "dateRange",
|
||||||
"description": "开始日期",
|
"description": "日期范围",
|
||||||
"type": "String",
|
"type": "DateRangePicker",
|
||||||
|
"valueType": "String",
|
||||||
"value": "",
|
"value": "",
|
||||||
"required": false
|
"required": false,
|
||||||
},
|
"startKey": "startDate",
|
||||||
{
|
"endKey": "endDate"
|
||||||
"name": "endDate",
|
|
||||||
"description": "结束日期",
|
|
||||||
"type": "String",
|
|
||||||
"value": "",
|
|
||||||
"required": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "yesterday",
|
"name": "yesterday",
|
||||||
"description": "是否是昨天",
|
"description": "是否是昨天",
|
||||||
"type": "Boolean",
|
"type": "Switch",
|
||||||
|
"valueType": "Boolean",
|
||||||
"value": true,
|
"value": true,
|
||||||
"required": false
|
"required": false
|
||||||
}
|
}
|
||||||
]',
|
]',
|
||||||
|
1,
|
||||||
3,
|
3,
|
||||||
'system',
|
'system',
|
||||||
NOW()
|
NOW()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
-- 4. 新华网关键字搜索爬取
|
||||||
|
INSERT INTO `tb_crontab_task_meta` (
|
||||||
|
`id`, `meta_id`, `name`, `description`, `category`,
|
||||||
|
`bean_name`, `method_name`, `script_path`, `param_schema`, `auto_publish`,
|
||||||
|
`sort_order`, `creator`, `create_time`
|
||||||
|
) VALUES (
|
||||||
|
'4',
|
||||||
|
'xhw_keyword_search',
|
||||||
|
'关键字搜索爬取',
|
||||||
|
'根据关键字搜索新华网新闻内容',
|
||||||
|
'新华网新闻爬取',
|
||||||
|
'newsCrewerTask',
|
||||||
|
'execute',
|
||||||
|
'crawler/xhw/XhwSearch.py',
|
||||||
|
'[
|
||||||
|
{
|
||||||
|
"name": "query",
|
||||||
|
"description": "搜索关键字",
|
||||||
|
"type": "Input",
|
||||||
|
"valueType": "String",
|
||||||
|
"value": "",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "total",
|
||||||
|
"description": "抓取数量",
|
||||||
|
"type": "InputNumber",
|
||||||
|
"valueType": "Integer",
|
||||||
|
"value": 10,
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
]',
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
'system',
|
||||||
|
NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 5. 新华网热点新闻爬取
|
||||||
|
INSERT INTO `tb_crontab_task_meta` (
|
||||||
|
`id`, `meta_id`, `name`, `description`, `category`,
|
||||||
|
`bean_name`, `method_name`, `script_path`, `param_schema`, `auto_publish`,
|
||||||
|
`sort_order`, `creator`, `create_time`
|
||||||
|
) VALUES (
|
||||||
|
'5',
|
||||||
|
'xhw_hot_point',
|
||||||
|
'热点新闻爬取',
|
||||||
|
'爬取新华网热点新闻',
|
||||||
|
'新华网新闻爬取',
|
||||||
|
'newsCrewerTask',
|
||||||
|
'execute',
|
||||||
|
'crawler/xhw/XhwHotPoint.py',
|
||||||
|
'[]',
|
||||||
|
1,
|
||||||
|
5,
|
||||||
|
'system',
|
||||||
|
NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 6. 新华网推荐新闻爬取
|
||||||
|
INSERT INTO `tb_crontab_task_meta` (
|
||||||
|
`id`, `meta_id`, `name`, `description`, `category`,
|
||||||
|
`bean_name`, `method_name`, `script_path`, `param_schema`, `auto_publish`,
|
||||||
|
`sort_order`, `creator`, `create_time`
|
||||||
|
) VALUES (
|
||||||
|
'6',
|
||||||
|
'xhw_commend',
|
||||||
|
'推荐新闻爬取',
|
||||||
|
'爬取新华网推荐新闻',
|
||||||
|
'新华网新闻爬取',
|
||||||
|
'newsCrewerTask',
|
||||||
|
'execute',
|
||||||
|
'crawler/xhw/XhwCommend.py',
|
||||||
|
'[]',
|
||||||
|
1,
|
||||||
|
6,
|
||||||
|
'system',
|
||||||
|
NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 7. 学习强国关键字搜索爬取
|
||||||
|
INSERT INTO `tb_crontab_task_meta` (
|
||||||
|
`id`, `meta_id`, `name`, `description`, `category`,
|
||||||
|
`bean_name`, `method_name`, `script_path`, `param_schema`, `auto_publish`,
|
||||||
|
`sort_order`, `creator`, `create_time`
|
||||||
|
) VALUES (
|
||||||
|
'7',
|
||||||
|
'xxqg_keyword_search',
|
||||||
|
'关键字搜索爬取',
|
||||||
|
'根据关键字搜索学习强国新闻内容',
|
||||||
|
'学习强国新闻爬取',
|
||||||
|
'newsCrewerTask',
|
||||||
|
'execute',
|
||||||
|
'crawler/xxqg/XxqgSearch.py',
|
||||||
|
'[
|
||||||
|
{
|
||||||
|
"name": "query",
|
||||||
|
"description": "搜索关键字",
|
||||||
|
"type": "Input",
|
||||||
|
"valueType": "String",
|
||||||
|
"value": "",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "total",
|
||||||
|
"description": "抓取数量",
|
||||||
|
"type": "InputNumber",
|
||||||
|
"valueType": "Integer",
|
||||||
|
"value": 10,
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
]',
|
||||||
|
1,
|
||||||
|
7,
|
||||||
|
'system',
|
||||||
|
NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 8. 学习强国栏目新闻爬取
|
||||||
|
INSERT INTO `tb_crontab_task_meta` (
|
||||||
|
`id`, `meta_id`, `name`, `description`, `category`,
|
||||||
|
`bean_name`, `method_name`, `script_path`, `param_schema`, `auto_publish`,
|
||||||
|
`sort_order`, `creator`, `create_time`
|
||||||
|
) VALUES (
|
||||||
|
'8',
|
||||||
|
'xxqg_column_crawl',
|
||||||
|
'栏目新闻爬取',
|
||||||
|
'爬取学习强国指定栏目的新闻内容',
|
||||||
|
'学习强国新闻爬取',
|
||||||
|
'newsCrewerTask',
|
||||||
|
'execute',
|
||||||
|
'crawler/xxqg/XxqgColumn.py',
|
||||||
|
'[
|
||||||
|
{
|
||||||
|
"name": "column",
|
||||||
|
"description": "栏目名称",
|
||||||
|
"type": "Select",
|
||||||
|
"valueType": "String",
|
||||||
|
"value": "important",
|
||||||
|
"required": true,
|
||||||
|
"options": [
|
||||||
|
{"label": "重要新闻", "value": "important"},
|
||||||
|
{"label": "学习时评", "value": "xuexishiping"},
|
||||||
|
{"label": "综合新闻", "value": "zonghexinwen"},
|
||||||
|
{"label": "中宣部发布", "value": "zhongxuanbu"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "yesterday",
|
||||||
|
"description": "是否抓取昨天的数据",
|
||||||
|
"type": "Switch",
|
||||||
|
"valueType": "Boolean",
|
||||||
|
"value": true,
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dateRange",
|
||||||
|
"description": "日期范围",
|
||||||
|
"type": "DateRangePicker",
|
||||||
|
"valueType": "String",
|
||||||
|
"value": "",
|
||||||
|
"required": false,
|
||||||
|
"startKey": "start",
|
||||||
|
"endKey": "end"
|
||||||
|
}
|
||||||
|
]',
|
||||||
|
1,
|
||||||
|
8,
|
||||||
|
'system',
|
||||||
|
NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public class TbCrontabLog extends BaseDTO {
|
|||||||
private String methodParams;
|
private String methodParams;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 执行状态(0:失败 1:成功)
|
* @description 执行状态(0:失败 1:成功,2 运行中)
|
||||||
*/
|
*/
|
||||||
private Integer executeStatus;
|
private Integer executeStatus;
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public class TaskExecutor {
|
|||||||
log.setDeleted(false);
|
log.setDeleted(false);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log.setExecuteStatus(0);
|
log.setExecuteStatus(2);
|
||||||
log.setExecuteMessage("执行中");
|
log.setExecuteMessage("执行中");
|
||||||
int i = logMapper.insertLog(log);
|
int i = logMapper.insertLog(log);
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.xyzh.api.system.role.RoleService;
|
|||||||
import org.xyzh.common.core.domain.ResultDomain;
|
import org.xyzh.common.core.domain.ResultDomain;
|
||||||
import org.xyzh.common.dto.crontab.TbCrontabEmailDefault;
|
import org.xyzh.common.dto.crontab.TbCrontabEmailDefault;
|
||||||
import org.xyzh.common.dto.crontab.TbCrontabEmailRecipient;
|
import org.xyzh.common.dto.crontab.TbCrontabEmailRecipient;
|
||||||
|
import org.xyzh.common.dto.crontab.TbCrontabLog;
|
||||||
import org.xyzh.common.dto.crontab.TbCrontabTask;
|
import org.xyzh.common.dto.crontab.TbCrontabTask;
|
||||||
import org.xyzh.common.dto.crontab.TbCrontabTaskMeta;
|
import org.xyzh.common.dto.crontab.TbCrontabTaskMeta;
|
||||||
import org.xyzh.common.dto.crontab.TbDataCollectionItem;
|
import org.xyzh.common.dto.crontab.TbDataCollectionItem;
|
||||||
@@ -25,6 +26,7 @@ import org.xyzh.common.utils.NonUtils;
|
|||||||
import org.xyzh.common.vo.DataCollectionItemVO;
|
import org.xyzh.common.vo.DataCollectionItemVO;
|
||||||
import org.xyzh.common.vo.ResourceVO;
|
import org.xyzh.common.vo.ResourceVO;
|
||||||
import org.xyzh.common.vo.UserDeptRoleVO;
|
import org.xyzh.common.vo.UserDeptRoleVO;
|
||||||
|
import org.xyzh.crontab.mapper.CrontabLogMapper;
|
||||||
import org.xyzh.crontab.pojo.TaskParams;
|
import org.xyzh.crontab.pojo.TaskParams;
|
||||||
import org.xyzh.crontab.task.PythonCommandTask;
|
import org.xyzh.crontab.task.PythonCommandTask;
|
||||||
|
|
||||||
@@ -79,6 +81,8 @@ public class NewsCrawlerTask extends PythonCommandTask {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private RoleService roleService;
|
private RoleService roleService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CrontabLogMapper logMapper;
|
||||||
/**
|
/**
|
||||||
* 构建Python脚本参数
|
* 构建Python脚本参数
|
||||||
*/
|
*/
|
||||||
@@ -132,9 +136,12 @@ public class NewsCrawlerTask extends PythonCommandTask {
|
|||||||
String pythonArg = "--"+key;
|
String pythonArg = "--"+key;
|
||||||
if (pythonArg != null && value != null) {
|
if (pythonArg != null && value != null) {
|
||||||
if (value instanceof Boolean) {
|
if (value instanceof Boolean) {
|
||||||
// Boolean类型: true时只传参数名,false时不传
|
|
||||||
if ((Boolean) value) {
|
if ((Boolean) value) {
|
||||||
args.add(pythonArg);
|
args.add(pythonArg);
|
||||||
|
args.add("true");
|
||||||
|
}else{
|
||||||
|
args.add(pythonArg);
|
||||||
|
args.add("false");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// String/Integer类型: 传参数名+值
|
// String/Integer类型: 传参数名+值
|
||||||
@@ -305,6 +312,13 @@ public class NewsCrawlerTask extends PythonCommandTask {
|
|||||||
} else {
|
} else {
|
||||||
logger.warn("没有有效的新闻数据需要保存");
|
logger.warn("没有有效的新闻数据需要保存");
|
||||||
}
|
}
|
||||||
|
if(passList.isEmpty() && notPassList.isEmpty()){
|
||||||
|
TbCrontabLog log = new TbCrontabLog();
|
||||||
|
log.setID(logId);
|
||||||
|
log.setExecuteStatus(1);
|
||||||
|
log.setExecuteMessage("未爬取到数据");
|
||||||
|
int i = logMapper.updateLog(log);
|
||||||
|
}
|
||||||
|
|
||||||
// 自动发布并记录成功发布的 URL 集合
|
// 自动发布并记录成功发布的 URL 集合
|
||||||
Set<String> publishedUrls = new HashSet<>();
|
Set<String> publishedUrls = new HashSet<>();
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export interface CrontabLog extends BaseDTO {
|
|||||||
methodName?: string;
|
methodName?: string;
|
||||||
/** 方法参数 */
|
/** 方法参数 */
|
||||||
methodParams?: string;
|
methodParams?: string;
|
||||||
/** 执行状态(0:失败 1:成功) */
|
/** 执行状态(0:失败 1:成功 2运行中) */
|
||||||
executeStatus?: number;
|
executeStatus?: number;
|
||||||
/** 执行结果信息 */
|
/** 执行结果信息 */
|
||||||
executeMessage?: string;
|
executeMessage?: string;
|
||||||
@@ -162,11 +162,38 @@ export interface CrontabParam {
|
|||||||
name: string;
|
name: string;
|
||||||
/** 参数描述 */
|
/** 参数描述 */
|
||||||
description: string;
|
description: string;
|
||||||
/** 参数类型 */
|
/**
|
||||||
type: string;
|
* 前端渲染的组件类型
|
||||||
|
* - Input: 文本输入框
|
||||||
|
* - InputNumber: 数字输入框
|
||||||
|
* - DatePicker: 日期选择器
|
||||||
|
* - DateRangePicker: 日期范围选择器
|
||||||
|
* - Switch: 布尔开关
|
||||||
|
* - Select: 下拉选择器
|
||||||
|
*/
|
||||||
|
type: 'Input' | 'InputNumber' | 'DatePicker' | 'DateRangePicker' | 'Switch' | 'Select';
|
||||||
|
/**
|
||||||
|
* 参数值的数据类型(后端处理使用)
|
||||||
|
* - String: 字符串
|
||||||
|
* - Integer: 整数
|
||||||
|
* - Boolean: 布尔值
|
||||||
|
*/
|
||||||
|
valueType: 'String' | 'Integer' | 'Boolean';
|
||||||
/** 默认值 */
|
/** 默认值 */
|
||||||
value: any;
|
value: any;
|
||||||
|
/** 是否必填 */
|
||||||
required: boolean;
|
required: boolean;
|
||||||
|
/** Select类型的选项列表 */
|
||||||
|
options?: Array<{
|
||||||
|
/** 选项显示文本 */
|
||||||
|
label: string;
|
||||||
|
/** 选项值 */
|
||||||
|
value: string;
|
||||||
|
}>;
|
||||||
|
/** DateRangePicker的开始日期参数名(如:startDate, start) */
|
||||||
|
startKey?: string;
|
||||||
|
/** DateRangePicker的结束日期参数名(如:endDate, end) */
|
||||||
|
endKey?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
>
|
>
|
||||||
<el-option label="成功" :value="1" />
|
<el-option label="成功" :value="1" />
|
||||||
<el-option label="失败" :value="0" />
|
<el-option label="失败" :value="0" />
|
||||||
|
<el-option label="运行中" :value="2" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<div class="search-actions">
|
<div class="search-actions">
|
||||||
@@ -69,8 +70,11 @@
|
|||||||
<el-table-column prop="methodName" label="方法名称" width="120" />
|
<el-table-column prop="methodName" label="方法名称" width="120" />
|
||||||
<el-table-column label="执行状态" width="100">
|
<el-table-column label="执行状态" width="100">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tag :type="row.executeStatus === 1 ? 'success' : 'danger'" size="small">
|
<el-tag
|
||||||
{{ row.executeStatus === 1 ? '成功' : '失败' }}
|
:type="row.executeStatus === 1 ? 'success' : row.executeStatus === 2 ? 'warning' : 'danger'"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
{{ row.executeStatus === 1 ? '成功' : row.executeStatus === 2 ? '运行中' : '失败' }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -152,8 +156,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
<span class="detail-label">执行状态:</span>
|
<span class="detail-label">执行状态:</span>
|
||||||
<el-tag :type="currentLog.executeStatus === 1 ? 'success' : 'danger'" size="small">
|
<el-tag
|
||||||
{{ currentLog.executeStatus === 1 ? '成功' : '失败' }}
|
:type="currentLog.executeStatus === 1 ? 'success' : currentLog.executeStatus === 2 ? 'warning' : 'danger'"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
{{ currentLog.executeStatus === 1 ? '成功' : currentLog.executeStatus === 2 ? '运行中' : '失败' }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
|
|||||||
@@ -232,25 +232,67 @@
|
|||||||
{{ param.description }}
|
{{ param.description }}
|
||||||
<span class="param-type">({{ param.type }})</span>
|
<span class="param-type">({{ param.type }})</span>
|
||||||
</span>
|
</span>
|
||||||
|
<!-- 文本输入框 -->
|
||||||
<el-input
|
<el-input
|
||||||
v-if="param.type === 'String'"
|
v-if="param.type === 'Input'"
|
||||||
v-model="dynamicParams[param.name]"
|
v-model="dynamicParams[param.name]"
|
||||||
:placeholder="`请输入${param.description}`"
|
:placeholder="`请输入${param.description}`"
|
||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
|
<!-- 数字输入框 -->
|
||||||
<el-input-number
|
<el-input-number
|
||||||
v-else-if="param.type === 'Integer'"
|
v-else-if="param.type === 'InputNumber'"
|
||||||
v-model="dynamicParams[param.name]"
|
v-model="dynamicParams[param.name]"
|
||||||
:placeholder="`请输入${param.description}`"
|
:placeholder="`请输入${param.description}`"
|
||||||
controls-position="right"
|
controls-position="right"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
/>
|
/>
|
||||||
|
<!-- 日期选择器 -->
|
||||||
|
<el-date-picker
|
||||||
|
v-else-if="param.type === 'DatePicker'"
|
||||||
|
v-model="dynamicParams[param.name]"
|
||||||
|
type="date"
|
||||||
|
:placeholder="`请选择${param.description}`"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
clearable
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
<!-- 日期范围选择器 -->
|
||||||
|
<el-date-picker
|
||||||
|
v-else-if="param.type === 'DateRangePicker'"
|
||||||
|
v-model="dynamicParams[param.name]"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
clearable
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
<!-- 布尔开关 -->
|
||||||
<el-switch
|
<el-switch
|
||||||
v-else-if="param.type === 'Boolean'"
|
v-else-if="param.type === 'Switch'"
|
||||||
v-model="dynamicParams[param.name]"
|
v-model="dynamicParams[param.name]"
|
||||||
active-text="是"
|
active-text="是"
|
||||||
inactive-text="否"
|
inactive-text="否"
|
||||||
/>
|
/>
|
||||||
|
<!-- 下拉选择器 -->
|
||||||
|
<el-select
|
||||||
|
v-else-if="param.type === 'Select'"
|
||||||
|
v-model="dynamicParams[param.name]"
|
||||||
|
:placeholder="`请选择${param.description}`"
|
||||||
|
clearable
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="option in param.options"
|
||||||
|
:key="option.value"
|
||||||
|
:label="option.label"
|
||||||
|
:value="option.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -819,6 +861,24 @@ async function handleEdit(row: CrontabTask) {
|
|||||||
const params = JSON.parse(row.methodParams);
|
const params = JSON.parse(row.methodParams);
|
||||||
// 排除系统参数
|
// 排除系统参数
|
||||||
const { scriptPath, taskId, logId, ...restParams } = params;
|
const { scriptPath, taskId, logId, ...restParams } = params;
|
||||||
|
|
||||||
|
// 处理DateRangePicker参数:将startKey和endKey合并为dateRange数组
|
||||||
|
if (method.params) {
|
||||||
|
for (const param of method.params) {
|
||||||
|
if (param.type === 'DateRangePicker' && param.startKey && param.endKey) {
|
||||||
|
const startValue = restParams[param.startKey];
|
||||||
|
const endValue = restParams[param.endKey];
|
||||||
|
if (startValue && endValue) {
|
||||||
|
// 合并为数组
|
||||||
|
restParams[param.name] = [startValue, endValue];
|
||||||
|
// 删除原始的start和end字段
|
||||||
|
delete restParams[param.startKey];
|
||||||
|
delete restParams[param.endKey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 延迟设置,确保watch先执行完
|
// 延迟设置,确保watch先执行完
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
dynamicParams.value = restParams;
|
dynamicParams.value = restParams;
|
||||||
@@ -971,11 +1031,12 @@ async function handleSubmit() {
|
|||||||
for (const param of selectedMethod.value.params) {
|
for (const param of selectedMethod.value.params) {
|
||||||
const value = dynamicParams.value[param.name];
|
const value = dynamicParams.value[param.name];
|
||||||
|
|
||||||
if (param.required && param.type === 'String' && (!value || value.trim() === '')) {
|
// 使用valueType判断值类型
|
||||||
|
if (param.required && param.valueType === 'String' && (!value || value.trim() === '')) {
|
||||||
ElMessage.warning(`请输入${param.description}`);
|
ElMessage.warning(`请输入${param.description}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (param.required && param.type === 'Integer' && (value === undefined || value === null || value === '')) {
|
if (param.required && param.valueType === 'Integer' && (value === undefined || value === null || value === '')) {
|
||||||
ElMessage.warning(`请输入${param.description}`);
|
ElMessage.warning(`请输入${param.description}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -984,15 +1045,32 @@ async function handleSubmit() {
|
|||||||
|
|
||||||
submitting.value = true;
|
submitting.value = true;
|
||||||
try {
|
try {
|
||||||
|
// 处理DateRangePicker参数,将数组拆分为开始和结束日期
|
||||||
|
const processedParams = { ...dynamicParams.value };
|
||||||
|
if (selectedMethod.value.params) {
|
||||||
|
for (const param of selectedMethod.value.params) {
|
||||||
|
if (param.type === 'DateRangePicker' && processedParams[param.name]) {
|
||||||
|
const dateRange = processedParams[param.name];
|
||||||
|
if (Array.isArray(dateRange) && dateRange.length === 2) {
|
||||||
|
// 拆分为startKey和endKey
|
||||||
|
const startKey = (param as any).startKey || 'startDate';
|
||||||
|
const endKey = (param as any).endKey || 'endDate';
|
||||||
|
processedParams[startKey] = dateRange[0];
|
||||||
|
processedParams[endKey] = dateRange[1];
|
||||||
|
// 删除原始的range参数
|
||||||
|
delete processedParams[param.name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 构建CreateTaskRequest
|
// 构建CreateTaskRequest
|
||||||
const requestData: CreateTaskRequest = {
|
const requestData: CreateTaskRequest = {
|
||||||
metaId: selectedMetaId.value,
|
metaId: selectedMetaId.value,
|
||||||
task: {
|
task: {
|
||||||
...formData,
|
...formData,
|
||||||
defaultRecipient: useDefaultRecipients.value,
|
defaultRecipient: useDefaultRecipients.value,
|
||||||
methodParams: JSON.stringify({
|
methodParams: JSON.stringify(processedParams)
|
||||||
...dynamicParams.value
|
|
||||||
})
|
|
||||||
} as CrontabTask,
|
} as CrontabTask,
|
||||||
additionalRecipients: additionalRecipients.value
|
additionalRecipients: additionalRecipients.value
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
<div class="article-management">
|
<div class="article-management">
|
||||||
<div class="action-bar">
|
<div class="action-bar">
|
||||||
<el-button type="primary" @click="showCreateDialog">+ 新增文章</el-button>
|
<el-button type="primary" @click="showCreateDialog">+ 新增文章</el-button>
|
||||||
<el-button @click="handleDataCollection">数据采集</el-button>
|
|
||||||
<el-input
|
<el-input
|
||||||
v-model="searchKeyword"
|
v-model="searchKeyword"
|
||||||
placeholder="搜索文章..."
|
placeholder="搜索文章..."
|
||||||
style="width: 300px"
|
style="width: 300px"
|
||||||
|
onkeydown=""
|
||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user