From 6cac15a14d299f6ff6846279c17b99315b56e9d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=96=8C?= <259278618@qq.com> Date: Tue, 25 Aug 2026 18:05:48 +0800 Subject: [PATCH] 1 --- flutter_app/lib/payment/paywall_screen.dart | 19 ++ server/Dockerfile | 2 + server/README.md | 4 + server/h5/index.html | 201 ++++++++++++++++++++ server/main.go | 6 + 5 files changed, 232 insertions(+) create mode 100644 server/h5/index.html diff --git a/flutter_app/lib/payment/paywall_screen.dart b/flutter_app/lib/payment/paywall_screen.dart index a052329..fbd99bb 100644 --- a/flutter_app/lib/payment/paywall_screen.dart +++ b/flutter_app/lib/payment/paywall_screen.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'models.dart'; @@ -73,6 +74,24 @@ class _PaywallScreenState extends State { } }, ), + const SizedBox(height: 12), + _PayButton( + label: '联系管理员', + icon: Icons.headset_mic, + color: Colors.grey.shade600, + enabled: true, + onTap: () async { + await Clipboard.setData( + const ClipboardData(text: 'wenwu901'), + ); + if (!context.mounted) return; + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('已复制管理员微信号,请使用微信添加联系人'), + ), + ); + }, + ), if (vm.state.state == PayState.failed) Padding( padding: const EdgeInsets.only(top: 16), diff --git a/server/Dockerfile b/server/Dockerfile index fa4945b..7aa8d7c 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -23,6 +23,8 @@ COPY --from=builder /out/observer-server /app/observer-server COPY config.yml /app/config.yml # 管理端静态产物(server_admin 构建输出),由 /admin 路径托管 COPY --from=builder /build/admin_dist /app/admin_dist +# APK 下载引导页(源码目录 h5/),由 /download-page 路径托管 +COPY --from=builder /build/h5 /app/h5 # 运行时数据目录(SQLite 库),由 docker-compose 挂载持久化 RUN mkdir -p /app/data EXPOSE 8080 diff --git a/server/README.md b/server/README.md index e7fff19..dbdedaa 100644 --- a/server/README.md +++ b/server/README.md @@ -151,6 +151,10 @@ App 版本更新检查(公开接口,无需 token,未登录/旧版本均可 - 客户端以「语义化版本号」比较:`1.10.0 > 1.9.9`(按数字段比较,禁止字符串比较) - 下载地址为固定静态路径:`/download/observer-latest.apk`(`app.apkDir` 目录下永远只有最新一个文件,由后端静态托管),客户端拼 `apiBaseUrl` 访问 +### GET /download-page + +APK 下载引导页(静态页面,源码在 `h5/index.html`,由后端 `/download-page` 路径托管):微信内置浏览器会拦截 APK 下载,此页面按打开环境分流——**微信内打开**显示图形引导(点击右上角「···」→「在浏览器打开」+ 复制链接兜底);**手机浏览器打开**直接显示下载按钮,直链 `http://observer.redpowerfuture.com/download/observer-latest.apk`。 + ### 管理端接口(`/api/v1/admin`,需请求头 `X-Admin-Token` = `config.yml admin.token`) | 方法 | 路径 | 说明 | diff --git a/server/h5/index.html b/server/h5/index.html new file mode 100644 index 0000000..e5c9ff0 --- /dev/null +++ b/server/h5/index.html @@ -0,0 +1,201 @@ + + + + + + + +视野 - App 下载 + + + +
+ + + + + + + +
视野 · 动物实时识别
+
+ + + + diff --git a/server/main.go b/server/main.go index a3a1d02..9678281 100644 --- a/server/main.go +++ b/server/main.go @@ -42,6 +42,12 @@ func main() { group.Middleware(common.UnifiedResponse, common.AdminAuth) common.BindController(group, controller.Admin) }) + // APK 下载引导页(h5/ 源码目录):微信内打开提示用手机浏览器,非微信直显下载按钮 + if stat, err := os.Stat("./h5"); err == nil && stat.IsDir() { + s.AddStaticPath("/download-page", "./h5") + } else { + g.Log().Warningf(ctx, "h5 目录不存在,跳过下载引导页托管") + } // 管理端静态页面托管:构建产物输出到 admin_dist(server_admin/ 构建),SPA history 路由回退 index.html; // 目录不存在(尚未构建前端)时跳过,不影响 API 启动 if stat, err := os.Stat("./admin_dist"); err == nil && stat.IsDir() {