**Push Server - 使用与变更说明** 简要说明 - 该文档记录对 `server/push-server.js` 的修改、运行所需的环境变量、表结构依赖、以及如何把 Supabase 的 cid 与通知通过云函数(`CLOUD_FUNC_URL`)下发的端到端操作步骤。 组件补充(新增) - `server/notify-worker.js`:常驻 worker,从 `notify_queue` 生成/写入 `express_notifications`(消息入队)。 - 说明文档:`server/NOTIFY_WORKER_README.md`(包含技术栈/依赖、配置项、监控与管理者摘要)。 变更要点(代码修改摘要) - supaFetch: 默认仅发送 `apikey`;仅当 `SUPA_USE_BEARER=true` 时才发送 `Authorization: Bearer`。用于避免自托管 Supabase/Kong 场景下因 JWT_SECRET 不一致触发 `PGRST301`。 - 新增 endpoint `/api/v1/notifications`:将通知写入 `express_notifications`(排队);由消费者(轮询)读取待处理记录并 POST 到 `CLOUD_FUNC_URL`。 - 仅云函数模式:`server/push-server.js` 已禁用 UNI‑PUSH/代理分支,发送只走 `CLOUD_FUNC_URL`。 新增/修改的接口(简要) - GET `/health` — 健康检查。 - POST `/api/v1/push/register` — 注册/更新设备;会写本地 `server/data/push_devices.json`,并尝试 upsert 到 Supabase `push_devices` 表(如果配置了 SUPA_URL + SERVICE_ROLE_KEY)。 - 支持字段:`cid`(必填)、`user_id`(可选)、`merchant_id`(可选)、`platform`(可选)、`appid`(可选,默认 `default`)。 - 注意:数据库侧 `push_devices` 的唯一约束是 `(appid, cid)`;服务端 upsert 使用 `on_conflict=appid,cid`。 - POST `/api/v1/push/unregister` — 注销设备(本地并尝试同步 Supabase)。 - GET `/api/v1/push/devices` — 列出设备(优先从 Supabase 获取)。 - POST `/api/v1/push/send` — 直接按 `cids` 或 `user_id` 发送推送(仅云函数:对每个 cid POST 到 `CLOUD_FUNC_URL`)。 - POST `/api/v1/notifications` — 将通知写入 `express_notifications`(排队);实际下发由消费者完成。 依赖的数据库表(必须存在) - `public.push_devices`:用于存储设备 cid、user_id/merchant_id、is_active 等(见仓库迁移脚本 `20260224_add_push_devices_and_notifications.sql`)。 - `public.express_notifications`:用于保存通知记录与状态(见迁移脚本)。 字段语义(从 2026-03-09 起,重要变更) - `status_code`:物流/业务状态(如 `OUT_FOR_DELIVERY/DELIVERED/...`),由 `notify-worker` 写入。 - `send_status`:投递处理状态(`null`=待发送,`processing/retrying/success/failed/no-targets`),由 push-server consumer 读写。 为什么需要这个变更:引入 `notify-worker` 后,`status_code` 不再适合作为“投递状态”,否则 consumer 会捞不到记录(或把物流状态误当投递状态)。 迁移脚本(必需执行一次): - `pages/mall/delivery/doc/需求文档/20260309_add_express_notifications_send_status.sql` 消息生成(可选但推荐) - `public.notify_queue`:轨迹事件入队表(由 DB trigger 写入)。 - 迁移脚本:`pages/mall/delivery/doc/需求文档/20260309_add_notify_queue_and_trigger.sql`(创建 `notify_queue` + `AFTER INSERT` trigger)。 - 常驻消费者:`server/notify-worker.js`(从 `notify_queue` 生成 `express_notifications`;push-server consumer 再负责下发)。 关键环境变量(示例与说明) - SUPA_URL — Supabase REST(PostgREST)地址(内部建议 `http://rest:3000`)。 - SERVICE_ROLE_KEY 或 SUPA_KEY — 用作 `apikey` 向 PostgREST 请求(不要把明文放到 Authorization 除非该值确为 JWT)。 - SUPA_USE_BEARER — (可选) 若为 `true` 则强制发送 Authorization: Bearer 。 - ENABLE_CONSUMER / CONSUMER_ENABLED — 启用消费者轮询(从 express_notifications 读取待处理记录)。 - CONSUMER_POLL_MS — 轮询间隔(毫秒)。 - CLOUD_FUNC_URL — 云函数外网调用 URL(每个目标 cid 会对该 URL 发 POST)。 - PUSH_TOKEN — (可选) 云函数鉴权 token(会在 POST body 的 token 字段中透传)。 - PUSH_SERVER_PORT — (可选) push-server 监听端口,默认 7301(推荐用这个,便于与 webhook-receiver 共享同一份 `server/config.json`)。 运行与测试(本地示例) 1) 安装依赖并启动: ```bash cd server npm install express body-parser cors node-fetch SUPA_URL='http://rest:3000' SERVICE_ROLE_KEY='PASTE_SERVICE_ROLE_KEY' node push-server.js ``` 2) 健康检查: ```bash curl http://localhost:7301/health # 返回 {"ok":true} ``` 3) 注册设备(后端会写本地并尝试写 Supabase): ```bash curl -X POST http://localhost:7301/api/v1/push/register \ -H 'Content-Type: application/json' \ -d '{"cid":"test-cid-1","user_id":"","platform":"android","appid":"default"}' ``` 商家端注册(写 merchant_id 维度,避免 `send_status=no-targets`): ```bash curl -X POST http://localhost:7301/api/v1/push/register \ -H 'Content-Type: application/json' \ -d '{"cid":"test-cid-2","merchant_id":"","platform":"android","appid":"default"}' ``` 4) 按 user 发通知(写入 express_notifications 并触发推送): ```bash curl -X POST http://localhost:7301/api/v1/notifications \ -H 'Content-Type: application/json' \ -d '{"aud":"user","recipient_id":"","notification":{"title":"测试","body":"uni-push 测试"}}' ``` > 说明(仅云函数模式):`/api/v1/notifications` 仅写入 `express_notifications`(排队)。实际下发由消费者轮询后对 `CLOUD_FUNC_URL` 执行 POST。 端到端验证(推荐) ----------------- 前置条件 - `CLOUD_FUNC_URL` 可访问且返回 2xx(否则会被标记为 failed/retrying)。 - 目标用户在 `push_devices` 中存在至少一个 `is_active=true` 的设备(否则会被标记为 no-targets)。 PowerShell 示例(Windows) ```powershell # 1) 注册设备(写入 push_devices) Invoke-RestMethod -Uri 'http://localhost:7301/api/v1/push/register' -Method POST -ContentType 'application/json' -Body (@{ cid='CID_TEST_001' user_id='a8e3a568-fc1f-4237-bcc5-5722e2fca0a3' platform='android' } | ConvertTo-Json) # 2) 写入通知(排队) $body = @{ aud='user' recipient_id='a8e3a568-fc1f-4237-bcc5-5722e2fca0a3' notification=@{ title='测试'; body='hello' } payload=@{ order_id='123' } } | ConvertTo-Json -Depth 6 Invoke-RestMethod -Uri 'http://localhost:7301/api/v1/notifications' -Method POST -ContentType 'application/json' -Body $body # 3) 等待 2 秒(CONSUMER_POLL_MS 默认 2000),观察 push-server 控制台日志:应出现对 CLOUD_FUNC_URL 的 POST ``` 如何确认处理结果 - 在 Supabase 中查询最近记录: ```sql select id, message_id, send_status, status_code, retry_count, last_error, updated_at from public.express_notifications order by created_at desc limit 10; ``` 5) 直接按 cid 发(跳过 DB): ```bash curl -X POST http://localhost:7301/api/v1/push/send \ -H 'Content-Type: application/json' \ -d '{"cids":["test-cid-1"],"notification":{"title":"hi","body":"msg"}}' ``` UNI‑PUSH 集成注意事项 故障与排查要点 - 如果 Supabase 报 401 或 PGRST301:不要把明文 service key 作为 Bearer;使用 `apikey` header,或生成并使用与 `PGRST_JWT_SECRET` 匹配的 JWT。可通过 `docker inspect` 检查容器 env 中的 `PGRST_JWT_SECRET`。 - 如果 `/rest/v1/push_devices` 返回 404:确认表名在 `public` schema 中并加载,或调整请求前缀。 - 查看 push-server 控制台输出中的 `supaFetch` warn 和 proxy 响应体以获取具体错误信息。 - 如果 `/api/v1/push/send` 返回 HTTP 200 但 body 为 `{ errCode: 400, errMsg: "push_clientid required" }`: - 含义:云函数没有拿到 `push_clientid`,因此没有真正执行 uni-push 下发。 - 常见原因:`CLOUD_FUNC_URL` 指向 HTTP 触发云函数(cloudbasefunction.cn 等),请求体通常在 `event.body`(字符串)中。 - 云函数侧最小兼容写法:先把 `event.body` 解析为 JSON,再从解析结果解构 `push_clientid/title/content`。 - 如果手机通知标题/内容变成 `????`: - 含义:中文在发出请求前已被错误编码(Windows PowerShell 5.1 常见)。 - 解决:用 UTF-8 字节发送 JSON(并设置 `application/json; charset=utf-8`)。示例: ```powershell $bodyObj = @{ cids = @('YOUR_DEVICE_CID') notification = @{ title = '测试标题'; body = '测试内容' } payload = @{ order_id = '123' } } $json = $bodyObj | ConvertTo-Json -Depth 10 $bytes = [System.Text.Encoding]::UTF8.GetBytes($json) Invoke-RestMethod -Method Post -Uri 'http://127.0.0.1:7301/api/v1/push/send' ` -ContentType 'application/json; charset=utf-8' ` -Body $bytes | ConvertTo-Json -Depth 20 ``` - 备注:也可用 `curl.exe` 发送 JSON,避免 PowerShell 的编码差异。 - 如果你在云函数里加了“回显 recv 并提前 return”用于排查乱码: - 排查完成后务必移除该提前 return,否则云函数不会执行 `uniPush.sendMessage()`,手机将收不到通知。 后续建议(可选实现) - 将 `express_notifications` 增加 `attempts`、`error`、`sent_at` 字段以支持重试与错误记录;可实现后台 worker 或 pg_notify+listener 做可靠投递与重试。 - 为 `/api/v1/push/send` 与 `/api/v1/notifications` 添加管理员鉴权(例如 `PUSH_ADMIN_KEY`)以限制谁能发送通知。 文件位置 - 文档:[server/PUSH_SERVER_README.md](server/PUSH_SERVER_README.md) - 代码:[server/push-server.js](server/push-server.js) 自动化部署(可选) - 本仓库新增了一个打包并可选上传云函数的脚本:`server/tools/deploy-cloudfunc.js`,会把 `uniCloud-alipay/cloudfunctions/testUnipush2` 打包到 `server/dist/testUnipush2.zip`。 - 使用前请在 `server` 目录安装依赖: ```bash cd server npm install archiver node-fetch form-data ``` - 本地打包(只生成 zip): ```bash node server/tools/deploy-cloudfunc.js ``` - 如果你有云平台的上传 URL,可直接上传(脚本会读取 `CLOUD_UPLOAD_URL` 与 `CLOUD_UPLOAD_TOKEN` 环境变量,或使用 `--upload ` 参数): ```bash export CLOUD_UPLOAD_URL='https://your-cloud-upload-api' export CLOUD_UPLOAD_TOKEN='xxxxx' node server/tools/deploy-cloudfunc.js --upload ``` - 我也添加了一个 GitHub Actions 模板(.github/workflows/deploy-cloudfunc.yml),可在仓库的 `main` 分支 push 时自动打包并上传(需要在 Actions Secrets 中配置 `CLOUD_UPLOAD_URL` 与 `CLOUD_UPLOAD_TOKEN`)。 CI / 自动化 说明 - 已提供 GitHub Actions workflow: [.github/workflows/deploy-cloudfunc.yml](.github/workflows/deploy-cloudfunc.yml)。支持可选步骤:上传 -> 触发部署 API -> 调用云函数做 smoke test。 - 推荐在仓库 Secrets 中添加(根据你云厂商调整): - `CLOUD_UPLOAD_URL` : 上传 zip 的 HTTP endpoint(upload 接口) - `CLOUD_UPLOAD_TOKEN` : 上传接口的 Bearer token - `CLOUD_DEPLOY_API` : (可选)触发云函数部署/发布的 API - `CLOUD_DEPLOY_TOKEN` : (可选)部署 API 的 token - `CLOUD_FUNC_URL` : (可选)已部署云函数的外网 URL,CI 将对其执行一次 smoke 测试 - `PUSH_TOKEN` : (可选)云函数鉴权 token,用于 smoke 测试 - `TEST_DEVICE_CID` : (可选)用于 CI smoke 测试的设备 CID - 我也添加了本地 helper 脚本:`server/tools/ci-deploy.ps1`,可在本地执行相同流程(打包 -> 上传 -> 触发 -> 调用)。 如果需要,我可以: - 把 adapter 的请求体精确匹配你现有成功的 uni-push curl(请把 curl 发来);或 - 为通知添加重试/记录字段并实现简单重试机制。 自动部署成功示例 ----------------- 下面是一个你实际执行并确认成功的示例,便于复制到文档或在团队内复现: - 启用自动部署并设置 token(PowerShell 示例): ```powershell $env:AUTO_DEPLOY_ON_START='true' $env:DEPLOY_BEARER='your-secret' # 请使用真实随机 token 暂无 $env:CLOUD_UPLOAD_URL='https://env-00jy5x5oy9zd.dev-hz.cloudbasefunction.cn/test' $env:CLOUD_UPLOAD_TOKEN='upload-token' # 如果上传需要鉴权 $env:AUTO_DEPLOY_ARGS='--upload' # 可选:将 --upload 传给 deploy 脚本 node push-server.js ``` - 预期控制台输出(已在你的环境中观察到): ``` Push server listening on http://0.0.0.0:7301 ENV: CLOUD_FUNC_URL configured? true ENV: ENABLE_CONSUMER= true Auto-deploy: spawning node D:\...\server\tools\deploy-cloudfunc.js [auto-deploy stdout] 打包目录: D:\...\uniCloud-alipay\cloudfunctions\testUnipush2 [auto-deploy stdout] 打包完成 -> D:\...\server\dist\testUnipush2.zip (1324 bytes) [auto-deploy stdout] 上传到: https://env-00jy5x5oy9zd.dev-hz.cloudbasefunction.cn/test [auto-deploy stdout] 上传响应: 200 {"data":{...},"errCode":0,"errMsg":"success"} Auto-deploy process exited with code=0 signal=null ``` - 说明: - `errCode:0` / `errMsg: "success"` 表示云平台已接收并下发(上线)该云函数包。具体字段会随云厂商略有不同,请以返回的 `errCode` / `errMsg` 或 `status` 为准。 - 若你需要进一步验证云函数可用性,请用 `curl` 或 `Invoke-RestMethod` 调用返回的云函数 URL 做一次 smoke test(见上文的“直接调用云函数(smoke test)”示例)。 安全提醒:自动部署具有上传并触发发布的能力,请仅在受控环境或 CI 中启用,并把 `DEPLOY_BEARER` / `CLOUD_UPLOAD_TOKEN` 等密钥放入 CI Secrets 或受限的环境变量中。 快速上手(最小环境变量) - 如果你只是想快速上传并通过云函数下发一次推送,最小只需设置两个环境变量并运行打包脚本: PowerShell 示例: ```powershell $env:CLOUD_UPLOAD_URL='https://env-00jy5x5oy9zd.dev-hz.cloudbasefunction.cn/test' $env:UNI_PUSH_APPID='__UNI__9462CA7' node tools\deploy-cloudfunc.js --upload ``` 说明:设置 `CLOUD_UPLOAD_URL` 与 `UNI_PUSH_APPID` 后,运行 `deploy-cloudfunc.js` 会把 `uniCloud-alipay/cloudfunctions/testUnipush2` 打包并上传到该地址,上传成功后云平台会返回已下发/上线的结果,从而完成推送通路的测试。 **用户整合指南(一步到位)** 下面把之前讨论过的流程与命令整合成一个可复用的清单,便于在本地或 CI 中自动化执行: - 先决条件:在 `server` 目录下有 `tools/deploy-cloudfunc.js`,并且已安装依赖 `archiver node-fetch form-data`。 1) 本地打包并上传(最小) PowerShell: ```powershell cd D:\叶\桌面文件\hfkj_mall\mall\server npm install archiver node-fetch form-data $env:CLOUD_UPLOAD_URL='https://env-00jy5x5oy9zd.dev-hz.cloudbasefunction.cn/test' $env:UNI_PUSH_APPID='__UNI__9462CA7' node tools\deploy-cloudfunc.js --upload ``` 说明:这两条环境变量(`CLOUD_UPLOAD_URL` + `UNI_PUSH_APPID`)在多数场景下已足够完成上传并让云平台下发推送;上传成功时脚本会打印上传后的 URL 与平台响应(检查返回的 `errCode` / `data` 字段以确认)。 2) 直接调用云函数(smoke test) PowerShell(使用 `curl.exe` 保持与 Linux curl 行为一致): ```powershell curl.exe -X POST "https://env-.../test" ` -H "Content-Type: application/json" ` -d "{\"token\":\"\",\"push_clientid\":\"\",\"title\":\"测试\",\"content\":\"hi\",\"payload\":{}}" ``` 或用 PowerShell 原生: ```powershell $body = @{ token = ''; push_clientid = ''; title='测试'; content='hi'; payload=@{} } | ConvertTo-Json Invoke-RestMethod -Method Post -Uri 'https://env-.../test' -ContentType 'application/json' -Body $body ``` 3) 本地辅助脚本(整合流程) - 已添加 `server/tools/ci-deploy.ps1`,支持:打包、上传、触发部署 API、调用云函数做 smoke test。用法示例: ```powershell # 只打包 .\server\tools\ci-deploy.ps1 -Pack # 打包并上传并触发部署 .\server\tools\ci-deploy.ps1 -Pack -UploadUrl 'https://your-upload' -UploadToken 'token' -DeployApi 'https://your-deploy-api' -DeployToken 'token' # 打包并上传然后调用云函数做 smoke test .\server\tools\ci-deploy.ps1 -Pack -UploadUrl 'https://your-upload' -UploadToken 'token' -FuncInvokeUrl 'https://env-.../test' -PushToken 'xxx' -TestCid 'device-cid' ``` 4) CI(GitHub Actions) - workflow: `.github/workflows/deploy-cloudfunc.yml` 已包含:打包 -> 上传 ->(可选)触发部署 API ->(可选)调用云函数做 smoke test。 - 建议在 Actions Secrets 中添加: - `CLOUD_UPLOAD_URL`(必需用于上传) - `CLOUD_UPLOAD_TOKEN`(上传 token) - `CLOUD_DEPLOY_API` / `CLOUD_DEPLOY_TOKEN`(可选,用于触发厂商部署) - `CLOUD_FUNC_URL` / `PUSH_TOKEN` / `TEST_DEVICE_CID`(可选,用于 smoke test) 5) 后端直接单推(如果你愿意绕过云函数) - 启动 `push-server`(示例): ```powershell cd D:\叶\桌面文件\hfkj_mall\mall\server npm install SUPA_URL='http://rest:3000' SERVICE_ROLE_KEY='PASTE_SERVICE_ROLE_KEY' node push-server.js ``` - 直接按 CID 发送(跳过 DB): ```powershell curl.exe -X POST "http://localhost:7301/api/v1/push/send" -H "Content-Type: application/json" -d "{\"cids\":[\"\"],\"notification\":{\"title\":\"hi\",\"body\":\"msg\"}}" ``` 注意与安全建议 - `uniCloud.uploadFile` 可以把 zip 存到云端存储(返回 `fileID`),但不会自动部署为云函数;若要自动部署需要调用云厂商的部署/发布 API(或使用厂商控制台/CLI)。 - 保管好 `CLOUD_UPLOAD_TOKEN`、`CLOUD_DEPLOY_TOKEN`、`SERVICE_ROLE_KEY` 等机密,放到 CI Secrets 或安全环境变量中,不要硬编码在仓库。 - 上传后若要确认运行状态,请查看云平台函数控制台或调用厂商的状态/日志接口;CI 的 smoke test 能快速验证下发路径是否通顺。 如果你希望,我可以把上面示例中的 `curl` 调用替换成你厂商的部署 API 或把 `ci-deploy.ps1` 改为更严格的错误处理与日志输出。 部署服务文件说明 ---------------- 以下两个文件用于让后端通过一个 HTTP 接口自动完成云函数的打包、上传、触发部署与可选的 smoke-test: - `server/tools/deploy-cloudfunc-service.js`: - 目的:封装打包(zip)、上传、触发部署 API、调用云函数(smoke-test)的通用函数,便于从后端代码调用或在 CI 中复用。 - 主要导出:`deployCloudFunction(options)`,接收的 `options` 支持字段: - `uploadUrl`(必需或由环境变量提供):上传 zip 的 HTTP endpoint。 - `uploadToken`:上传接口的 Bearer token(可选)。 - `uniAppId`:可附加到上传请求的 appId(可选)。 - `deployApi` / `deployToken`:如果需要调用厂商的部署/发布 API,可指定(可选)。 - `funcInvokeUrl` / `pushToken` / `testCid`:若提供,将在上传/部署后调用该云函数进行 smoke-test(可选)。 - 返回值:一个对象,包含 `packed`(打包信息)、`uploaded`(上传响应)、`deployed`(触发部署 API 的响应)和 `invoked`(smoke-test 调用响应)四部分,便于在后端记录和判断各个阶段的结果。 - `server/routes/deploy.js`: - 目的:提供一个 Express 路由 `/api/v1/deploy-cloudfunc`,让后端或 CI 通过一次 HTTP POST 调用完成上面封装的流程。 - 请求体示例(JSON): ```json { "uploadUrl":"https://your-upload-endpoint", "uploadToken":"", "uniAppId":"__UNI__9462CA7", "deployApi":"https://your-deploy-api", "deployToken":"", "funcInvokeUrl":"https://env-.../test", "pushToken":"", "testCid":"" } ``` - 行为:路由优先使用请求体中的字段;若缺失则回落使用环境变量(例如 `CLOUD_UPLOAD_URL`、`CLOUD_UPLOAD_TOKEN`、`UNI_PUSH_APPID`、`CLOUD_DEPLOY_API`、`CLOUD_DEPLOY_TOKEN`、`CLOUD_FUNC_URL`、`PUSH_TOKEN`、`TEST_DEVICE_CID`)。 - 返回:JSON 格式,包含 `result` 对象(见 `deployCloudFunction` 的返回结构)。 安全与接入建议 - 强烈建议只在内网或受保护的管理接口上暴露该路由,或在请求中加入鉴权头(例如 `Authorization: Bearer `)并在服务端验证。此接口具有上传并触发部署的能力,若未保护将导致安全风险。 - 如果你需要,我可以: - 把路由自动挂载到 `server/push-server.js`(并添加简单的 token 验证);或 - 增强 `deploy-cloudfunc-service.js` 的错误日志与重试策略,或把上传/部署调用改写成你云厂商的精确 API 参数格式。 异步 Consumer(已实现说明) --------------------------------- - 目的:从数据库 `express_notifications` 拉取待发送的消息(pending),解耦写入与下发流程,保证可重试、可审计与可运维。 - 实现位置:`server/push-server.js`(已新增 consumer 逻辑)。 - 启用:设置环境变量 `ENABLE_CONSUMER=true`(或 `CONSUMER_ENABLED=true`),可选配置轮询间隔 `CONSUMER_POLL_MS`(默认 2000 ms)。 - 轮询频率说明:默认 `CONSUMER_POLL_MS` 为 `2000`(即每 2000 毫秒,2 秒轮询一次)。若需调整频率,请设置环境变量 `CONSUMER_POLL_MS` 为毫秒数。例如将间隔改为 5 秒: ```powershell $env:ENABLE_CONSUMER="true" $env:CONSUMER_POLL_MS="5000" node push-server.js ``` - 关键环境变量:`SUPA_URL`、`SUPA_KEY`(Supabase REST)、`CLOUD_FUNC_URL`(云函数 invoke URL)、`PUSH_TOKEN`(云函数鉴权)。 - 行为摘要: - 轮询 `express_notifications`(`send_status IS NULL` 或 `send_status='retrying'` 且到期)并选取记录; - 通过带过滤的 PATCH 抢占(将 `send_status` 设为 `processing`)以避免并发重复处理; - 查询目标设备(`push_devices`),对每个 `cid` 构造 event 并 POST 到 `CLOUD_FUNC_URL`;若未配置 `CLOUD_FUNC_URL` 则本次处理将失败并写入错误原因; - 根据调用结果回写 `express_notifications.send_status` 为 `success` / `failed` / `no-targets`(失败时会写入 `retry_count/last_error/next_attempt_at` 用于退避重试)。 - 限制与扩展点:当前 consumer 依赖 Supabase REST;并发控制基于“先查后 claim”的方式实现(适合 MVP)。如需更强一致性/高并发,可进一步改造为 DB 端锁(`FOR UPDATE SKIP LOCKED`)或引入队列系统。 文档实践 --------------------------------- - 变更策略:今后每次由自动化工具或我生成/修改后端代码时,将同步把该变更的作用、配置项与启用方法记录到本文件(`server/PUSH_SERVER_README.md`)或对应 workflow 文档中,确保运行/运维人员能直接查阅到最新说明。