494 lines
15 KiB
Markdown
494 lines
15 KiB
Markdown
|
|
# 策略管理优化方案
|
|||
|
|
|
|||
|
|
## 1. 需求概述
|
|||
|
|
|
|||
|
|
### 1.1 问题背景
|
|||
|
|
- 策略启动后如果出现异常会停止进程
|
|||
|
|
- 需要机制确保重要策略能够自动重启
|
|||
|
|
|
|||
|
|
### 1.2 优化目标
|
|||
|
|
1. **白名单机制**: 本地文件保存希望自动启动的策略列表
|
|||
|
|
2. **Web管理**: 界面可以管理白名单策略
|
|||
|
|
3. **定时启动**: 每个工作日 8:58 自动尝试启动白名单中未运行的策略
|
|||
|
|
4. **单日单次**: 每天只尝试一次,避免重复尝试
|
|||
|
|
5. **手动控制**: 手动停止后不自动重启,需要用户重新操作
|
|||
|
|
6. **双状态展示**: 每个策略显示"是否在白名单"和"是否运行中"
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. 系统架构
|
|||
|
|
|
|||
|
|
### 2.1 新增/修改文件
|
|||
|
|
|
|||
|
|
| 文件 | 操作 | 说明 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| `config/whitelist.json` | 新增 | 白名单配置文件 |
|
|||
|
|
| `core/whitelist_manager.py` | 新增 | 白名单管理器 |
|
|||
|
|
| `core/manager.py` | 修改 | 集成白名单功能 |
|
|||
|
|
| `web_backend.py` | 修改 | 新增白名单管理 API |
|
|||
|
|
| `frontend/` | 修改 | 前端界面添加白名单管理 |
|
|||
|
|
|
|||
|
|
### 2.2 核心数据结构
|
|||
|
|
|
|||
|
|
#### 白名单配置 (`config/whitelist.json`)
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"version": "1.0",
|
|||
|
|
"last_auto_start_date": "2024-01-25",
|
|||
|
|
"strategies": {
|
|||
|
|
"DualModeTrendlineHawkesStrategy2_FG": {
|
|||
|
|
"enabled": true,
|
|||
|
|
"added_at": "2024-01-25T10:00:00",
|
|||
|
|
"added_by": "web"
|
|||
|
|
},
|
|||
|
|
"SpectralTrendStrategy_rb": {
|
|||
|
|
"enabled": true,
|
|||
|
|
"added_at": "2024-01-25T10:00:00",
|
|||
|
|
"added_by": "web"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 策略状态结构扩展
|
|||
|
|
```python
|
|||
|
|
# 在 StrategyManager.strategies 字典中新增字段
|
|||
|
|
{
|
|||
|
|
"strategy_key": {
|
|||
|
|
# ... 现有字段 ...
|
|||
|
|
"in_whitelist": True, # 是否在白名单中
|
|||
|
|
"whitelist_enabled": True, # 白名单中是否启用
|
|||
|
|
"last_auto_start_attempt": "2024-01-25T08:58:00", # 上次自动启动尝试
|
|||
|
|
"auto_start_success": False # 上次自动启动是否成功
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. 详细设计
|
|||
|
|
|
|||
|
|
### 3.1 白名单管理器 (`core/whitelist_manager.py`)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
class WhitelistManager:
|
|||
|
|
"""白名单管理器"""
|
|||
|
|
|
|||
|
|
def __init__(self, config_path: str = "config/whitelist.json"):
|
|||
|
|
self.config_path = Path(config_path)
|
|||
|
|
self.data = self._load()
|
|||
|
|
|
|||
|
|
def _load(self) -> Dict:
|
|||
|
|
"""加载白名单配置"""
|
|||
|
|
if not self.config_path.exists():
|
|||
|
|
return {"version": "1.0", "strategies": {}}
|
|||
|
|
with open(self.config_path, 'r') as f:
|
|||
|
|
return json.load(f)
|
|||
|
|
|
|||
|
|
def _save(self):
|
|||
|
|
"""保存白名单配置"""
|
|||
|
|
self.config_path.parent.mkdir(exist_ok=True)
|
|||
|
|
with open(self.config_path, 'w') as f:
|
|||
|
|
json.dump(self.data, f, indent=2, ensure_ascii=False)
|
|||
|
|
|
|||
|
|
def add(self, strategy_key: str, enabled: bool = True) -> bool:
|
|||
|
|
"""添加策略到白名单"""
|
|||
|
|
if strategy_key in self.data["strategies"]:
|
|||
|
|
return False # 已存在
|
|||
|
|
self.data["strategies"][strategy_key] = {
|
|||
|
|
"enabled": enabled,
|
|||
|
|
"added_at": datetime.now().isoformat(),
|
|||
|
|
"added_by": "web"
|
|||
|
|
}
|
|||
|
|
self._save()
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
def remove(self, strategy_key: str) -> bool:
|
|||
|
|
"""从白名单移除策略"""
|
|||
|
|
if strategy_key not in self.data["strategies"]:
|
|||
|
|
return False
|
|||
|
|
del self.data["strategies"][strategy_key]
|
|||
|
|
self._save()
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
def set_enabled(self, strategy_key: str, enabled: bool) -> bool:
|
|||
|
|
"""设置策略在白名单中的启用状态"""
|
|||
|
|
if strategy_key not in self.data["strategies"]:
|
|||
|
|
return False
|
|||
|
|
self.data["strategies"][strategy_key]["enabled"] = enabled
|
|||
|
|
self._save()
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
def get_all(self) -> Dict[str, Dict]:
|
|||
|
|
"""获取所有白名单策略"""
|
|||
|
|
return self.data.get("strategies", {})
|
|||
|
|
|
|||
|
|
def is_in_whitelist(self, strategy_key: str) -> bool:
|
|||
|
|
"""检查策略是否在白名单中"""
|
|||
|
|
return strategy_key in self.data.get("strategies", {})
|
|||
|
|
|
|||
|
|
def is_enabled_in_whitelist(self, strategy_key: str) -> bool:
|
|||
|
|
"""检查策略是否在白名单中且已启用"""
|
|||
|
|
if strategy_key not in self.data.get("strategies", {}):
|
|||
|
|
return False
|
|||
|
|
return self.data["strategies"][strategy_key].get("enabled", False)
|
|||
|
|
|
|||
|
|
def update_last_auto_start_date(self, date_str: str):
|
|||
|
|
"""更新最后自动启动日期"""
|
|||
|
|
self.data["last_auto_start_date"] = date_str
|
|||
|
|
self._save()
|
|||
|
|
|
|||
|
|
def should_auto_start_today(self) -> bool:
|
|||
|
|
"""检查今天是否应该自动启动"""
|
|||
|
|
today = datetime.now().date().isoformat()
|
|||
|
|
return self.data.get("last_auto_start_date") != today
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.2 修改 `core/manager.py`
|
|||
|
|
|
|||
|
|
#### 新增功能
|
|||
|
|
```python
|
|||
|
|
class StrategyManager:
|
|||
|
|
def __init__(self, config_path: str = "config/main.json"):
|
|||
|
|
# ... 现有代码 ...
|
|||
|
|
self.whitelist_manager = WhitelistManager()
|
|||
|
|
|
|||
|
|
def get_status(self) -> Dict[str, Any]:
|
|||
|
|
"""获取完整状态(扩展白名单信息)"""
|
|||
|
|
status = super().get_status()
|
|||
|
|
|
|||
|
|
# 添加白名单信息
|
|||
|
|
for name, info in status["strategies"].items():
|
|||
|
|
info["in_whitelist"] = self.whitelist_manager.is_in_whitelist(name)
|
|||
|
|
info["whitelist_enabled"] = self.whitelist_manager.is_enabled_in_whitelist(name)
|
|||
|
|
|
|||
|
|
status["whitelist_auto_start_today"] = self.whitelist_manager.should_auto_start_today()
|
|||
|
|
|
|||
|
|
return status
|
|||
|
|
|
|||
|
|
def add_to_whitelist(self, name: str) -> bool:
|
|||
|
|
"""添加策略到白名单"""
|
|||
|
|
return self.whitelist_manager.add(name, enabled=True)
|
|||
|
|
|
|||
|
|
def remove_from_whitelist(self, name: str) -> bool:
|
|||
|
|
"""从白名单移除策略"""
|
|||
|
|
return self.whitelist_manager.remove(name)
|
|||
|
|
|
|||
|
|
def set_whitelist_enabled(self, name: str, enabled: bool) -> bool:
|
|||
|
|
"""设置白名单中策略的启用状态"""
|
|||
|
|
return self.whitelist_manager.set_enabled(name, enabled)
|
|||
|
|
|
|||
|
|
def start_strategy(self, name: str, auto_start: bool = False) -> bool:
|
|||
|
|
"""
|
|||
|
|
启动策略
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
name: 策略标识符
|
|||
|
|
auto_start: 是否为自动启动(自动启动不会更新最后尝试日期)
|
|||
|
|
"""
|
|||
|
|
# ... 现有启动逻辑 ...
|
|||
|
|
|
|||
|
|
# 如果是手动启动,清除自动启动相关的标记
|
|||
|
|
if not auto_start:
|
|||
|
|
if name in self.strategies:
|
|||
|
|
self.strategies[name]["last_auto_start_attempt"] = None
|
|||
|
|
self.strategies[name]["auto_start_success"] = None
|
|||
|
|
|
|||
|
|
return success
|
|||
|
|
|
|||
|
|
def auto_start_whitelist_strategies(self) -> Dict[str, bool]:
|
|||
|
|
"""
|
|||
|
|
自动启动白名单中所有未运行的策略
|
|||
|
|
一天只执行一次
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
Dict[str, bool]: 每个策略的启动结果
|
|||
|
|
"""
|
|||
|
|
if not self.whitelist_manager.should_auto_start_today():
|
|||
|
|
return {}
|
|||
|
|
|
|||
|
|
results = {}
|
|||
|
|
whitelist = self.whitelist_manager.get_all()
|
|||
|
|
|
|||
|
|
for name, config in whitelist.items():
|
|||
|
|
if not config.get("enabled", True):
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
if name not in self.strategies:
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
# 检查是否已在运行
|
|||
|
|
if self._is_running(name):
|
|||
|
|
results[name] = True
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
# 尝试启动
|
|||
|
|
success = self.start_strategy(name, auto_start=True)
|
|||
|
|
results[name] = success
|
|||
|
|
|
|||
|
|
# 更新策略状态
|
|||
|
|
if name in self.strategies:
|
|||
|
|
self.strategies[name]["last_auto_start_attempt"] = datetime.now().isoformat()
|
|||
|
|
self.strategies[name]["auto_start_success"] = success
|
|||
|
|
|
|||
|
|
# 更新日期
|
|||
|
|
self.whitelist_manager.update_last_auto_start_date(
|
|||
|
|
datetime.now().date().isoformat()
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
return results
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.3 修改 `web_backend.py`
|
|||
|
|
|
|||
|
|
#### 新增 API 端点
|
|||
|
|
```python
|
|||
|
|
# ============ 白名单管理 API ============
|
|||
|
|
|
|||
|
|
@app.get("/api/whitelist")
|
|||
|
|
def get_whitelist():
|
|||
|
|
"""获取白名单列表"""
|
|||
|
|
whitelist = manager.whitelist_manager.get_all()
|
|||
|
|
return {
|
|||
|
|
"whitelist": whitelist,
|
|||
|
|
"auto_start_today": manager.whitelist_manager.should_auto_start_today()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@app.post("/api/whitelist/{name}/add")
|
|||
|
|
def add_to_whitelist(name: str):
|
|||
|
|
"""添加策略到白名单"""
|
|||
|
|
if manager.add_to_whitelist(name):
|
|||
|
|
return {"success": True}
|
|||
|
|
raise HTTPException(400, "添加失败,策略可能已存在")
|
|||
|
|
|
|||
|
|
@app.post("/api/whitelist/{name}/remove")
|
|||
|
|
def remove_from_whitelist(name: str):
|
|||
|
|
"""从白名单移除策略"""
|
|||
|
|
if manager.remove_from_whitelist(name):
|
|||
|
|
return {"success": True}
|
|||
|
|
raise HTTPException(400, "移除失败,策略可能不在白名单中")
|
|||
|
|
|
|||
|
|
@app.post("/api/whitelist/{name}/enable")
|
|||
|
|
def enable_in_whitelist(name: str):
|
|||
|
|
"""启用白名单中的策略"""
|
|||
|
|
if manager.set_whitelist_enabled(name, True):
|
|||
|
|
return {"success": True}
|
|||
|
|
raise HTTPException(400, "操作失败")
|
|||
|
|
|
|||
|
|
@app.post("/api/whitelist/{name}/disable")
|
|||
|
|
def disable_in_whitelist(name: str):
|
|||
|
|
"""禁用白名单中的策略"""
|
|||
|
|
if manager.set_whitelist_enabled(name, False):
|
|||
|
|
return {"success": True}
|
|||
|
|
raise HTTPException(400, "操作失败")
|
|||
|
|
|
|||
|
|
@app.post("/api/whitelist/auto-start")
|
|||
|
|
def trigger_auto_start():
|
|||
|
|
"""手动触发白名单自动启动(用于测试)"""
|
|||
|
|
results = manager.auto_start_whitelist_strategies()
|
|||
|
|
return {
|
|||
|
|
"success": True,
|
|||
|
|
"results": results,
|
|||
|
|
"count": len(results)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# ============ 修改现有的状态 API ============
|
|||
|
|
|
|||
|
|
@app.get("/api/status")
|
|||
|
|
def get_status():
|
|||
|
|
"""获取策略状态(包含白名单信息)"""
|
|||
|
|
status_data = manager.get_status()
|
|||
|
|
status_data['git_info'] = get_git_commit_info()
|
|||
|
|
return status_data
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 修改定时任务
|
|||
|
|
```python
|
|||
|
|
@app.on_event("startup")
|
|||
|
|
async def start_scheduler():
|
|||
|
|
# ... 现有的 08:58, 20:58 重启任务 ...
|
|||
|
|
|
|||
|
|
# 新增:白名单自动启动任务(仅 08:58)
|
|||
|
|
scheduler.add_job(
|
|||
|
|
auto_start_whitelist_task,
|
|||
|
|
CronTrigger(hour=8, minute=58),
|
|||
|
|
id="whitelist_auto_start",
|
|||
|
|
replace_existing=True
|
|||
|
|
)
|
|||
|
|
logger.info("📅 白名单自动启动任务已添加 (计划时间: 08:58)")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def auto_start_whitelist_task():
|
|||
|
|
"""
|
|||
|
|
白名单自动启动任务
|
|||
|
|
"""
|
|||
|
|
logger.info("⏰ [白名单任务] 触发自动启动...")
|
|||
|
|
|
|||
|
|
results = manager.auto_start_whitelist_strategies()
|
|||
|
|
|
|||
|
|
if not results:
|
|||
|
|
logger.info("⏰ [白名单任务] 今天已执行过或无需启动")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
success_count = sum(1 for v in results.values() if v)
|
|||
|
|
fail_count = len(results) - success_count
|
|||
|
|
|
|||
|
|
logger.info(f"⏰ [白名单任务] 完成: 成功 {success_count}, 失败 {fail_count}")
|
|||
|
|
|
|||
|
|
for name, success in results.items():
|
|||
|
|
if success:
|
|||
|
|
logger.info(f"✅ [白名单任务] {name} 启动成功")
|
|||
|
|
else:
|
|||
|
|
logger.warning(f"❌ [白名单任务] {name} 启动失败")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.4 修改 `start.py`
|
|||
|
|
|
|||
|
|
#### 新增 CLI 命令
|
|||
|
|
```python
|
|||
|
|
def main():
|
|||
|
|
# ... 现有的参数解析 ...
|
|||
|
|
|
|||
|
|
parser.add_argument("--whitelist", action="store_true", help="白名单操作模式")
|
|||
|
|
|
|||
|
|
# 白名单子命令
|
|||
|
|
whitelist_parser = subparsers.add_parser("whitelist", help="白名单管理")
|
|||
|
|
whitelist_parser.add_argument("action", choices=["add", "remove", "list", "enable", "disable"])
|
|||
|
|
whitelist_parser.add_argument("-n", "--name", help="策略标识符")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.5 前端界面修改
|
|||
|
|
|
|||
|
|
#### 状态表格新增列
|
|||
|
|
| 列名 | 说明 | 示例值 |
|
|||
|
|
|------|------|--------|
|
|||
|
|
| 白名单 | 是否在白名单中 | ✅ / ❌ |
|
|||
|
|
| 白名单状态 | 白名单中是否启用 | 启用 / 禁用 |
|
|||
|
|
| 自动启动 | 今天是否已尝试自动启动 | 是 / 否 |
|
|||
|
|
| 自动启动结果 | 上次自动启动是否成功 | 成功 / 失败 / - |
|
|||
|
|
|
|||
|
|
#### 新增管理操作
|
|||
|
|
1. **添加到白名单**: 勾选策略后点击"添加到白名单"
|
|||
|
|
2. **从白名单移除**: 勾选策略后点击"从白名单移除"
|
|||
|
|
3. **启用/禁用**: 在白名单中启用或禁用某个策略
|
|||
|
|
4. **手动触发**: 按钮"立即执行白名单启动"
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. 工作流程
|
|||
|
|
|
|||
|
|
### 4.1 自动启动流程
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
sequenceDiagram
|
|||
|
|
participant T as 定时任务 (08:58)
|
|||
|
|
participant W as Web Backend
|
|||
|
|
participant M as StrategyManager
|
|||
|
|
participant WM as WhitelistManager
|
|||
|
|
participant S as Strategy Process
|
|||
|
|
|
|||
|
|
T->>W: 触发定时任务
|
|||
|
|
W->>M: auto_start_whitelist_strategies()
|
|||
|
|
M->>WM: should_auto_start_today()?
|
|||
|
|
|
|||
|
|
alt 今天未执行过
|
|||
|
|
WM-->>M: True
|
|||
|
|
M->>WM: get_all() 获取白名单
|
|||
|
|
loop 遍历白名单策略
|
|||
|
|
M->>M: 检查策略是否运行
|
|||
|
|
alt 未运行
|
|||
|
|
M->>S: 启动策略进程 (auto_start=True)
|
|||
|
|
M->>M: 记录启动结果
|
|||
|
|
else 已在运行
|
|||
|
|
M->>M: 标记为成功
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
M->>WM: update_last_auto_start_date(今天)
|
|||
|
|
M-->>W: 返回启动结果
|
|||
|
|
W->>T: 记录日志
|
|||
|
|
else 今天已执行过
|
|||
|
|
WM-->>M: False
|
|||
|
|
M-->>W: 返回空结果
|
|||
|
|
W->>T: 跳过(今天已执行)
|
|||
|
|
end
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4.2 手动管理流程
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
sequenceDiagram
|
|||
|
|
participant U as 用户
|
|||
|
|
participant W as Web界面
|
|||
|
|
participant API as Web Backend API
|
|||
|
|
participant M as StrategyManager
|
|||
|
|
participant WM as WhitelistManager
|
|||
|
|
participant F as 白名单配置文件
|
|||
|
|
|
|||
|
|
U->>W: 点击"添加到白名单"
|
|||
|
|
W->>API: POST /api/whitelist/{name}/add
|
|||
|
|
API->>M: add_to_whitelist(name)
|
|||
|
|
M->>WM: add(name)
|
|||
|
|
WM->>F: 写入配置
|
|||
|
|
F-->>WM: 保存成功
|
|||
|
|
WM-->>M: True
|
|||
|
|
M-->>API: True
|
|||
|
|
API-->>W: {"success": True}
|
|||
|
|
W->>U: 显示成功提示
|
|||
|
|
W->>API: GET /api/status
|
|||
|
|
API->>M: get_status()
|
|||
|
|
M->>WM: 获取白名单状态
|
|||
|
|
M-->>API: 状态数据
|
|||
|
|
API-->>W: 状态数据
|
|||
|
|
W->>U: 更新表格(显示白名单状态)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. 兼容性设计
|
|||
|
|
|
|||
|
|
### 5.1 向后兼容
|
|||
|
|
- 现有 `start.py status` 命令保持不变
|
|||
|
|
- 现有 API `/api/status` 保持兼容(新增字段不影响现有功能)
|
|||
|
|
- 现有日志查看功能保持不变
|
|||
|
|
|
|||
|
|
### 5.2 数据迁移
|
|||
|
|
- 首次启动时自动创建 `config/whitelist.json`
|
|||
|
|
- 无需手动迁移现有配置
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. 实施计划
|
|||
|
|
|
|||
|
|
### 阶段一:后端实现
|
|||
|
|
1. 创建 `config/whitelist.json` 模板
|
|||
|
|
2. 实现 `core/whitelist_manager.py`
|
|||
|
|
3. 修改 `core/manager.py` 集成白名单功能
|
|||
|
|
4. 修改 `web_backend.py` 添加 API
|
|||
|
|
5. 修改 `start.py` 添加 CLI 命令
|
|||
|
|
|
|||
|
|
### 阶段二:前端实现
|
|||
|
|
1. 修改状态表格,添加白名单列
|
|||
|
|
2. 添加白名单管理操作按钮
|
|||
|
|
3. 添加手动触发按钮
|
|||
|
|
4. 美化界面交互
|
|||
|
|
|
|||
|
|
### 阶段三:测试
|
|||
|
|
1. 测试白名单添加/移除
|
|||
|
|
2. 测试自动启动功能
|
|||
|
|
3. 测试手动停止不自动重启
|
|||
|
|
4. 测试一天只启动一次
|
|||
|
|
5. 测试重启后状态恢复
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. 文件修改清单
|
|||
|
|
|
|||
|
|
| 文件路径 | 操作 | 说明 |
|
|||
|
|
|----------|------|------|
|
|||
|
|
| `config/whitelist.json` | 新增 | 白名单配置文件 |
|
|||
|
|
| `core/whitelist_manager.py` | 新增 | 白名单管理器类 |
|
|||
|
|
| `core/manager.py` | 修改 | 集成白名单功能,扩展状态结构 |
|
|||
|
|
| `web_backend.py` | 修改 | 添加白名单管理 API,修改定时任务 |
|
|||
|
|
| `start.py` | 修改 | 添加白名单 CLI 命令 |
|
|||
|
|
| `frontend/` | 修改 | 前端界面添加白名单管理功能 |
|