parent
17b8b990ac
commit
e1bc7c8ca3
@ -0,0 +1,149 @@
|
||||
"""
|
||||
用户权限系统 - API路由
|
||||
"""
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||
from sqlalchemy.orm import Session
|
||||
from pydantic import BaseModel
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from app.database import get_db
|
||||
from app.user_models import User
|
||||
from app import auth_service
|
||||
|
||||
router = APIRouter(prefix="/auth", tags=["用户认证"])
|
||||
security = HTTPBearer()
|
||||
|
||||
|
||||
# 请求模型
|
||||
class LoginRequest(BaseModel):
|
||||
username: str
|
||||
password: str
|
||||
|
||||
|
||||
class LoginResponse(BaseModel):
|
||||
success: bool
|
||||
token: Optional[str] = None
|
||||
user: Optional[dict] = None
|
||||
message: Optional[str] = None
|
||||
|
||||
|
||||
class UserInfo(BaseModel):
|
||||
id: int
|
||||
username: str
|
||||
role: str
|
||||
email: Optional[str]
|
||||
|
||||
|
||||
# 登录接口
|
||||
@router.post("/login", response_model=LoginResponse)
|
||||
def login(request: LoginRequest, db: Session = Depends(get_db)):
|
||||
"""用户登录"""
|
||||
user = auth_service.authenticate_user(db, request.username, request.password)
|
||||
|
||||
if not user:
|
||||
return LoginResponse(
|
||||
success=False,
|
||||
message="用户名或密码错误"
|
||||
)
|
||||
|
||||
# 创建会话
|
||||
token = auth_service.create_session(db, user.id)
|
||||
auth_service.update_last_login(db, user)
|
||||
|
||||
return LoginResponse(
|
||||
success=True,
|
||||
token=token,
|
||||
user={
|
||||
"id": user.id,
|
||||
"username": user.username,
|
||||
"role": user.role,
|
||||
"email": user.email
|
||||
},
|
||||
message="登录成功"
|
||||
)
|
||||
|
||||
|
||||
# 登出接口
|
||||
@router.post("/logout")
|
||||
def logout(credentials: HTTPAuthorizationCredentials = Depends(security), db: Session = Depends(get_db)):
|
||||
"""用户登出"""
|
||||
auth_service.invalidate_session(db, credentials.credentials)
|
||||
return {"success": True, "message": "已登出"}
|
||||
|
||||
|
||||
# 验证令牌接口
|
||||
@router.get("/verify")
|
||||
def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security), db: Session = Depends(get_db)):
|
||||
"""验证用户令牌"""
|
||||
user = auth_service.validate_session(db, credentials.credentials)
|
||||
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="无效的会话令牌"
|
||||
)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"user": {
|
||||
"id": user.id,
|
||||
"username": user.username,
|
||||
"role": user.role,
|
||||
"email": user.email,
|
||||
"last_login": user.last_login.isoformat() if user.last_login else None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# 获取当前用户信息
|
||||
@router.get("/me")
|
||||
def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security), db: Session = Depends(get_db)):
|
||||
"""获取当前用户信息"""
|
||||
user = auth_service.validate_session(db, credentials.credentials)
|
||||
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="无效的会话令牌"
|
||||
)
|
||||
|
||||
return {
|
||||
"id": user.id,
|
||||
"username": user.username,
|
||||
"role": user.role,
|
||||
"email": user.email,
|
||||
"is_active": user.is_active,
|
||||
"created_at": user.created_at.isoformat(),
|
||||
"last_login": user.last_login.isoformat() if user.last_login else None
|
||||
}
|
||||
|
||||
|
||||
# 依赖注入:验证用户已登录
|
||||
def get_current_user_dependency(credentials: HTTPAuthorizationCredentials = Depends(security), db: Session = Depends(get_db)):
|
||||
"""依赖注入:获取当前用户"""
|
||||
user = auth_service.validate_session(db, credentials.credentials)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="请先登录"
|
||||
)
|
||||
return user
|
||||
|
||||
|
||||
# 依赖注入:仅管理员可访问
|
||||
def require_admin(credentials: HTTPAuthorizationCredentials = Depends(security), db: Session = Depends(get_db)):
|
||||
"""依赖注入:验证管理员权限"""
|
||||
user = auth_service.validate_session(db, credentials.credentials)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="请先登录"
|
||||
)
|
||||
if user.role != 'admin':
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="需要管理员权限"
|
||||
)
|
||||
return user
|
||||
@ -0,0 +1,129 @@
|
||||
"""
|
||||
用户权限系统 - 认证服务
|
||||
"""
|
||||
import hashlib
|
||||
import secrets
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
from sqlalchemy.orm import Session
|
||||
from app.user_models import User, Session as UserSession
|
||||
|
||||
|
||||
# 密码加密工具
|
||||
def hash_password(password: str) -> str:
|
||||
"""对密码进行哈希加密"""
|
||||
salt = secrets.token_hex(16)
|
||||
pwd_hash = hashlib.sha256((salt + password).encode()).hexdigest()
|
||||
return f"{salt}${pwd_hash}"
|
||||
|
||||
|
||||
def verify_password(password: str, hashed: str) -> bool:
|
||||
"""验证密码"""
|
||||
try:
|
||||
salt, pwd_hash = hashed.split('$')
|
||||
return hashlib.sha256((salt + password).encode()).hexdigest() == pwd_hash
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
def generate_token() -> str:
|
||||
"""生成会话令牌"""
|
||||
return secrets.token_urlsafe(32)
|
||||
|
||||
|
||||
# 用户操作
|
||||
def create_user(db: Session, username: str, password: str, email: str = None, role: str = 'user') -> User:
|
||||
"""创建新用户"""
|
||||
user = User(
|
||||
username=username,
|
||||
password_hash=hash_password(password),
|
||||
email=email,
|
||||
role=role,
|
||||
is_active=True
|
||||
)
|
||||
db.add(user)
|
||||
db.commit()
|
||||
db.refresh(user)
|
||||
return user
|
||||
|
||||
|
||||
def authenticate_user(db: Session, username: str, password: str) -> Optional[User]:
|
||||
"""验证用户登录"""
|
||||
user = db.query(User).filter(User.username == username).first()
|
||||
if not user:
|
||||
return None
|
||||
if not user.is_active:
|
||||
return None
|
||||
if not verify_password(password, user.password_hash):
|
||||
return None
|
||||
return user
|
||||
|
||||
|
||||
def update_last_login(db: Session, user: User):
|
||||
"""更新最后登录时间"""
|
||||
user.last_login = datetime.utcnow()
|
||||
db.commit()
|
||||
|
||||
|
||||
# 会话操作
|
||||
def create_session(db: Session, user_id: int, expires_hours: int = 24) -> str:
|
||||
"""创建用户会话"""
|
||||
token = generate_token()
|
||||
session = UserSession(
|
||||
user_id=user_id,
|
||||
token=token,
|
||||
expires_at=datetime.utcnow() + timedelta(hours=expires_hours),
|
||||
is_valid=True
|
||||
)
|
||||
db.add(session)
|
||||
db.commit()
|
||||
return token
|
||||
|
||||
|
||||
def validate_session(db: Session, token: str) -> Optional[User]:
|
||||
"""验证会话令牌"""
|
||||
session = db.query(UserSession).filter(
|
||||
UserSession.token == token,
|
||||
UserSession.is_valid == True,
|
||||
UserSession.expires_at > datetime.utcnow()
|
||||
).first()
|
||||
|
||||
if not session:
|
||||
return None
|
||||
|
||||
user = db.query(User).filter(User.id == session.user_id).first()
|
||||
if not user or not user.is_active:
|
||||
return None
|
||||
|
||||
return user
|
||||
|
||||
|
||||
def invalidate_session(db: Session, token: str):
|
||||
"""使会话失效(登出)"""
|
||||
session = db.query(UserSession).filter(UserSession.token == token).first()
|
||||
if session:
|
||||
session.is_valid = False
|
||||
db.commit()
|
||||
|
||||
|
||||
def cleanup_expired_sessions(db: Session):
|
||||
"""清理过期会话"""
|
||||
db.query(UserSession).filter(
|
||||
UserSession.expires_at < datetime.utcnow()
|
||||
).update({"is_valid": False})
|
||||
db.commit()
|
||||
|
||||
|
||||
# 默认用户创建
|
||||
def create_default_admin(db: Session):
|
||||
"""创建默认管理员账户"""
|
||||
admin = db.query(User).filter(User.username == 'lxy_root').first()
|
||||
if not admin:
|
||||
create_user(
|
||||
db=db,
|
||||
username='lxy_root',
|
||||
password='admin123',
|
||||
email='admin@system.local',
|
||||
role='admin'
|
||||
)
|
||||
print("✓ 默认管理员账户已创建: lxy_root / admin123")
|
||||
@ -0,0 +1,284 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>登录 - 期货智析系统</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
padding: 40px;
|
||||
animation: slideIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.login-header {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.login-logo {
|
||||
font-size: 48px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.login-title {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: #1D1D1F;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.login-subtitle {
|
||||
font-size: 14px;
|
||||
color: #86868B;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1D1D1F;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #E5E5E7;
|
||||
border-radius: 12px;
|
||||
font-size: 15px;
|
||||
transition: all 0.3s;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.form-input:focus {
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
width: 100%;
|
||||
padding: 14px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.login-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
|
||||
.login-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.login-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background: #FFF3F3;
|
||||
color: #DC2626;
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
margin-bottom: 20px;
|
||||
display: none;
|
||||
border-left: 4px solid #DC2626;
|
||||
}
|
||||
|
||||
.error-message.show {
|
||||
display: block;
|
||||
animation: shake 0.5s;
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
25% { transform: translateX(-10px); }
|
||||
75% { transform: translateX(10px); }
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
font-size: 12px;
|
||||
color: #86868B;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
border-top-color: white;
|
||||
animation: spin 1s ease-in-out infinite;
|
||||
margin-right: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-container">
|
||||
<div class="login-header">
|
||||
<div class="login-logo">◈</div>
|
||||
<h1 class="login-title">期货智析</h1>
|
||||
<p class="login-subtitle">智能期货期权分析系统</p>
|
||||
</div>
|
||||
|
||||
<div class="error-message" id="error-message"></div>
|
||||
|
||||
<form id="login-form">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="username">用户名</label>
|
||||
<input type="text" id="username" class="form-input" placeholder="请输入用户名" required autocomplete="username">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="password">密码</label>
|
||||
<input type="password" id="password" class="form-input" placeholder="请输入密码" required autocomplete="current-password">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="login-btn" id="login-btn">
|
||||
登录
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="footer">
|
||||
<p>© 2026 期货智析系统 · 安全登录</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const loginForm = document.getElementById('login-form');
|
||||
const loginBtn = document.getElementById('login-btn');
|
||||
const errorMessage = document.getElementById('error-message');
|
||||
|
||||
loginForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const username = document.getElementById('username').value;
|
||||
const password = document.getElementById('password').value;
|
||||
|
||||
loginBtn.disabled = true;
|
||||
loginBtn.innerHTML = '<span class="loading-spinner"></span>登录中...';
|
||||
errorMessage.classList.remove('show');
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/v1/auth/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ username, password })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
// 保存令牌
|
||||
localStorage.setItem('auth_token', data.token);
|
||||
localStorage.setItem('user_info', JSON.stringify(data.user));
|
||||
|
||||
// 根据角色跳转
|
||||
if (data.user.role === 'admin') {
|
||||
window.location.href = '/role-select';
|
||||
} else {
|
||||
window.location.href = '/futures-analysis';
|
||||
}
|
||||
} else {
|
||||
showError(data.message || '登录失败,请重试');
|
||||
}
|
||||
} catch (error) {
|
||||
showError('网络错误,请检查连接后重试');
|
||||
} finally {
|
||||
loginBtn.disabled = false;
|
||||
loginBtn.innerHTML = '登录';
|
||||
}
|
||||
});
|
||||
|
||||
function showError(message) {
|
||||
errorMessage.textContent = message;
|
||||
errorMessage.classList.add('show');
|
||||
setTimeout(() => {
|
||||
errorMessage.classList.remove('show');
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// 检查是否已登录
|
||||
window.addEventListener('DOMContentLoaded', async () => {
|
||||
const token = localStorage.getItem('auth_token');
|
||||
if (token) {
|
||||
try {
|
||||
const response = await fetch('/api/v1/auth/verify', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
if (data.user.role === 'admin') {
|
||||
window.location.href = '/role-select';
|
||||
} else {
|
||||
window.location.href = '/futures-analysis';
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// 令牌无效,继续显示登录页
|
||||
localStorage.removeItem('auth_token');
|
||||
localStorage.removeItem('user_info');
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,38 @@
|
||||
"""
|
||||
用户权限系统 - 数据库模型
|
||||
"""
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Column, Integer, String, Boolean, DateTime
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class User(Base):
|
||||
"""用户表"""
|
||||
__tablename__ = "users"
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
username = Column(String(50), unique=True, nullable=False, index=True)
|
||||
password_hash = Column(String(255), nullable=False)
|
||||
email = Column(String(100), unique=True, nullable=True)
|
||||
role = Column(String(20), nullable=False, default='user') # 'admin' 或 'user'
|
||||
is_active = Column(Boolean, default=True)
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
last_login = Column(DateTime, nullable=True)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<User(id={self.id}, username='{self.username}', role='{self.role}')>"
|
||||
|
||||
|
||||
class Session(Base):
|
||||
"""用户会话表"""
|
||||
__tablename__ = "sessions"
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
user_id = Column(Integer, nullable=False, index=True)
|
||||
token = Column(String(255), unique=True, nullable=False, index=True)
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
expires_at = Column(DateTime, nullable=False)
|
||||
is_valid = Column(Boolean, default=True)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Session(id={self.id}, user_id={self.user_id}, token='{self.token}')>"
|
||||
@ -0,0 +1,41 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
echo ========================================
|
||||
echo 数据库备份工具
|
||||
echo ========================================
|
||||
echo.
|
||||
|
||||
REM 生成备份文件名
|
||||
set BACKUP_DATE=%date:~0,4%%date:~5,2%%date:~8,2%_%time:~0,2%%time:~3,2%%time:~6,2%
|
||||
set BACKUP_DATE=%BACKUP_DATE: =0%
|
||||
set BACKUP_FILE=buffer_backup_%BACKUP_DATE%.db
|
||||
|
||||
echo 备份文件: %BACKUP_FILE%
|
||||
echo.
|
||||
|
||||
REM 停止服务
|
||||
echo [1/3] 停止服务...
|
||||
docker-compose stop
|
||||
echo ✓ 服务已停止
|
||||
|
||||
REM 备份数据库
|
||||
echo [2/3] 备份数据库...
|
||||
if exist "E:\docker_workspace\futures_datas\buffer.db" (
|
||||
copy "E:\docker_workspace\futures_datas\buffer.db" "E:\docker_workspace\futures_datas\%BACKUP_FILE%"
|
||||
echo ✓ 数据库已备份到: E:\docker_workspace\futures_datas\%BACKUP_FILE%
|
||||
) else (
|
||||
echo ✗ 数据库文件不存在
|
||||
)
|
||||
|
||||
REM 启动服务
|
||||
echo [3/3] 启动服务...
|
||||
docker-compose start
|
||||
echo ✓ 服务已启动
|
||||
|
||||
echo.
|
||||
echo ========================================
|
||||
echo 备份完成!
|
||||
echo ========================================
|
||||
pause
|
||||
Binary file not shown.
@ -0,0 +1,98 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
echo ====================================
|
||||
echo 期货智析平台 - Docker 管理脚本
|
||||
echo ====================================
|
||||
echo.
|
||||
echo 请选择操作:
|
||||
echo 1. 启动服务
|
||||
echo 2. 停止服务
|
||||
echo 3. 重启服务
|
||||
echo 4. 查看日志
|
||||
echo 5. 查看状态
|
||||
echo 6. 进入容器
|
||||
echo 7. 清理资源
|
||||
echo 0. 退出
|
||||
echo.
|
||||
set /p choice=请输入选项 (0-7):
|
||||
|
||||
if "%choice%"=="1" goto start
|
||||
if "%choice%"=="2" goto stop
|
||||
if "%choice%"=="3" goto restart
|
||||
if "%choice%"=="4" goto logs
|
||||
if "%choice%"=="5" goto status
|
||||
if "%choice%"=="6" goto shell
|
||||
if "%choice%"=="7" goto clean
|
||||
if "%choice%"=="0" goto end
|
||||
goto menu
|
||||
|
||||
:start
|
||||
echo.
|
||||
echo [启动服务...]
|
||||
docker-compose up -d
|
||||
echo.
|
||||
echo 服务已启动!访问 http://localhost:9600
|
||||
pause
|
||||
goto menu
|
||||
|
||||
:stop
|
||||
echo.
|
||||
echo [停止服务...]
|
||||
docker-compose stop
|
||||
echo.
|
||||
echo 服务已停止!
|
||||
pause
|
||||
goto menu
|
||||
|
||||
:restart
|
||||
echo.
|
||||
echo [重启服务...]
|
||||
docker-compose restart
|
||||
echo.
|
||||
echo 服务已重启!
|
||||
pause
|
||||
goto menu
|
||||
|
||||
:logs
|
||||
echo.
|
||||
echo [查看日志 - 按Ctrl+C退出]
|
||||
docker-compose logs -f --tail=100
|
||||
goto menu
|
||||
|
||||
:status
|
||||
echo.
|
||||
echo [服务状态]
|
||||
docker-compose ps
|
||||
echo.
|
||||
echo [容器资源使用]
|
||||
docker stats --no-stream futures-buffer-platform
|
||||
pause
|
||||
goto menu
|
||||
|
||||
:shell
|
||||
echo.
|
||||
echo [进入容器...]
|
||||
docker exec -it futures-buffer-platform /bin/bash
|
||||
goto menu
|
||||
|
||||
:clean
|
||||
echo.
|
||||
echo [警告] 这将停止并删除容器,但保留数据卷
|
||||
set /p confirm=确认继续? (y/n):
|
||||
if /i not "%confirm%"=="y" goto menu
|
||||
echo.
|
||||
echo [清理资源...]
|
||||
docker-compose down
|
||||
echo.
|
||||
echo 资源已清理!
|
||||
pause
|
||||
goto menu
|
||||
|
||||
:menu
|
||||
cls
|
||||
goto start
|
||||
|
||||
:end
|
||||
echo.
|
||||
echo 再见!
|
||||
exit
|
||||
@ -0,0 +1,25 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
echo ========================================
|
||||
echo 启动期货智析缓冲平台
|
||||
echo ========================================
|
||||
echo.
|
||||
|
||||
docker-compose start
|
||||
echo.
|
||||
|
||||
echo 等待服务启动...
|
||||
timeout /t 3 /nobreak >nul
|
||||
|
||||
echo.
|
||||
echo ========================================
|
||||
echo 访问地址:
|
||||
echo 品种分析页面: http://localhost:9600/futures-analysis
|
||||
echo 配置管理页面: http://localhost:9600/ui
|
||||
echo AI配置页面: http://localhost:9600/ai-config
|
||||
echo API文档: http://localhost:9600/docs
|
||||
echo ========================================
|
||||
echo.
|
||||
|
||||
docker-compose ps
|
||||
pause
|
||||
Loading…
Reference in new issue