Files
zhanghongyu_0626 647c07062e feat(trade): Enhance trade processing and context handling in customer service
- Introduced `pending_trade` handling in the chat API to manage trade requests more effectively.
- Updated the `submit_trade_api` to allow advisors to access customer trades based on assigned roles.
- Added new methods in `GatewayRepository` for managing core holdings during trade subscriptions and redemptions.
- Implemented context-aware trade dialogue management in the customer service layer to improve user experience during multi-turn interactions.
- Enhanced the tool service to support trade actions and suitability checks, ensuring accurate processing of user requests.

This update significantly improves the trade interaction flow, providing a more robust and user-friendly experience for customers engaging in trading activities.
2026-09-13 17:45:04 +08:00

309 lines
12 KiB
PowerShell
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#Requires -Version 5.1
<#
.SYNOPSIS
重启本机 JinRong 前后端开发服务(前端 Vite · 后端 Docker 或本机 uvicorn)。
.EXAMPLE
.\scripts\dev\restart-dev.ps1
.\scripts\dev\restart-dev.ps1 -Backend Local
.\scripts\dev\restart-dev.ps1 -DockerContainer stu-system-app
.NOTES
后端 Auto:若 Docker 有 jinrong* 容器映射 8000 → docker restart;否则 taskkill /T 清掉所有 app.main:app uvicorn + 8000 监听,再开单实例 uvicorn。
前端:taskkill /T 清 vite/node(5173–5175)后在新 PowerShell 窗口执行 npm run dev。
Redis 不在此脚本内;需要时先 .\scripts\dev\start-redis.ps1
#>
param(
[ValidateSet("Auto", "Docker", "Local")]
[string]$Backend = "Auto",
[string]$DockerContainer = "",
[switch]$SkipBackend,
[switch]$SkipFrontend
)
$ErrorActionPreference = "Stop"
$Root = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
$Web = Join-Path $Root "web"
Set-Location $Root
function Invoke-DockerText {
param([Parameter(ValueFromRemainingArguments)][string[]]$Args)
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) { return $null }
$prevEa = $ErrorActionPreference
$ErrorActionPreference = "SilentlyContinue"
try {
$lines = & docker @Args 2>&1 | Where-Object { $_ -is [string] }
if ($LASTEXITCODE -ne 0) { return $null }
$line = $lines | Select-Object -First 1
if (-not $line) { return $null }
return "$line".Trim()
} catch {
return $null
} finally {
$ErrorActionPreference = $prevEa
}
}
function Invoke-DockerOk {
param([Parameter(ValueFromRemainingArguments)][string[]]$Args)
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) { return $false }
$prevEa = $ErrorActionPreference
$ErrorActionPreference = "SilentlyContinue"
try {
& docker @Args 2>&1 | Out-Null
return ($LASTEXITCODE -eq 0)
} catch {
return $false
} finally {
$ErrorActionPreference = $prevEa
}
}
function Get-ListenerPids([int[]]$Ports) {
$pids = @()
foreach ($port in $Ports) {
Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue |
ForEach-Object { $pids += $_.OwningProcess }
}
$pids | Select-Object -Unique
}
function Stop-ProcessTree([int]$ProcId) {
if (-not $ProcId -or $ProcId -le 0) { return }
# /T 结束子进程(uvicorn --reload 会起父子两套 python,只杀 Listen PID 常会叠尸)
$null = Start-Process -FilePath "taskkill.exe" -ArgumentList @("/PID", $ProcId, "/T", "/F") -Wait -WindowStyle Hidden -ErrorAction SilentlyContinue
}
function Stop-PortListeners([int[]]$Ports, [switch]$WithTree) {
foreach ($procId in (Get-ListenerPids $Ports)) {
if ($procId -and $procId -gt 0) {
if ($WithTree) { Stop-ProcessTree $procId } else { Stop-Process -Id $procId -Force -ErrorAction SilentlyContinue }
}
}
}
function Test-IsJinRongUvicornCommandLine([string]$CommandLine) {
if (-not $CommandLine) { return $false }
if ($CommandLine -notmatch '(?i)uvicorn') { return $false }
if ($CommandLine -notmatch 'app\.main:app') { return $false }
if ($CommandLine -match '(?i)--port\s+8000') { return $true }
if ($CommandLine -match ':8000') { return $true }
if ($CommandLine -match [regex]::Escape($Root)) { return $true }
return $false
}
function Get-JinRongUvicornPids {
$pids = @()
foreach ($exe in @("python.exe", "python3.exe", "pythonw.exe")) {
Get-CimInstance Win32_Process -Filter "Name='$exe'" -ErrorAction SilentlyContinue |
Where-Object { Test-IsJinRongUvicornCommandLine $_.CommandLine } |
ForEach-Object { $pids += $_.ProcessId }
}
$pids | Select-Object -Unique
}
function Stop-AllBackendOnPort8000 {
Write-Host "清理 8000(Docker 外来容器 + 本机 uvicorn 进程树)..." -ForegroundColor Cyan
Stop-ForeignPort8000Container
for ($round = 1; $round -le 4; $round++) {
$batch = @()
$batch += Get-JinRongUvicornPids
$batch += Get-ListenerPids @(8000)
$batch = $batch | Where-Object { $_ -and $_ -gt 0 } | Select-Object -Unique
if ($batch.Count -eq 0) { break }
foreach ($procId in $batch) {
Write-Host " [$round] taskkill /T PID $procId" -ForegroundColor DarkGray
Stop-ProcessTree $procId
}
Start-Sleep -Milliseconds 700
}
Stop-PortListeners @(8000) -WithTree
Start-Sleep -Milliseconds 400
}
function Test-RedisDevReady {
try {
$ready = Invoke-RestMethod -Uri "http://127.0.0.1:8000/api/ready" -TimeoutSec 4 -ErrorAction Stop
if ($ready.checks.redis -eq $true) { return $true }
} catch {
# 后端尚未起来时走端口探测
}
$listen6380 = Get-NetTCPConnection -LocalPort 6380 -State Listen -ErrorAction SilentlyContinue
return [bool]$listen6380
}
function Write-RedisDevReminder {
Write-Host ""
Write-Host "[WARN] Redis 未就绪(推荐 Docker jinrong-redis @ 127.0.0.1:6380)。" -ForegroundColor Yellow
Write-Host " 影响:客户助手会话窗口、交易多轮抽槽、问数 D-06 缓存、JWT 吊销列表等(fail-open 时可能静默降级)。" -ForegroundColor Yellow
Write-Host " 不影响:交易中心直连 simulate、持仓/流水/看板(走 MySQL Core)。" -ForegroundColor DarkGray
Write-Host " 请先打开 Docker Desktop,再执行: .\scripts\dev\start-redis.ps1" -ForegroundColor Yellow
Write-Host " 自检: http://127.0.0.1:8000/api/ready (checks.redis 应为 true)" -ForegroundColor DarkGray
}
function Stop-AllViteDev {
$webEsc = [regex]::Escape($Web)
Get-CimInstance Win32_Process -Filter "Name='node.exe'" -ErrorAction SilentlyContinue |
Where-Object {
$cmd = $_.CommandLine
$cmd -and ($cmd -match '(?i)\bvite\b') -and ($cmd -match $webEsc -or $cmd -match 'npm run dev')
} |
ForEach-Object {
Write-Host " taskkill vite PID $($_.ProcessId)" -ForegroundColor DarkGray
Stop-ProcessTree $_.ProcessId
}
Stop-PortListeners @(5173, 5174, 5175) -WithTree
Start-Sleep -Milliseconds 400
}
function Wait-Port8000Clear([int]$TimeoutSec = 15) {
$deadline = (Get-Date).AddSeconds($TimeoutSec)
while ((Get-Date) -lt $deadline) {
$left = @(Get-ListenerPids @(8000))
if ($left.Count -eq 0) { return $true }
Start-Sleep -Seconds 1
}
return $false
}
function Test-BackendHasNavHistoryRoute {
try {
$paths = (Invoke-RestMethod -Uri "http://127.0.0.1:8000/openapi.json" -TimeoutSec 10).paths
return [bool]$paths.'/api/products/{product_id}/nav/history'
} catch {
return $false
}
}
function Get-DockerBackendName {
if ($DockerContainer -ne "") {
return $DockerContainer
}
$name = Invoke-DockerText ps --filter "publish=8000" --format "{{.Names}}"
if (-not $name) { return $null }
# 仅把 JinRong 自有容器当后端;其它项目(如 stu-system-app)占 8000 时走本机 uvicorn
if ($name -match '^jinrong') { return $name }
return $null
}
function Stop-ForeignPort8000Container {
$name = Invoke-DockerText ps --filter "publish=8000" --format "{{.Names}}"
if (-not $name) { return }
if ($name -match '^jinrong') { return }
Write-Host "[WARN] 容器 '$name' 占用 8000 且非 JinRong 后端,将 stop 以便加载本仓库 uvicorn(行情 nav/history 等路由)" -ForegroundColor Yellow
Invoke-DockerOk stop $name | Out-Null
}
function Test-HttpOk([string]$Url, [int]$TimeoutSec = 12) {
try {
$null = Invoke-WebRequest -Uri $Url -UseBasicParsing -TimeoutSec $TimeoutSec
return $true
} catch {
return $false
}
}
function Test-HealthOk {
try {
$r = Invoke-RestMethod -Uri "http://127.0.0.1:8000/health" -TimeoutSec 15
return ($r.status -eq "ok")
} catch {
return $false
}
}
# --- 后端 -----------------------------------------------------------------
if (-not $SkipBackend) {
$useDocker = $false
$container = Get-DockerBackendName
switch ($Backend) {
"Docker" {
if (-not $container) {
Write-Error "未找到映射 8000 的 Docker 容器;请 -DockerContainer 指定或改用 -Backend Local"
exit 1
}
$useDocker = $true
}
"Local" { $useDocker = $false }
default {
$useDocker = [bool]$container
}
}
if ($useDocker) {
Write-Host "重启后端 Docker: $container (8000) ..." -ForegroundColor Cyan
if (-not (Invoke-DockerOk restart $container)) {
Write-Error "docker restart $container 失败(Docker Desktop 是否已启动?)"
exit 1
}
$deadline = (Get-Date).AddSeconds(45)
while ((Get-Date) -lt $deadline) {
if (Test-HealthOk) { break }
Start-Sleep -Seconds 2
}
if (-not (Test-HealthOk)) {
Write-Host "[WARN] /health 暂未就绪,请稍后 docker logs $container" -ForegroundColor Yellow
} else {
Write-Host "后端就绪: http://127.0.0.1:8000/health" -ForegroundColor Green
}
} else {
Stop-AllBackendOnPort8000
$foreign = Invoke-DockerText ps --filter "publish=8000" --format "{{.Names}}"
if ($foreign -and ($foreign -notmatch '^jinrong')) {
Write-Host "[WARN] 8000 仍被 Docker 容器 '$foreign' 占用;脚本已尝试 stop,若仍存在请手动 docker stop 或启动 Docker Desktop" -ForegroundColor Yellow
}
if (-not (Wait-Port8000Clear)) {
Write-Host "[WARN] 8000 仍被占用。将列出监听进程 — 请关掉所有「uvicorn」PowerShell 窗口后重跑本脚本。" -ForegroundColor Yellow
netstat -ano | findstr ":8000"
exit 1
}
Write-Host "重启本机 uvicorn (8000) ..." -ForegroundColor Cyan
Start-Sleep -Seconds 1
$uvicornCmd = "Set-Location '$Root'; `$env:PYTHONPATH='$Root'; python -m uvicorn app.main:app --reload --host 127.0.0.1 --port 8000"
Start-Process powershell -ArgumentList @("-NoExit", "-Command", $uvicornCmd)
$routeDeadline = (Get-Date).AddSeconds(30)
$routesOk = $false
while ((Get-Date) -lt $routeDeadline) {
if ((Test-HealthOk) -and (Test-BackendHasNavHistoryRoute)) {
$routesOk = $true
break
}
Start-Sleep -Seconds 2
}
if ($routesOk) {
Write-Host "后端就绪: http://127.0.0.1:8000/health · nav/history 已挂载" -ForegroundColor Green
} elseif (Test-HealthOk) {
Write-Host "[WARN] /health 有响应但 OpenAPI 无 nav/history — 仍在跑旧代码,请关掉所有 uvicorn 窗口后重跑本脚本" -ForegroundColor Yellow
} else {
Write-Host "[WARN] uvicorn 窗口已打开,/health 尚未响应(可能仍在启动)" -ForegroundColor Yellow
}
}
}
# --- 前端 -----------------------------------------------------------------
if (-not $SkipFrontend) {
if (-not (Test-Path (Join-Path $Web "package.json"))) {
Write-Error "未找到 web/package.json"
exit 1
}
Write-Host "重启前端 Vite (5173) ..." -ForegroundColor Cyan
Stop-AllViteDev
Start-Sleep -Seconds 1
$viteCmd = "Set-Location '$Web'; npm run dev"
Start-Process powershell -ArgumentList @("-NoExit", "-Command", $viteCmd)
Start-Sleep -Seconds 4
if (Test-HttpOk "http://localhost:5173/") {
Write-Host "前端就绪: http://localhost:5173/" -ForegroundColor Green
} else {
Write-Host "[WARN] Vite 窗口已打开,5173 暂未响应(可能落在 5174,请看新窗口日志)" -ForegroundColor Yellow
}
}
if (-not (Test-RedisDevReady)) {
Write-RedisDevReminder
} else {
Write-Host "Redis 自检: 6380 / api/ready checks.redis = OK" -ForegroundColor Green
}
Write-Host "`n完成。后端 Swagger: http://127.0.0.1:8000/docs" -ForegroundColor DarkGray
Write-Host "提示: 本脚本不启动 Redis;需要时先 .\scripts\dev\start-redis.ps1(见脚本头部 NOTES)" -ForegroundColor DarkGray