From 91264adc87a289f7f92b4578bf37d213d291015c Mon Sep 17 00:00:00 2001 From: Alex-larget <33240357+Alex-larget@users.noreply.github.com> Date: Wed, 8 Apr 2026 09:37:04 +0800 Subject: [PATCH] 1 --- miniprogram/app.js | 6 +-- miniprogram/pages/read/read.js | 30 ++++++++++++--- soul-admin/.deploy-manager.sync.json | 15 ++++++++ soul-api/.env copy.development | 36 +++++++++--------- soul-api/deploy/Dockerfile | 57 ++++++++++++++++++++++++---- soul-api/deploy/README.md | 7 ++-- soul-api/deploy/docker-compose.yml | 9 ++++- 7 files changed, 122 insertions(+), 38 deletions(-) create mode 100644 soul-admin/.deploy-manager.sync.json diff --git a/miniprogram/app.js b/miniprogram/app.js index f638834f..14f12284 100644 --- a/miniprogram/app.js +++ b/miniprogram/app.js @@ -11,11 +11,11 @@ const DEFAULT_APP_ID = 'wxb8bbb2b10dec74aa' const DEFAULT_MCH_ID = '1318592501' const DEFAULT_WITHDRAW_TMPL_ID = 'u3MbZGPRkrZIk-I7QdpwzFxnO_CeQPaCWF2FkiIablE' // 线上正式域名(真机/体验版/正式版强制使用此地址) -const API_BASE_URL_PROD = 'https://soulapi.quwanzhi.com' -// const API_BASE_URL_PROD = 'https://souldev.quwanzhi.com' +// const API_BASE_URL_PROD = 'https://soulapi.quwanzhi.com' +const API_BASE_URL_PROD = 'https://souldev.quwanzhi.com' // 本地开发地址(仅开发者工具模拟器生效;真机自动回退 PROD) -// const API_BASE_URL_DEV = 'http://127.0.0.1:9090' +// const API_BASE_URL_DEV = 'http://127.0.0.1:9100' const API_BASE_URL_DEV = 'https://souldev.quwanzhi.com' // 运行时自动判断:非开发工具(真机/体验版/正式版)强制用 PROD,避免 localhost 打进上传包导致白屏 diff --git a/miniprogram/pages/read/read.js b/miniprogram/pages/read/read.js index 80a97955..0932d1fa 100644 --- a/miniprogram/pages/read/read.js +++ b/miniprogram/pages/read/read.js @@ -187,7 +187,27 @@ function normalizeMiniProgramNavPath(p) { /** 当前用户已绑定手机号(与 app 内登录态一致) */ function getLoggedInUserPhone() { const u = app.globalData.userInfo || {} - return (u.phone || wx.getStorageSync('user_phone') || '').trim().replace(/\s/g, '') + return (u.phone || u.phoneNumber || wx.getStorageSync('user_phone') || '').trim().replace(/\s/g, '') +} + +/** 本地没手机号时,静默刷新一次用户资料,避免缓存滞后导致漏传 */ +async function ensureLoggedInUserPhone() { + let phone = getLoggedInUserPhone() + if (phone) return phone + try { + const profileRes = await app.request({ url: '/api/miniprogram/user/profile', silent: true, timeout: 3000 }) + const p = (profileRes && profileRes.data) ? profileRes.data : {} + phone = String(p.phone || p.phoneNumber || '').trim().replace(/\s/g, '') + if (phone) { + const merged = { ...(app.globalData.userInfo || {}), ...p, phone } + app.globalData.userInfo = merged + try { + wx.setStorageSync('userInfo', merged) + wx.setStorageSync('user_phone', phone) + } catch (_) {} + } + } catch (e) {} + return phone } Page({ @@ -922,7 +942,7 @@ Page({ }, // 点击正文中的 #链接标签:小程序内页/预览页/唤醒其他小程序 - onLinkTagTap(e) { + async onLinkTagTap(e) { let url = (e.currentTarget.dataset.url || '').trim() const label = (e.currentTarget.dataset.label || '').trim() let tagType = (e.currentTarget.dataset.tagType || '').trim().toLowerCase() @@ -944,9 +964,9 @@ Page({ // 透传配置优先取点击节点;节点缺失时回落后台配置 const hasCachedPassPhone = !(cached.passPhone == null || cached.passPhone === '') if (hasCachedPassPhone) { + // 标签配置以后台 read-extras 为准,覆盖正文节点里的历史值 explicitPassPhoneConfigured = true - const cachedPassPhone = parseDatasetBool(cached.passPhone) - if (!hasDatasetPassPhone) passPhone = cachedPassPhone + passPhone = parseDatasetBool(cached.passPhone) } phoneParamName = normalizeQueryKey(cached.phoneParamName, phoneParamName) } @@ -993,7 +1013,7 @@ Page({ const linked = (app.globalData.linkedMiniprograms || []).find(m => m.key === mpKey) if (linked && linked.appId) { let targetPath = pagePath || linked.path || '' - const phone = getLoggedInUserPhone() + const phone = await ensureLoggedInUserPhone() if (passPhone && phone) { targetPath = appendQueryToPath(targetPath, phoneParamName, phone) } diff --git a/soul-admin/.deploy-manager.sync.json b/soul-admin/.deploy-manager.sync.json new file mode 100644 index 00000000..ebfc777f --- /dev/null +++ b/soul-admin/.deploy-manager.sync.json @@ -0,0 +1,15 @@ +{ + "version": 1, + "updatedAt": "2026-04-07T08:22:54.262Z", + "projectId": "1775203188158", + "projectName": "Soul-管理端(正式)", + "kind": "static", + "deploy": { + "staticAppRoot": "E:\\Gongsi\\Mycontent\\soul-admin", + "staticBasePath": "/www/wwwroot/self/soul-admin", + "staticBuildCmd": "pnpm run build", + "staticDistDir": "dist", + "remoteBasePath": "", + "domain": "" + } +} diff --git a/soul-api/.env copy.development b/soul-api/.env copy.development index 623761f7..cfc16585 100644 --- a/soul-api/.env copy.development +++ b/soul-api/.env copy.development @@ -1,50 +1,50 @@ -# 测试环境配置(air / make dev 时加载,见 .air.toml env_files) +# 测试环境配置(air / make dev 时加载,?.air.toml env_files? APP_ENV=development # 服务(启动端口在 .env 中配置,修改 PORT 后重启生效) -PORT=9090 +PORT=9100 GIN_MODE=debug -# 版本号:打包 zip 前在此填写,上传服务器覆盖 .env 后,访问 /health 会返回此版本 +# 版本号:打包 zip 前在此填写,上传服务器覆?.env 后,访问 /health 会返回此版本 APP_VERSION=0.0.0 -# 数据库(测试环境 souldev) +# 数据库(测试环境 souldev? DB_DSN=souldev:RXW2FeRcRdH2GtXy@tcp(56b4c23f6853c.gz.cdb.myqcloud.com:14413)/souldev?charset=utf8mb4&parseTime=True -# Redis(与 n-api 一致:REDIS_ADDR / REDIS_PASSWORD / REDIS_DB;也可用 REDIS_URL 覆盖) -# 宿主机直连:127.0.0.1;容器内需 host.docker.internal(compose extra_hosts),勿用 127.0.0.1 指容器自身 +# Redis(与 n-api 一致:REDIS_ADDR / REDIS_PASSWORD / REDIS_DB;也可用 REDIS_URL 覆盖? +# 宿主机直连:127.0.0.1;容器内需 host.docker.internal(compose extra_hosts),勿用 127.0.0.1 指容器自? REDIS_ADDR=host.docker.internal:26738 REDIS_PASSWORD=WZX7HbnpZErnS6Tm REDIS_DB=0 # REDIS_ADDR=host.docker.internal:6379 -# 远程库启动时跳过 AutoMigrate(表已存在,避免 information_schema 慢查询);首次建表时注释掉 +# 远程库启动时跳过 AutoMigrate(表已存在,避免 information_schema 慢查询);首次建表时注释? SKIP_AUTO_MIGRATE=1 -# 慢查询阈值(ms),默认 5000,避免 200ms 刷屏;设为 0 可恢复 GORM 默认 +# 慢查询阈?ms),默?5000,避?200ms 刷屏;设?0 可恢?GORM 默认 # SLOW_SQL_THRESHOLD_MS=5000 # DB_DSN=cdb_outerroot:Zhiqun1984@tcp(56b4c23f6853c.gz.cdb.myqcloud.com:14413)/soul_miniprogram?charset=utf8mb4&parseTime=True # 统一 API 域名(测试环境) API_BASE_URL=https://souldev.quwanzhi.com -#添加卡若(内部 API,用于 /v1/api/scenarios) +#添加卡若(内?API,用?/v1/api/scenarios? CKB_LEAD_API_KEY=2y4v5-rjhfc-sg5wy-zklkv-bg0tl -# 存客宝开放 API:创建/更新/删除获客计划、拉取设备列表 -# - CKB_OPEN_API_KEY:开放 API Key(开发文档中的 mI9Ol-NO6cS-ho3Py-7Pj22-WyK3A) +# 存客宝开?API:创?更新/删除获客计划、拉取设备列? +# - CKB_OPEN_API_KEY:开?API Key(开发文档中?mI9Ol-NO6cS-ho3Py-7Pj22-WyK3A? # - CKB_OPEN_ACCOUNT:对应的存客宝登录账号(手机号或用户名) CKB_OPEN_API_KEY=mI9Ol-NO6cS-ho3Py-7Pj22-WyK3A CKB_OPEN_ACCOUNT=karuo1 -# 微信小程序配置 +# 微信小程序配? WECHAT_APPID=wxb8bbb2b10dec74aa WECHAT_APPSECRET=3c1fb1f63e6e052222bbcead9d07fe0c WECHAT_MCH_ID=1318592501 WECHAT_MCH_KEY=wx3e31b068be59ddc131b068be59ddc2 # 支付回调:未设置时由 API_BASE_URL 派生 # WECHAT_NOTIFY_URL=https://souldev.quwanzhi.com/api/miniprogram/pay/notify -# 小程序码/订阅消息跳转版本:formal=正式版(默认) | trial=体验版 | developer=开发版 +# 小程序码/订阅消息跳转版本:formal=正式?默认) | trial=体验?| developer=开发版 WECHAT_MINI_PROGRAM_STATE=formal -# 微信转账配置(API v3) +# 微信转账配置(API v3? WECHAT_APIV3_KEY=wx3e31b068be59ddc131b068be59ddc2 # 公钥证书(本地或 OSS):https://karuocert.oss-cn-shenzhen.aliyuncs.com/1318592501/apiclient_cert.pem WECHAT_CERT_PATH=certs/apiclient_cert.pem @@ -54,16 +54,16 @@ WECHAT_SERIAL_NO=4A1DB62CD5C9BE0B6FC51C30621D6F99686E75C5 # 转账回调:未设置时由 API_BASE_URL 派生 # WECHAT_TRANSFER_URL=https://souldev.quwanzhi.com/api/payment/wechat/transfer/notify -# 管理端登录(与 next-project 一致,默认 admin / admin123) +# 管理端登录(?next-project 一致,默认 admin / admin123? # ADMIN_USERNAME=admin # ADMIN_PASSWORD=admin123 # ADMIN_SESSION_SECRET=soul-admin-secret-change-in-prod -# 可选:信任代理 IP(逗号分隔),部署在 Nginx 后时填写 +# 可选:信任代理 IP(逗号分隔),部署?Nginx 后时填写 # TRUSTED_PROXIES=127.0.0.1,::1 -# 跨域 CORS:允许的源,逗号分隔。未设置时使用默认值(含 localhost、soul.quwanzhi.com) +# 跨域 CORS:允许的源,逗号分隔。未设置时使用默认值(?localhost、soul.quwanzhi.com? CORS_ORIGINS=http://localhost:5175,http://localhost:5174,http://127.0.0.1:5174,https://soul.quwanzhi.com,http://soul.quwanzhi.com,https://souladmin.quwanzhi.com,http://souladmin.quwanzhi.com -# 定时任务密钥:调用 /api/cron/* 时须携带请求头 X-Cron-Secret: (留空则开发模式不校验) +# 定时任务密钥:调?/api/cron/* 时须携带请求?X-Cron-Secret: (留空则开发模式不校验? CRON_SECRET= diff --git a/soul-api/deploy/Dockerfile b/soul-api/deploy/Dockerfile index 8ec3e282..f098ca1d 100644 --- a/soul-api/deploy/Dockerfile +++ b/soul-api/deploy/Dockerfile @@ -1,20 +1,64 @@ # 以下由「部署管理器」根据项目配置在保存时自动生成/更新 # 根据「构建命令」解析编译目标;可在项目表单中修改 buildCmd +# 打入镜像的目录 / 单文件 / 构建期必需文件:由 docker-compose build.args 或 docker build --build-arg 配置(见部署管理器「镜像打包」) FROM golang:1.25 AS builder -WORKDIR /src +ARG DOCKER_BUNDLE_PATHS=certs +ARG DOCKER_BUNDLE_REQUIRED_FILES= +ARG DOCKER_BUNDLE_FILES= -# 与 deploy-manager 传入的 --build-arg GOPROXY 对应;无 ARG 时 build-arg 会被忽略,go 会走默认 proxy.golang.org +# 构建容器内常无法访问 proxy.golang.org;默认 goproxy.cn,可用 --build-arg GOPROXY= 覆盖 ARG GOPROXY=https://goproxy.cn,direct ENV GOPROXY=${GOPROXY} -ARG GOSUMDB=sum.golang.google.cn -ENV GOSUMDB=${GOSUMDB} + +WORKDIR /src COPY go.mod go.sum ./ RUN go mod download COPY . . +ARG DOCKER_BUNDLE_PATHS +ARG DOCKER_BUNDLE_REQUIRED_FILES +ARG DOCKER_BUNDLE_FILES +RUN set -e; mkdir -p /bundle-out; \ + if [ -n "$DOCKER_BUNDLE_REQUIRED_FILES" ]; then \ + rem="$DOCKER_BUNDLE_REQUIRED_FILES"; \ + while [ -n "$rem" ]; do \ + case "$rem" in \ + *:*) f="${rem%%:*}"; rem="${rem#*:}";; \ + *) f="$rem"; rem="";; \ + esac; \ + [ -z "$f" ] && continue; \ + case "$f" in *..*|/*) echo "illegal required file: $f" >&2; exit 1;; esac; \ + test -f "/src/$f" || (echo "missing required file: $f" >&2; exit 1); \ + done; \ + fi; \ + if [ -n "$DOCKER_BUNDLE_PATHS" ]; then \ + for d in $DOCKER_BUNDLE_PATHS; do \ + [ -z "$d" ] && continue; \ + case "$d" in *..*|/*) echo "illegal bundle dir: $d" >&2; exit 1;; esac; \ + test -d "/src/$d" || (echo "missing bundle dir: $d" >&2; exit 1); \ + mkdir -p "/bundle-out/$d" && cp -a "/src/$d/." "/bundle-out/$d/"; \ + done; \ + fi; \ + if [ -n "$DOCKER_BUNDLE_FILES" ]; then \ + rem="$DOCKER_BUNDLE_FILES"; \ + while [ -n "$rem" ]; do \ + case "$rem" in \ + *:*) f="${rem%%:*}"; rem="${rem#*:}";; \ + *) f="$rem"; rem="";; \ + esac; \ + [ -z "$f" ] && continue; \ + case "$f" in *..*|/*) echo "illegal bundle file: $f" >&2; exit 1;; esac; \ + test -f "/src/$f" || (echo "missing bundle file: $f" >&2; exit 1); \ + df="$(dirname "$f")"; \ + mkdir -p "/bundle-out/$df"; \ + cp "/src/$f" "/bundle-out/$f"; \ + done; \ + fi; \ + touch /bundle-out/.bundle-stamp + RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" -o /out/soul-api ./cmd/server FROM alpine:3.19 @@ -25,9 +69,8 @@ RUN apk add --no-cache ca-certificates tzdata wget && \ addgroup -S app && adduser -S -G app app COPY --from=builder /out/soul-api /app/soul-api - -# 若需要微信支付证书,创建 certs/ 后取消下行注释 -# COPY certs/ /app/certs/ +COPY --from=builder /bundle-out/ /app/ +RUN rm -f /app/.bundle-stamp RUN mkdir -p /app/uploads /app/log && chown -R app:app /app diff --git a/soul-api/deploy/README.md b/soul-api/deploy/README.md index 79133999..858dd993 100644 --- a/soul-api/deploy/README.md +++ b/soul-api/deploy/README.md @@ -1,8 +1,9 @@ # 以下由「部署管理器」根据项目配置在保存时自动生成/更新 -# 测试环境soul · deploy +# Soul-测试版 · deploy -- **.env**:Compose 变量(`COMPOSE_PROJECT`、宿主机上传/日志目录、网关镜像等),与部署管理器远端流程一致。 +- **.env**:Compose 变量(`COMPOSE_PROJECT`、`COMPOSE_BG_CONTAINER_BASE` → 容器名 `{base}-blue|-green|-gateway`、宿主机上传/日志目录、网关镜像等),与部署管理器远端流程一致。 - **docker-compose.bg.yml** / **nginx.bg.conf**:蓝绿 + 网关;容器端口 **8080**,健康检查 **/health**。 -- **Dockerfile**(`deploy/Dockerfile`):由表单「构建命令」解析 `go build -o` 目标。 +- **docker-compose.yml** 的 `build.args`:`GOPROXY`(`go mod download`)、`DOCKER_BUNDLE_PATHS` / `DOCKER_BUNDLE_REQUIRED_FILES` / `DOCKER_BUNDLE_FILES`(与 Dockerfile `ARG` 一致)。 +- **Dockerfile**(`deploy/Dockerfile`):由表单「构建命令」解析 `go build -o`;可选在项目配置里设置 `dockerGoProxy` 覆盖默认 `GOPROXY`。 修改构建配置后请再次保存项目以同步本目录。 diff --git a/soul-api/deploy/docker-compose.yml b/soul-api/deploy/docker-compose.yml index 5c8ca126..13629c86 100644 --- a/soul-api/deploy/docker-compose.yml +++ b/soul-api/deploy/docker-compose.yml @@ -6,8 +6,13 @@ services: build: context: .. dockerfile: deploy/Dockerfile - image: soul-dev:latest - container_name: soul-dev + args: + GOPROXY: "https://goproxy.cn,direct" + DOCKER_BUNDLE_PATHS: "certs" + DOCKER_BUNDLE_REQUIRED_FILES: "" + DOCKER_BUNDLE_FILES: "" + image: soulceshiban:latest + container_name: soulceshiban restart: unless-stopped env_file: - ./app.env