init code

This commit is contained in:
fish
2025-10-03 16:39:24 +08:00
parent d3008ab9f5
commit 1e9cdda192
61 changed files with 3196 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
sudo docker rm -f user_update_password_api && sudo docker run -itd --name user_update_password_api -v ./:/app -p 20004:80 golang:1.25.0-alpine3.22
----
# 格式docker build -t 镜像名:标签 .
sudo docker rmi -f user-update-password-api:1.0.0
sudo docker build -t user-update-password-api:1.0.0 .

View File

@@ -0,0 +1,69 @@
import subprocess
import os
def run_command(command, check=True, shell=True):
"""执行shell命令并返回结果包含错误处理"""
try:
print(f"执行命令: {command}")
result = subprocess.run(
command,
shell=shell,
check=check,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
print(f"命令输出: {result.stdout}")
return result
except subprocess.CalledProcessError as e:
print(f"命令执行失败: {e.stderr}")
raise
def main():
container_name = "user_update_password_api"
script_dir = os.path.dirname(os.path.abspath(__file__)) # 获取当前脚本所在目录
try:
# 1. 删除已存在的容器
print("===== 步骤1: 删除已存在的容器 =====")
run_command(f"sudo docker rm -f {container_name}", check=False)
# 2. 启动新容器
print("\n===== 步骤2: 启动新容器 =====")
run_command(
f"sudo docker run -itd --name {container_name} "
f"-v {script_dir}:/app -p 20004:80 golang:1.25.0-alpine3.22"
)
# 3-5. 进入容器、进入app目录并执行go mod tidy使用sh而非bash
print("\n===== 步骤3-5: 容器内操作 =====")
exec_commands = (
"cd /app && " # 进入app目录
"echo '当前目录内容:' && ls -la && " # 新增目录检查
"go version && " # 检查Go版本
"go mod init user_update_password && " # 执行依赖整理
"go mod tidy && " # 执行依赖整理
"exit" # 退出容器
)
# 使用sh代替bash执行命令
run_command(
f"sudo docker exec -it {container_name} sh -c '{exec_commands}'"
)
# 7. 删除容器
print("\n===== 步骤7: 删除容器 =====")
run_command(f"sudo docker rm -f {container_name}")
# 8. 删除虚悬镜像
print("\n===== 步骤8: 清理虚悬镜像 =====")
run_command("sudo docker images -f 'dangling=true' -q | xargs -r sudo docker rmi")
print("\n===== 所有操作完成 =====")
except Exception as e:
print(f"\n操作失败: {str(e)}")
exit(1)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,16 @@
services:
user_update_password:
image: user-update-password-api:1.0.0
container_name: api_user_update_password
restart: always
depends_on:
- postgres # 依赖数据库服务
networks:
- user-network
environment:
DB_HOST: postgres
DB_PORT: ${DB_PORT} # 引用.env变量
DB_USER: ${DB_USER} # 引用.env变量
DB_PASSWORD: ${DB_PASSWORD} # 引用.env变量
DB_NAME: ${DB_NAME}
TZ: ${TZ} # 引用.env变量

View File

@@ -0,0 +1,38 @@
# ==================== 第一阶段构建Go程序构建阶段====================
# 使用官方Go镜像作为构建基础选择与项目匹配的Go版本示例用1.25.0,可根据实际调整)
FROM golang:1.25.0-alpine3.22 AS builder
# 设置工作目录(容器内的目录,规范文件位置)
WORKDIR /app
# 复制go.mod和go.sum先复制依赖文件利用Docker缓存机制避免每次代码变动都重新下载依赖
COPY go.mod go.sum ./
# 下载项目依赖仅当go.mod/go.sum变动时才会重新执行
RUN go mod download
# 复制整个项目代码到工作目录
COPY . .
# 构建Go程序
# - CGO_ENABLED=0禁用CGO生成静态链接的二进制文件避免依赖系统库保证镜像兼容性
# - -o app指定输出二进制文件名为app
# - ./main.go指定入口文件
RUN CGO_ENABLED=0 GOOS=linux go build -o app ./main.go
# ==================== 第二阶段:运行程序(运行阶段)====================
# 使用轻量级的alpine镜像仅5MB左右大幅减小最终镜像体积
FROM alpine:3.19
# 设置工作目录
WORKDIR /app
# 从构建阶段复制编译好的二进制文件到当前镜像(仅复制最终产物,减小体积)
COPY --from=builder /app/app ./
# 暴露程序运行端口(与代码中一致)
EXPOSE 8080
# 容器启动时执行的命令:运行二进制文件
CMD ["./app"]

View File

@@ -0,0 +1,43 @@
module user_update_password
go 1.25.0
require (
github.com/gin-contrib/cors v1.7.6
github.com/gin-gonic/gin v1.11.0
github.com/lib/pq v1.10.9
golang.org/x/crypto v0.42.0
)
require (
github.com/bytedance/sonic v1.14.0 // indirect
github.com/bytedance/sonic/loader v0.3.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.27.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.54.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect
go.uber.org/mock v0.5.0 // indirect
golang.org/x/arch v0.20.0 // indirect
golang.org/x/mod v0.27.0 // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.36.0 // indirect
golang.org/x/text v0.29.0 // indirect
golang.org/x/tools v0.36.0 // indirect
google.golang.org/protobuf v1.36.9 // indirect
)

206
api_update_password/main.go Normal file
View File

@@ -0,0 +1,206 @@
package main
import (
"database/sql"
"fmt"
"log"
"net/http"
"os"
"github.com/gin-gonic/gin"
_ "github.com/lib/pq"
"golang.org/x/crypto/bcrypt"
)
// UpdatePasswordRequest 更新密码请求参数结构
type UpdatePasswordRequest struct {
UserID string `json:"user_id" binding:"required"`
OldPassword string `json:"old_password" binding:"required"`
NewPassword string `json:"new_password" binding:"required,min=6"`
}
// UpdatePasswordResponse 更新密码响应结构
type UpdatePasswordResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
}
var db *sql.DB
func main() {
// 初始化日志输出格式
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
log.Println("开始初始化应用程序")
// 初始化Gin引擎
r := gin.Default()
log.Println("Gin引擎初始化完成")
// 从环境变量获取数据库配置
dbHost := os.Getenv("DB_HOST")
dbPort := os.Getenv("DB_PORT")
dbUser := os.Getenv("DB_USER")
dbPassword := os.Getenv("DB_PASSWORD")
dbName := os.Getenv("DB_NAME")
log.Printf("读取数据库配置: host=%s, port=%s, user=%s, dbname=%s", dbHost, dbPort, dbUser, dbName)
// 构建数据库连接字符串
connStr := fmt.Sprintf(
"host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
dbHost, dbPort, dbUser, dbPassword, dbName,
)
var err error
db, err = sql.Open("postgres", connStr)
if err != nil {
log.Panicf("无法连接数据库: %v", err)
}
defer db.Close()
log.Println("数据库连接对象创建成功")
// 验证数据库连接
if err := db.Ping(); err != nil {
log.Panicf("数据库连接失败: %v", err)
}
log.Println("数据库连接验证成功")
// 更新密码接口
r.POST("/user/update/password", updatePasswordHandler)
log.Println("注册更新密码接口: POST /user/update/password")
// 启动服务监听80端口
log.Println("服务启动在80端口")
if err := r.Run(":80"); err != nil {
log.Panicf("服务启动失败: %v", err)
}
}
// updatePasswordHandler 处理密码更新逻辑
func updatePasswordHandler(c *gin.Context) {
requestID := c.Request.Header.Get("X-Request-ID")
if requestID == "" {
requestID = fmt.Sprintf("req-%d", gin.Mode())
}
log.Printf("[%s] 收到更新密码请求 from %s", requestID, c.ClientIP())
var req UpdatePasswordRequest
// 绑定并验证请求参数
if err := c.ShouldBindJSON(&req); err != nil {
log.Printf("[%s] 请求参数绑定失败: %v", requestID, err)
c.JSON(http.StatusBadRequest, UpdatePasswordResponse{
Success: false,
Message: "请求参数错误: " + err.Error(),
})
return
}
log.Printf("[%s] 请求参数验证通过, 用户ID: %s", requestID, req.UserID)
// 1. 检查新旧密码是否一致
if req.OldPassword == req.NewPassword {
log.Printf("[%s] 新密码与旧密码相同, 用户ID: %s", requestID, req.UserID)
c.JSON(http.StatusOK, UpdatePasswordResponse{
Success: false,
Message: "新密码不能与旧密码相同",
})
return
}
// 2. 对旧密码进行加密处理
log.Printf("[%s] 开始对旧密码进行加密, 用户ID: %s", requestID, req.UserID)
hashedOldPassword, err := bcrypt.GenerateFromPassword([]byte(req.OldPassword), bcrypt.DefaultCost)
if err != nil {
log.Printf("[%s] 旧密码加密失败: %v, 用户ID: %s", requestID, err, req.UserID)
c.JSON(http.StatusInternalServerError, UpdatePasswordResponse{
Success: false,
Message: "旧密码加密失败: " + err.Error(),
})
return
}
log.Printf("[%s] 旧密码加密完成, 用户ID: %s", requestID, req.UserID)
// 3. 使用加密后的旧密码查询并验证按user_id匹配
var count int
query := `
SELECT COUNT(*) FROM user_password
WHERE user_id = $1 AND password = $2
`
log.Printf("[%s] 执行旧密码验证查询, SQL: %s, 参数: [%s, ****]", requestID, query, req.UserID)
err = db.QueryRow(query, req.UserID, string(hashedOldPassword)).Scan(&count)
if err != nil {
log.Printf("[%s] 旧密码验证查询失败: %v, 用户ID: %s", requestID, err, req.UserID)
c.JSON(http.StatusInternalServerError, UpdatePasswordResponse{
Success: false,
Message: "验证旧密码失败: " + err.Error(),
})
return
}
// 验证失败处理
if count == 0 {
log.Printf("[%s] 旧密码验证失败, 用户ID: %s", requestID, req.UserID)
c.JSON(http.StatusOK, UpdatePasswordResponse{
Success: false,
Message: "旧密码不正确",
})
return
}
log.Printf("[%s] 旧密码验证成功, 用户ID: %s", requestID, req.UserID)
// 4. 对新密码进行加密处理
log.Printf("[%s] 开始对新密码进行加密, 用户ID: %s", requestID, req.UserID)
hashedNewPassword, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost)
if err != nil {
log.Printf("[%s] 新密码加密失败: %v, 用户ID: %s", requestID, err, req.UserID)
c.JSON(http.StatusInternalServerError, UpdatePasswordResponse{
Success: false,
Message: "新密码加密失败: " + err.Error(),
})
return
}
log.Printf("[%s] 新密码加密完成, 用户ID: %s", requestID, req.UserID)
// 5. 更新密码表中的密码按user_id匹配
updateQuery := `
UPDATE user_password
SET password = $1
WHERE user_id = $2
`
log.Printf("[%s] 执行密码更新, SQL: %s, 参数: [****, %s]", requestID, updateQuery, req.UserID)
result, err := db.Exec(updateQuery, string(hashedNewPassword), req.UserID)
if err != nil {
log.Printf("[%s] 密码更新执行失败: %v, 用户ID: %s", requestID, err, req.UserID)
c.JSON(http.StatusInternalServerError, UpdatePasswordResponse{
Success: false,
Message: "更新密码失败: " + err.Error(),
})
return
}
// 6. 检查更新结果
rowsAffected, err := result.RowsAffected()
if err != nil {
log.Printf("[%s] 获取更新行数失败: %v, 用户ID: %s", requestID, err, req.UserID)
c.JSON(http.StatusInternalServerError, UpdatePasswordResponse{
Success: false,
Message: "检查更新结果失败: " + err.Error(),
})
return
}
log.Printf("[%s] 密码更新影响行数: %d, 用户ID: %s", requestID, rowsAffected, req.UserID)
if rowsAffected == 0 {
log.Printf("[%s] 未找到用户或密码未变化, 用户ID: %s", requestID, req.UserID)
c.JSON(http.StatusOK, UpdatePasswordResponse{
Success: false,
Message: "未找到用户或密码未发生变化",
})
return
}
// 7. 更新成功
log.Printf("[%s] 密码更新成功, 用户ID: %s", requestID, req.UserID)
c.JSON(http.StatusOK, UpdatePasswordResponse{
Success: true,
Message: "密码更新成功",
})
}

22
api_update_password/release.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
set -e # 当任何命令失败时立即退出脚本
# 定义镜像名称和标签
IMAGE_NAME="user-update-password-api"
IMAGE_TAG="1.0.0"
FULL_IMAGE="${IMAGE_NAME}:${IMAGE_TAG}"
echo "开始删除现有镜像 ${FULL_IMAGE}..."
if sudo docker rmi -f "${FULL_IMAGE}" >/dev/null 2>&1; then
echo "镜像 ${FULL_IMAGE} 删除成功"
else
echo "镜像 ${FULL_IMAGE} 不存在,跳过删除步骤"
fi
echo "开始构建新镜像 ${FULL_IMAGE}..."
if sudo docker build -t "${FULL_IMAGE}" .; then
echo "镜像 ${FULL_IMAGE} 构建成功!"
else
echo "错误:镜像构建失败" >&2
exit 1
fi