package handlers import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "os" "strconv" "strings" "time" "yh_web/server/config" "yh_web/server/models" "github.com/gin-gonic/gin" "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/mongo/options" ) const yuhengCloudRegisterColl = "yuheng_cloud_register_records" func cloudRegisterURL() string { u := strings.TrimSpace(os.Getenv("YH_CLOUD_REGISTER_URL")) if u != "" { return strings.TrimSuffix(u, "/") } return "http://www.cloud.yuxindazhineng.com:3001/register" } // YuhengCloudRegisterInput 与云端 POST /register 一致;email 仅用于调用云端,不写入 Mongo type YuhengCloudRegisterInput struct { Username string `json:"username" binding:"required"` Password string `json:"password" binding:"required"` Email string `json:"email" binding:"required"` } type cloudRegisterPayload struct { Username string `json:"username"` Password string `json:"password"` Email string `json:"email"` } func postCloudRegister(ctx context.Context, payload cloudRegisterPayload) (int, string, error) { body, err := json.Marshal(payload) if err != nil { return 0, "", err } req, err := http.NewRequestWithContext(ctx, http.MethodPost, cloudRegisterURL(), bytes.NewReader(body)) if err != nil { return 0, "", err } req.Header.Set("Content-Type", "application/json") client := &http.Client{Timeout: 45 * time.Second} resp, err := client.Do(req) if err != nil { return 0, "", err } defer resp.Body.Close() b, _ := io.ReadAll(io.LimitReader(resp.Body, 4096)) return resp.StatusCode, strings.TrimSpace(string(b)), nil } // CreateYuhengCloudRegister 调用云端注册接口,成功后在 Mongo 写入一条记录(仅 username、password) func CreateYuhengCloudRegister(c *gin.Context) { var in YuhengCloudRegisterInput if err := c.ShouldBindJSON(&in); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "请填写用户名、密码与邮箱(邮箱仅提交云端)"}) return } in.Username = strings.TrimSpace(in.Username) in.Password = strings.TrimSpace(in.Password) in.Email = strings.TrimSpace(in.Email) if in.Username == "" || in.Password == "" || in.Email == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "用户名、密码、邮箱不能为空"}) return } ctx, cancel := context.WithTimeout(c.Request.Context(), 50*time.Second) defer cancel() status, bodySnippet, err := postCloudRegister(ctx, cloudRegisterPayload{ Username: in.Username, Password: in.Password, Email: in.Email, }) if err != nil { c.JSON(http.StatusBadGateway, gin.H{"error": "调用云端注册失败: " + err.Error()}) return } if status < 200 || status >= 300 { msg := bodySnippet if len(msg) > 500 { msg = msg[:500] + "…" } if msg == "" { msg = http.StatusText(status) } c.JSON(http.StatusBadGateway, gin.H{"error": fmt.Sprintf("云端返回 %d: %s", status, msg)}) return } db := config.GetDB(config.DBName) if db == nil { c.JSON(http.StatusServiceUnavailable, gin.H{"error": "数据库未连接,未写入本地记录"}) return } insCtx, insCancel := context.WithTimeout(context.Background(), 8*time.Second) defer insCancel() doc := bson.M{ "username": in.Username, "password": in.Password, "created_at": time.Now().UTC().Format(time.RFC3339), } res, err := db.Collection(yuhengCloudRegisterColl).InsertOne(insCtx, doc) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "云端已成功但本地记录失败: " + err.Error()}) return } idHex := "" switch v := res.InsertedID.(type) { case bson.ObjectID: idHex = v.Hex() } c.JSON(http.StatusOK, gin.H{"id": idHex, "message": "已提交云端注册并写入本地记录"}) } // ListYuhengCloudRegisterRecords 分页列出本地留痕(便于管理页展示) func ListYuhengCloudRegisterRecords(c *gin.Context) { db := config.GetDB(config.DBName) if db == nil { c.JSON(http.StatusOK, gin.H{"list": []any{}, "total": 0}) return } page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) if page < 1 { page = 1 } if pageSize < 1 || pageSize > 100 { pageSize = 20 } skip := int64((page - 1) * pageSize) limit := int64(pageSize) ctx, cancel := context.WithTimeout(c.Request.Context(), 15*time.Second) defer cancel() coll := db.Collection(yuhengCloudRegisterColl) total, err := coll.CountDocuments(ctx, bson.M{}) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } opts := options.Find().SetSort(bson.D{{Key: "created_at", Value: -1}}).SetSkip(skip).SetLimit(limit) cur, err := coll.Find(ctx, bson.M{}, opts) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } defer cur.Close(ctx) var list []models.YuhengCloudRegisterRecord for cur.Next(ctx) { var row struct { ID bson.ObjectID `bson:"_id"` Username string `bson:"username"` Password string `bson:"password"` CreatedAt string `bson:"created_at"` } if err := cur.Decode(&row); err != nil { continue } list = append(list, models.YuhengCloudRegisterRecord{ ID: row.ID.Hex(), Username: row.Username, Password: row.Password, CreatedAt: row.CreatedAt, }) } c.JSON(http.StatusOK, gin.H{"list": list, "total": total}) }