diff --git a/backend/gateway/dto/response_dto.go b/backend/gateway/dto/response_dto.go index 99b0ce8..d456ccd 100644 --- a/backend/gateway/dto/response_dto.go +++ b/backend/gateway/dto/response_dto.go @@ -34,6 +34,7 @@ type UserWithIdentityDTO struct { SlotLimit int32 `json:"slot_limit"` AssetsNum int32 `json:"assets_num"` // assets_count CrystalBalance int64 `json:"crystal_balance"` + MobileMasked string `json:"mobile_masked,omitempty"` // 脱敏手机号,如 139****0001 } // ========== 认证相关响应 ========== diff --git a/backend/gateway/dto/user_converter.go b/backend/gateway/dto/user_converter.go index dc0fe4e..a055f00 100644 --- a/backend/gateway/dto/user_converter.go +++ b/backend/gateway/dto/user_converter.go @@ -57,6 +57,11 @@ func ToUserWithIdentityDTO(user *pb.User, profile *pb.FanProfile, star *pb.Star) dto.FanIdentity = ToFanIdentityDTO(star) } + // 脱敏手机号:139****0001 + if user.Mobile != "" && len(user.Mobile) == 11 { + dto.MobileMasked = user.Mobile[:3] + "****" + user.Mobile[7:] + } + return dto } diff --git a/docker/deploy.sh b/docker/deploy.sh index f44c5c7..7f77b96 100755 --- a/docker/deploy.sh +++ b/docker/deploy.sh @@ -45,12 +45,8 @@ NC='\033[0m' SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" -# ==================== 镜像仓库配置 ==================== -# ⚠️ 需要修改为你的阿里云仓库地址 -REGISTRY_HOST="registry.cn-hangzhou.aliyuncs.com" -NAMESPACE="你的命名空间" # ⚠️ 修改这里 - -# 服务列表(必须与 docker-compose 中的服务名一致) +# ==================== 镜像配置 ==================== +# 本地构建后打包传输到服务器,不需要镜像仓库 SERVICES=( "gateway" "userservice" @@ -62,7 +58,7 @@ SERVICES=( # ==================== 服务器配置 ==================== # ⚠️ 修改为你的服务器信息 -SERVER_HOST="" # 服务器 IP 或域名 +SERVER_HOST="101.132.250.62" # 服务器 IP 或域名 SERVER_PORT="22" # SSH 端口 SERVER_USER="root" # SSH 用户名 SERVER_PATH="/opt/topfans/docker" # 服务器上 docker 目录路径 @@ -90,31 +86,30 @@ ${YELLOW}用法:${NC} $0 <命令> [版本号] [选项] ${YELLOW}命令:${NC} - ${GREEN}build${NC} <版本号> 本地构建镜像并推送到仓库 - ${GREEN}deploy${NC} <版本号> 远程部署(从仓库拉取 + 启动服务) + ${GREEN}build${NC} <版本号> 本地构建镜像并打包传输到服务器 + ${GREEN}deploy${NC} <版本号> 远程部署(加载镜像 + 启动服务) ${GREEN}rollback${NC} <版本号> 回滚到指定版本 ${GREEN}history${NC} 查看部署历史 - ${GREEN}all${NC} <版本号> 一键构建 + 推送 + 部署 + ${GREEN}all${NC} <版本号> 一键构建 + 传输 + 部署 ${GREEN}clean${NC} 清理本地镜像(谨慎使用) ${YELLOW}选项:${NC} --server 指定服务器(覆盖配置文件) --skip-build 跳过构建(用于已构建过的情况) - --skip-push 跳过推送(用于已推送过的情况) + --skip-push 跳过传输(用于已传输过的情况) --force 强制执行(不确认) --help, -h 显示此帮助 ${YELLOW}示例:${NC} - $0 build v1.0.0 # 构建并推送 + $0 build v1.0.0 # 构建并打包传输到服务器 $0 deploy v1.0.0 --server 192.168.1.100 # 部署到服务器 $0 rollback v0.9.0 # 回滚到 v0.9.0 $0 history # 查看部署历史 $0 all v1.0.0 --server 192.168.1.100 # 一键完成所有操作 ${YELLOW}前提准备:${NC} - 1. 阿里云容器镜像: https://cr.console.aliyun.com/ - 2. 修改脚本中的 REGISTRY_HOST 和 NAMESPACE - 3. 修改 SERVER_HOST 为你的服务器 IP + 1. 修改 SERVER_HOST 为你的服务器 IP + 2. 配置服务器 SSH 免密登录(建议) EOF } @@ -123,11 +118,6 @@ EOF check_config() { local errors=0 - if [ "$NAMESPACE" = "你的命名空间" ]; then - print_msg "$RED" "错误: 请修改 deploy.sh 中的 NAMESPACE 为你的阿里云仓库命名空间" - errors=$((errors + 1)) - fi - if [ -z "$SERVER_HOST" ]; then print_msg "$YELLOW" "警告: SERVER_HOST 未设置,远程部署功能将不可用" fi @@ -150,71 +140,66 @@ do_build() { print_msg "$GREEN" "✅ 镜像构建完成" } -# ==================== 2. 推送镜像 ==================== +# ==================== 2. 打包并传输镜像到服务器 ==================== do_push() { local version=$1 - print_step "🔑 登录镜像仓库" - - # 登录(可能需要输入密码) - docker login --username="${NAMESPACE}" "${REGISTRY_HOST}" || { - print_msg "$RED" "❌ 登录失败" + if [ -z "$SERVER_HOST" ]; then + print_msg "$RED" "错误: 请设置 SERVER_HOST" exit 1 - } + fi - print_msg "$GREEN" "✅ 登录成功" + print_step "📦 打包镜像为 tar 文件" - print_step "📦 推送镜像到仓库" + local tmp_dir="/tmp/topfans-images-${version}" + mkdir -p "${tmp_dir}" local failed=() - local pushed=() + local packed=() for SERVICE in "${SERVICES[@]}"; do local local_image="topfans/${SERVICE}:latest" - local remote_image="${REGISTRY_HOST}/${NAMESPACE}/topfans-${SERVICE}:v${version}" - local latest_image="${REGISTRY_HOST}/${NAMESPACE}/topfans-${SERVICE}:latest" + local tar_file="${tmp_dir}/${SERVICE}.tar" echo "" print_msg "$YELLOW" "处理 ${SERVICE}..." - # 打标签 - docker tag "${local_image}" "${remote_image}" - docker tag "${local_image}" "${latest_image}" - - # 推送版本标签 - echo -e " ${CYAN}→ ${remote_image}${NC}" - if docker push "${remote_image}"; then - echo -e " ${GREEN}✅ 已推送${NC}" - pushed+=("${SERVICE}") + if docker save "${local_image}" -o "${tar_file}"; then + echo -e " ${GREEN}✅ 已打包${NC}" + packed+=("${SERVICE}") else - echo -e " ${RED}❌ 推送失败${NC}" + echo -e " ${RED}❌ 打包失败${NC}" failed+=("${SERVICE}") fi done - # 推送 latest - echo "" - print_msg "$YELLOW" "推送 latest 标签..." - for SERVICE in "${SERVICES[@]}"; do - local latest_image="${REGISTRY_HOST}/${NAMESPACE}/topfans-${SERVICE}:latest" - docker push "${latest_image}" 2>/dev/null || true - done - - print_step "📊 推送结果" - - if [ ${#failed[@]} -eq 0 ]; then - print_msg "$GREEN" "✅ 全部推送成功" - else - print_msg "$RED" "❌ 失败: ${failed[*]}" + if [ ${#failed[@]} -ne 0 ]; then + print_msg "$RED" "❌ 打包失败: ${failed[*]}" + rm -rf "${tmp_dir}" + exit 1 fi - echo "" - print_msg "$CYAN" "已推送的镜像: v${version}" - for SERVICE in "${pushed[@]}"; do - echo -e " ${GREEN}${REGISTRY_HOST}/${NAMESPACE}/topfans-${SERVICE}:v${version}${NC}" + print_msg "$GREEN" "✅ 全部打包完成" + + print_step "📤 传输镜像到服务器" + + print_msg "$YELLOW" "正在传输到 ${SERVER_USER}@${SERVER_HOST}:${SERVER_PATH}..." + + # 创建服务器目录 + ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "mkdir -p ${SERVER_PATH}/images && rm -f ${SERVER_PATH}/images/*.tar 2>/dev/null || true" + + # 传输 tar 文件 + for SERVICE in "${packed[@]}"; do + local tar_file="${tmp_dir}/${SERVICE}.tar" + print_msg "$YELLOW" "传输 ${SERVICE}.tar..." + scp -P "${SERVER_PORT}" "${tar_file}" "${SERVER_USER}@${SERVER_HOST}:${SERVER_PATH}/images/" + print_msg "$GREEN" "✅ ${SERVICE}.tar 传输完成" done - return $([ ${#failed[@]} -eq 0 ] && echo 0 || echo 1) + # 清理本地临时文件 + rm -rf "${tmp_dir}" + + print_msg "$GREEN" "✅ 镜像传输完成" } # ==================== 3. 远程部署 ==================== @@ -229,54 +214,7 @@ do_deploy() { print_step "🚀 远程部署到 ${SERVER_HOST}" - # 构建远程部署脚本 - local remote_script=" - set -e - - echo '=== 1. 创建部署目录 ===' - mkdir -p ${SERVER_PATH} - cd ${SERVER_PATH} - - echo '=== 2. 登录镜像仓库 ===' - # 注意:需要提前在服务器上配置 docker login,或者使用阿里云 AccessKey 登录 - # 这里假设已配置免密登录或使用 docker-credential-ecr-login - echo '使用镜像: ${REGISTRY_HOST}/${NAMESPACE}' - - echo '=== 3. 拉取镜像 ===' - for service in ${SERVICES[*]}; do - echo \"拉取 topfans-\$service:v${version}...\" - docker pull ${REGISTRY_HOST}/${NAMESPACE}/topfans-\$service:v${version} - done - - echo '=== 4. 打 latest 标签 ===' - for service in ${SERVICES[*]}; do - docker tag ${REGISTRY_HOST}/${NAMESPACE}/topfans-\$service:v${version} \ - ${REGISTRY_HOST}/${NAMESPACE}/topfans-\$service:latest - done - - echo '=== 5. 停止现有服务 ===' - docker-compose -f docker-compose.prod.yml down 2>/dev/null || true - - echo '=== 6. 启动服务 ===' - docker-compose -f docker-compose.prod.yml --profile prod up -d - - echo '=== 7. 等待服务就绪 ===' - sleep 10 - - echo '=== 8. 健康检查 ===' - curl -s http://localhost:8080/health > /dev/null && echo '✅ Gateway 健康' || echo '⚠️ Gateway 可能未就绪' - - echo '=== 9. 记录部署历史 ===' - echo '{\"version\":\"${version}\",\"deployed_at\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"services\":${#SERVICES[@]}}' \ - >> ${SERVER_PATH}/deploy_history.json - - echo '' - echo '✅ 部署完成!' - docker-compose -f docker-compose.prod.yml ps - " - - # 执行远程脚本 - print_msg "$YELLOW" "正在连接 ${SERVER_USER}@${SERVER_HOST}..." + print_msg "$YELLOW" "检查 Docker 环境..." ssh -p "${SERVER_PORT}" -T "${SERVER_USER}@${SERVER_HOST}" << 'ENDSSH' set -e @@ -295,32 +233,26 @@ fi echo '✅ Docker 环境就绪' ENDSSH - # 由于 heredoc 在复杂脚本中有问题,这里简化为直接执行关键命令 - print_msg "$YELLOW" "执行部署命令..." - - # 分步执行远程命令 - ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" " - mkdir -p ${SERVER_PATH} && \ - echo '目录就绪' - " - + # 确保服务器目录存在 + ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "mkdir -p ${SERVER_PATH}/images" print_msg "$GREEN" "✅ 服务器目录就绪" - # 拉取镜像(逐个拉取以便查看进度) + # 从 tar 文件加载镜像 + print_step "📥 从 tar 文件加载镜像" for SERVICE in "${SERVICES[@]}"; do - print_msg "$YELLOW" "拉取 ${SERVICE}..." + print_msg "$YELLOW" "加载 ${SERVICE}..." ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" " - docker pull ${REGISTRY_HOST}/${NAMESPACE}/topfans-${SERVICE}:v${version} + docker load -i ${SERVER_PATH}/images/${SERVICE}.tar " - print_msg "$GREEN" "✅ ${SERVICE} 拉取完成" + print_msg "$GREEN" "✅ ${SERVICE} 加载完成" done - # 打标签 + # 打 latest 标签 print_msg "$YELLOW" "打 latest 标签..." ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" " for service in ${SERVICES[*]}; do - docker tag ${REGISTRY_HOST}/${NAMESPACE}/topfans-\$service:v${version} \ - ${REGISTRY_HOST}/${NAMESPACE}/topfans-\$service:latest + docker tag topfans/\${service}:latest topfans/\${service}:v${version} + docker tag topfans/\${service}:latest topfans/\${service}:latest done echo '标签完成' " @@ -376,13 +308,13 @@ do_rollback() { docker-compose -f docker-compose.prod.yml down " - # 拉取指定版本镜像并打标签 + # 从已有的 tar 文件加载镜像并打标签 for SERVICE in "${SERVICES[@]}"; do - print_msg "$YELLOW" "拉取 ${SERVICE}:v${version}..." + print_msg "$YELLOW" "加载 ${SERVICE}:v${version}..." ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" " - docker pull ${REGISTRY_HOST}/${NAMESPACE}/topfans-${SERVICE}:v${version} - docker tag ${REGISTRY_HOST}/${NAMESPACE}/topfans-${SERVICE}:v${version} \ - ${REGISTRY_HOST}/${NAMESPACE}/topfans-${SERVICE}:latest + docker load -i ${SERVER_PATH}/images/${SERVICE}.tar + docker tag topfans/${SERVICE}:latest topfans/${SERVICE}:v${version} + docker tag topfans/${SERVICE}:latest topfans/${SERVICE}:latest " print_msg "$GREEN" "✅ ${SERVICE} 回滚完成" done @@ -512,7 +444,7 @@ main() { fi echo -e "${CYAN}版本: v${version}${NC}" - echo -e "${CYAN}仓库: ${REGISTRY_HOST}/${NAMESPACE}${NC}" + echo -e "${CYAN}目标: ${SERVER_USER}@${SERVER_HOST}${NC}" if [ "$skip_build" = false ]; then do_build @@ -573,7 +505,7 @@ main() { echo -e "${MAGENTA}一键部署流程:${NC}" echo " 1. 构建镜像" - echo " 2. 推送到仓库" + echo " 2. 打包传输到服务器" echo " 3. 部署到 ${SERVER_HOST}" echo "" diff --git a/frontend/pages/profile/profile.vue b/frontend/pages/profile/profile.vue index de9fdd3..64720f2 100644 --- a/frontend/pages/profile/profile.vue +++ b/frontend/pages/profile/profile.vue @@ -318,13 +318,9 @@ const avatarKey = ref(0); // 用于强制刷新Avatar组件 // 手机号 const mobile = ref(''); -// 显示脱敏后的手机号 +// 显示脱敏后的手机号(后端已脱敏,直接显示) const displayMobile = computed(() => { - const phone = mobile.value; - if (!phone) return ''; - if (phone.length !== 11) return phone; - // 显示格式:138****8888 - return phone.substring(0, 3) + '****' + phone.substring(7); + return mobile.value || ''; }); // 显示截断后的区块链地址 diff --git a/frontend/store/modules/user.js b/frontend/store/modules/user.js index 963dfd9..e2ee8bc 100644 --- a/frontend/store/modules/user.js +++ b/frontend/store/modules/user.js @@ -76,11 +76,13 @@ const actions = { uni.setStorageSync('access_token', accessToken) commit('SET_TOKEN', accessToken) - // 缓存登录手机号 - uni.setStorageSync('login_mobile', mobile) - // 缓存用户信息 const user = res.data.user + + // 缓存登录手机号(优先使用后端返回的脱敏手机号) + const loginMobile = user.mobile_masked || mobile + uni.setStorageSync('login_mobile', loginMobile) + uni.setStorageSync('user', JSON.stringify(user)) commit('SET_USER_INFO', user) @@ -133,11 +135,13 @@ const actions = { uni.setStorageSync('access_token', accessToken) commit('SET_TOKEN', accessToken) - // 缓存登录手机号 - uni.setStorageSync('login_mobile', mobile) - // 缓存用户信息 const user = res.data.user + + // 缓存登录手机号(优先使用后端返回的脱敏手机号) + const loginMobile = user.mobile_masked || mobile + uni.setStorageSync('login_mobile', loginMobile) + uni.setStorageSync('user', JSON.stringify(user)) commit('SET_USER_INFO', user)