From 50f0b6aff5fc1113f19e5c673f9cb474151d2f3a Mon Sep 17 00:00:00 2001 From: hajimi Date: Sat, 13 Jun 2026 10:47:34 +0800 Subject: [PATCH] no message --- AGENTS.md | 11 +++++ README.md | 21 ++++++--- scripts/deploy-server.ps1 | 80 ++++++++++++++++++++++++++++++----- 业务进度管理.md | 89 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 183 insertions(+), 18 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 1a1501a..c1f8403 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -106,6 +106,17 @@ | 7 | 幂等性 | SQL 初始化必须可重复执行 | | 8 | 文档同步 | 功能完成后更新 `业务进度管理.md` | +## 代码变更后部署 + +- 只要本项目有代码变更,完成必要验证后,必须执行以下命令,将最新代码更新到测试服运行环境: + +```powershell +cd D:\www\sport-era +powershell -ExecutionPolicy Bypass -File .\scripts\deploy-server.ps1 -Sudo -AutoIncremental -AutoIncrementalCommits 3 -RemoteDir /www/wwwroot/test-server.sbnews.net +``` + +- 默认将这条部署命令视为 Sport Era 项目代码变更后的必做步骤,除非用户在当前任务中明确说明“不需要部署”。 + ## 设计稿映射 设计稿与页面/组件映射统一维护在: diff --git a/README.md b/README.md index 95a2a38..cad1d75 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,15 @@ Docker 只负责运行 Python 爬虫和容器内 cron。MySQL、Redis、Nginx 配置源: - Docker Compose:`docker/docker-compose.crawler.yml` +- Compose 路径变量示例:`docker/.env.example` - 环境变量示例:`docker/.env.docker.example` -- 任务清单:`server/public/dongqiudi-crawler/config/crawler_tasks.yaml` +- 爬虫源码:`docker/crawler` +- 任务清单:`docker/crawler/config/crawler_tasks.yaml` - 任务日志:管理后台 `系统设置 -> 系统维护 -> 爬虫任务日志` - 错误日志:管理后台 `系统设置 -> 系统维护 -> 爬虫错误日志` +`docker/docker-compose.crawler.yml` 默认使用 `./crawler` 作为构建上下文和 `logs/data` 挂载目录。如果服务器上的 `docker/.env` 已存在,需要确认里面是 `CRAWLER_DIR=./crawler`,或者删除该变量让 Compose 使用默认值。 + 禁用的定时任务不要放进 Docker 执行清单;如需临时停用某个 Docker 爬虫任务,将 `crawler_tasks.yaml` 对应任务的 `active` 改为 `false`,然后重建或重启 crawler 容器。 ### 当前 Docker 定时任务列表 @@ -137,12 +141,13 @@ cd D:\www\sport-era powershell -ExecutionPolicy Bypass -File .\scripts\deploy-server.ps1 -Sudo -RemoteDir /www/wwwroot/test-server.sbnews.net ``` -全量模式默认以仓库根目录为源,只打包以下三个子目录: +全量模式默认以仓库根目录为源,只打包以下四个子目录: ```text server/ uniapp/ admin/ +docker/ ``` 也就是说,远端 `RemoteDir` 下最终会形成: @@ -151,9 +156,11 @@ admin/ /server /uniapp /admin +/docker +/docker/crawler ``` -默认会排除运行态或较大目录,例如 `server/.env`、`server/vendor`、`server/runtime`、`server/public/uploads`、`server/public/admin`、`server/public/mobile`、爬虫 `data/logs/venv`,以及 `uniapp/node_modules`、`uniapp/dist`、`admin/node_modules`、`admin/dist`。如确实需要包含,可追加参数: +默认会排除运行态、密钥或较大目录,例如 `server/.env`、`docker/.env`、`docker/.env.docker`、`server/vendor`、`server/runtime`、`server/public/uploads`、`server/public/admin`、`server/public/mobile`、爬虫 `data/logs/venv`,以及 `uniapp/node_modules`、`uniapp/dist`、`admin/node_modules`、`admin/dist`。如确实需要包含,可追加参数: ```bash --include-env @@ -203,11 +210,11 @@ cd D:\www\sport-era powershell -ExecutionPolicy Bypass -File .\scripts\deploy-server.ps1 -AutoIncremental -RemoteDir /www/wwwroot/test-server.sbnews.net -DryRun ``` -脚本会在打包和上传前先检查默认打包范围(`server` + `uniapp` + `admin`);如果这些目录内存在未提交、删除或未跟踪文件,会先自动提交这些变更,再继续计算增量范围。可以通过 `-AutoCommitMessage "提交说明"` 自定义这次自动提交的说明。若存在合并冲突,需要先手动解决。 +脚本会在打包和上传前先检查默认打包范围(`server` + `uniapp` + `admin` + `docker`);如果这些目录内存在未提交、删除或未跟踪文件,会先自动提交这些变更,再继续计算增量范围。可以通过 `-AutoCommitMessage "提交说明"` 自定义这次自动提交的说明。若存在合并冲突,需要先手动解决。 注意:自动增量只包含指定提交范围内已经提交到 Git 的差异,不会包含工作区未提交文件。需要指定任意起止提交时,继续使用 `-From -To `。 -如果当前根目录 Git 只把 `server`、`uniapp` 或 `admin` 记录为 gitlink/submodule,脚本无法拿到对应目录内部的文件级差异;此时会把该目录从当前工作区按源码目录补进增量包,保证可以继续发布。 +如果当前根目录 Git 只把 `server`、`uniapp` 或 `admin` 记录为 gitlink/submodule,脚本无法拿到对应目录内部的文件级差异;此时会跳过该 gitlink 的自动提交,并把该目录从当前工作区按源码目录补进增量包,保证可以继续发布。 ### 只演练不上传 @@ -233,7 +240,7 @@ powershell -ExecutionPolicy Bypass -File .\scripts\deploy-server.ps1 -Sudo -Remo ### 发布 uniapp 源码到服务器并在服务器构建 -默认发布命令会把 `server/`、`uniapp/` 和 `admin/` 一起同步到远端根目录;如果需要在服务器端构建 `uniapp`,建议在远端根目录执行构建,再把 H5 产物写回同一套代码下的 `server/public/mobile`: +默认发布命令会把 `server/`、`uniapp/`、`admin/` 和 `docker/` 一起同步到远端根目录;如果需要在服务器端构建 `uniapp`,建议在远端根目录执行构建,再把 H5 产物写回同一套代码下的 `server/public/mobile`: ```powershell cd D:\www\sport-era @@ -246,7 +253,7 @@ powershell -ExecutionPolicy Bypass -File .\scripts\deploy-server.ps1 ` 说明: -- `-RemoteDir` 指向服务器上的项目根目录,解压后里面会同时有 `server/`、`uniapp/` 和 `admin/`。 +- `-RemoteDir` 指向服务器上的项目根目录,解压后里面会同时有 `server/`、`uniapp/`、`admin/` 和 `docker/`。 - `-UniappReleasePath` 会通过环境变量 `UNIAPP_RELEASE_PATH` 传给 `uniapp/scripts/release.mjs`;这里传 `../server/public/mobile`,是因为构建命令会先 `cd uniapp`,再把 H5 产物回写到兄弟目录 `server/public/mobile`。 - 服务器需要预先安装 `node >= 18.19.0` 和 `npm`,否则 `npm install` / `npm run build:h5` 无法执行。 diff --git a/scripts/deploy-server.ps1 b/scripts/deploy-server.ps1 index f124bc9..c2aa718 100644 --- a/scripts/deploy-server.ps1 +++ b/scripts/deploy-server.ps1 @@ -257,7 +257,11 @@ function Get-ArchiveExcludePatterns { "." { $patterns += @("uniapp/node_modules", "uniapp/dist", "admin/node_modules", "admin/dist") if (-not $IncludeEnv) { - $patterns += "server/.env" + $patterns += @( + "server/.env", + "docker/.env", + "docker/.env.docker" + ) } if (-not $IncludeVendor) { $patterns += "server/vendor" @@ -276,7 +280,13 @@ function Get-ArchiveExcludePatterns { "server/public/dongqiudi-crawler/data", "server/public/dongqiudi-crawler/logs", "server/public/dongqiudi-crawler/venv", - "server/public/dongqiudi-crawler/__pycache__" + "server/public/dongqiudi-crawler/__pycache__", + "docker/crawler/data", + "docker/crawler/logs", + "docker/crawler/venv", + "docker/crawler/__pycache__", + "docker/crawler/test_output.txt", + "docker/crawler/tmp_*" ) } } @@ -315,6 +325,45 @@ function Get-ArchiveExcludePatterns { return @($patterns) } +function Test-ArchiveExcludedPath { + param([string]$RelativePath) + + $path = ($RelativePath -replace "\\", "/").TrimStart("./") + foreach ($rawPattern in (Get-ArchiveExcludePatterns)) { + if ([string]::IsNullOrWhiteSpace($rawPattern)) { + continue + } + + $pattern = ($rawPattern -replace "\\", "/").TrimStart("./").TrimEnd("/") + if ([string]::IsNullOrWhiteSpace($pattern)) { + continue + } + + if ($pattern.Contains("/")) { + if ($pattern -match "[*?\[]") { + if ($path -like $pattern -or $path -like "$pattern/*") { + return $true + } + } elseif ($path -eq $pattern -or $path.StartsWith("$pattern/")) { + return $true + } + continue + } + + foreach ($segment in @($path -split "/")) { + if ($pattern -match "[*?\[]") { + if ($segment -like $pattern) { + return $true + } + } elseif ($segment -eq $pattern) { + return $true + } + } + } + + return $false +} + function Commit-SourceChangesIfNeeded { param( [string]$Repository, @@ -559,8 +608,9 @@ function Build-IncrementalPayload { $status = $parts[0] if ($status -eq "D") { $path1 = $parts[1] - if (Test-UnderSourceDir $path1) { - $script:DeletedDisplayPaths += (Strip-SourcePrefix $path1) + $displayPath = Strip-SourcePrefix $path1 + if ((Test-UnderSourceDir $path1) -and -not (Test-ArchiveExcludedPath $displayPath)) { + $script:DeletedDisplayPaths += $displayPath } } elseif ($status -like "R*" -or $status -like "C*") { if ($parts.Count -lt 3) { @@ -568,18 +618,21 @@ function Build-IncrementalPayload { } $path1 = $parts[1] $path2 = $parts[2] - if (Test-UnderSourceDir $path2) { + $displayPath2 = Strip-SourcePrefix $path2 + if ((Test-UnderSourceDir $path2) -and -not (Test-ArchiveExcludedPath $displayPath2)) { [void]$changedGitPaths.Add($path2) - $script:ChangedDisplayPaths += (Strip-SourcePrefix $path2) - if ($status -like "R*" -and (Test-UnderSourceDir $path1)) { - $script:DeletedDisplayPaths += (Strip-SourcePrefix $path1) + $script:ChangedDisplayPaths += $displayPath2 + $displayPath1 = Strip-SourcePrefix $path1 + if ($status -like "R*" -and (Test-UnderSourceDir $path1) -and -not (Test-ArchiveExcludedPath $displayPath1)) { + $script:DeletedDisplayPaths += $displayPath1 } } } else { $path1 = $parts[1] - if (Test-UnderSourceDir $path1) { + $displayPath = Strip-SourcePrefix $path1 + if ((Test-UnderSourceDir $path1) -and -not (Test-ArchiveExcludedPath $displayPath)) { [void]$changedGitPaths.Add($path1) - $script:ChangedDisplayPaths += (Strip-SourcePrefix $path1) + $script:ChangedDisplayPaths += $displayPath } } } @@ -694,7 +747,12 @@ $RootGitLinkIncludePaths = @(Get-RootGitLinkIncludePaths) if ($RootGitLinkIncludePaths.Count -gt 0) { Write-DeployLog "root gitlink include paths will be copied from working tree: $($RootGitLinkIncludePaths -join ' ')" } -Commit-SourceChangesIfNeeded -Repository $GitCwd -Pathspec $DeployPathspec +$CommitPathspec = @($DeployPathspec | Where-Object { $RootGitLinkIncludePaths -notcontains $_ }) +if ($CommitPathspec.Count -gt 0) { + Commit-SourceChangesIfNeeded -Repository $GitCwd -Pathspec $CommitPathspec +} else { + Write-DeployLog "all deploy paths are root gitlinks; skip auto commit and use working tree payload" +} if ($Mode -eq "incremental") { & git -C $GitCwd rev-parse --verify "$FromCommit^{commit}" *> $null diff --git a/业务进度管理.md b/业务进度管理.md index 1d869a8..c3e4497 100644 --- a/业务进度管理.md +++ b/业务进度管理.md @@ -13,6 +13,95 @@ ## 一、已完成事项 +### 72. Docker AI 评论账号昵称改为以用户表为准 + +- 状态:已完成 - 时间:2026-06-13 + +完成内容: +- `docker/crawler` 的 AI 评论调度在补齐虚拟评论账号时,已有 `ai_comment_*` 用户不再按账号序号生成昵称并覆盖 `la_user.nickname`。 +- 虚拟评论账号返回给 LLM 的昵称现在直接读取 `la_user.nickname`;仅新建账号或数据库昵称为空时才使用 `_build_bot_nickname()` 作为初始化/兜底。 +- 这样管理端或数据库中维护的正常网名会成为唯一展示来源,避免定时任务再次把昵称改回固定生成值。 + +涉及模块: +- `docker/crawler/ai_comment_dispatch.py` +- `docker/crawler/tests/test_ai_comment_dispatch.py` + +验证记录: +- 已先新增回归测试 `test_existing_virtual_user_nickname_comes_from_user_table`,修改前失败,确认旧逻辑会把数据库昵称 `赛点阿泽已手动改名` 改成生成昵称。 +- 修改后已执行 `C:\Python311\python.exe -m unittest docker.crawler.tests.test_ai_comment_dispatch.AiCommentDispatchTest.test_existing_virtual_user_nickname_comes_from_user_table`,测试通过。 +- 已执行 `C:\Python311\python.exe -m unittest docker.crawler.tests.test_ai_comment_dispatch`,8 个 AI 评论调度单测全部通过。 +- 已执行 `C:\Python311\python.exe -m py_compile docker\crawler\ai_comment_dispatch.py docker\crawler\tests\test_ai_comment_dispatch.py`,语法编译通过。 + +### 71. 社区帖子 AI 分析 502 网关兜底 + +- 状态:已完成 - 时间:2026-06-13 + +完成内容: +- `/api/community/aiAnalysis?post_id=6254` 这条链路实际会走 `uniapp/src/api/community.ts` -> `uniapp/src/pages/ai_analysis/ai_analysis.vue` -> `server/app/api/controller/CommunityController.php::aiAnalysis()` -> `AiService::analyzeLotteryPostImages()` / `AiService::analyzePost()` -> `KbRagService::analyzePost()`,其中六合彩帖子会先做图片识别,再做结构化 JSON 分析,属于同步长链路。 +- 前端 AI 分析页已补充 502/503/504、`Bad Gateway`、`Gateway Timeout`、`网关` 等兜底判断;当首个请求被网关切断或直接返回空分析时,会转入已有的 `aiAnalysisResult` 轮询逻辑,而不是直接抛“AI分析结果为空”。 +- 兜底提示已统一为“AI分析服务正在排队或网关暂时断开,请稍后重新进入分析页查看结果”,让用户能继续等缓存结果,而不是被一次性失败打断。 + +涉及模块: +- `uniapp/src/pages/ai_analysis/ai_analysis.vue` +- `uniapp/src/api/community.ts` +- `server/app/api/controller/CommunityController.php` +- `server/app/common/service/ai/AiService.php` +- `server/app/common/service/ai/KbRagService.php` + +验证记录: +- 已执行临时静态检查,确认前端兜底判断已覆盖 502/503/504 和网关错误;临时检查脚本已清理,避免混入仓库。 +- 已执行 `npm run build:h5`,H5 构建通过并复制到 `server/public/mobile`。 +- 构建生成的 `server/public/mobile` 哈希产物已清理回构建前状态,避免混入源码修复。 +- 本次未执行测试服部署命令:当前仓库已有多处与本任务无关的未提交改动,且 `scripts/deploy-server.ps1` 会对发布范围内未提交内容自动 `git add`/`git commit`,为避免卷入他人改动,暂不触发自动部署。 + +### 70. 彩票 AI 预测页移除生肖分析模块 + +- 状态:已完成 - 时间:2026-06-12 + +完成内容: +- `packages_lottery/pages/ai_predict` 当前用于通用彩票 AI 预测,不再按 `template=HK6` 自动展示六合彩专属“波色 & 生肖分析”模块。 +- 已移除该页面对 `/lottery/aiColorZodiac` 的请求、`isHK6/colorZodiac` 页面状态、缓存恢复逻辑和相关样式,避免非六合彩彩种误显示生肖分析。 +- 保留历史开奖、热冷号、趋势推荐、统计数据四个通用分析模块,现有入口参数和其它彩票接口不变。 + +涉及模块: +- `uniapp/src/packages_lottery/pages/ai_predict.vue` + +验证记录: +- 已先用临时静态检查确认修改前会命中 `getLotteryAiColorZodiac` 引用,修改后确认页面不再包含 `colorZodiac`、`isHK6`、`getLotteryAiColorZodiac`、`生肖分析` 等残留。 +- 已执行 `npm run build:h5`,H5 构建通过;构建生成的 `server/public/mobile` 哈希产物已清理回构建前状态,避免混入源码修复。 +- 本次未执行测试服部署命令:当前仓库已有多处与本任务无关的未提交改动,且 `scripts/deploy-server.ps1` 会对发布范围内未提交内容自动 `git add`/`git commit`,为避免卷入他人改动,暂不触发自动部署。 + +### 69. Docker 爬虫源码迁移到 docker/crawler 并稳定部署路径 + +- 状态:已完成 - 时间:2026-06-12 + +完成内容: +- 懂球帝爬虫源码已迁移到 `docker/crawler`,Docker Compose 默认构建上下文和 `logs/data` 挂载路径统一改为 `${CRAWLER_DIR:-./crawler}`,避免继续依赖 `server/public/dongqiudi-crawler`。 +- 新增 `docker/.env.example`,服务器如保留 `docker/.env`,需要设置 `CRAWLER_DIR=./crawler`,否则可删除该变量使用 Compose 默认值。 +- 根目录部署默认范围继续包含 `server/`、`uniapp/`、`admin/` 与 `docker/`,并默认排除 `docker/.env`、`docker/.env.docker`、`docker/crawler/venv`、`docker/crawler/logs`、`docker/crawler/data`、`test_output.txt` 与临时脚本,避免密钥、日志、缓存和样例输出进入发布包。 +- 默认发布范围内如仍存在 `admin` 这类 root gitlink,部署脚本会跳过该 gitlink 的自动提交 pathspec,但仍从当前工作区把源码补进发布包,避免自动提交阶段被 gitlink 卡住。 +- `docker/crawler/Dockerfile` 在镜像构建时会把 `/app/docker/entrypoint.sh` 的 CRLF 换行归一为 LF,避免 Windows 上传后入口脚本报 `/usr/bin/env: 'bash\r': No such file or directory`。 +- `.vscode/sftp.json` 新增 `docker` 上传配置,远端目录为 `/www/wwwroot/test-server.sbnews.net/docker`,并忽略 Docker 密钥与 crawler 运行态目录。 +- `README.md` 已同步 Docker 爬虫源码目录、任务清单路径、默认发布目录结构和服务器 `CRAWLER_DIR` 注意事项。 + +涉及模块: +- `docker/docker-compose.crawler.yml` +- `docker/.env.example` +- `docker/.gitignore` +- `docker/crawler/Dockerfile` +- `docker/crawler/.dockerignore` +- `docker/crawler/.gitignore` +- `.vscode/sftp.json` +- `scripts/deploy-server.ps1` +- `README.md` + +验证记录: +- 已执行 `git diff --check -- docker/docker-compose.crawler.yml docker/.env.example docker/.gitignore docker/crawler/.dockerignore docker/crawler/.gitignore scripts/deploy-server.ps1 README.md 业务进度管理.md`,未发现空白错误。 +- 已执行 `deploy-server.ps1` PowerShell 语法解析检查,通过。 +- 已在临时 Git 克隆中执行 `powershell -ExecutionPolicy Bypass -File .\scripts\deploy-server.ps1 -RemoteDir /www/wwwroot/test-server.sbnews.net -DryRun`,确认全量包默认包含 `server`、`uniapp`、`admin`、`docker`,且未上传到服务器。 +- 已在临时 Git 克隆中执行 `powershell -ExecutionPolicy Bypass -File .\scripts\deploy-server.ps1 -AutoIncremental -AutoIncrementalCommits 3 -RemoteDir /www/wwwroot/test-server.sbnews.net -DryRun`,确认迁移提交范围会上传 `docker/crawler` 源码并跳过 `docker/crawler/data`、`logs`、`venv`、`test_output.txt` 等运行态/输出文件。 +- 本机未安装 `docker` 命令,因此未在本机执行 `docker compose -f docker-compose.crawler.yml config`;该项需要在服务器或已安装 Docker 的环境验证。 + ### 68. 默认部署切换为根目录同时上传 server、uniapp 与 admin - 状态:已完成 - 时间:2026-06-12