package handler import ( "fmt" "net/http" "strings" "time" "soul-api/internal/database" "soul-api/internal/model" "soul-api/internal/wechat" "github.com/gin-gonic/gin" ) // WechatLogin POST /api/wechat/login func WechatLogin(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"success": true}) } // WechatPhoneLoginReq 手机号登录请求:code 为 wx.login() 的 code,phoneCode 为 getPhoneNumber 返回的 code type WechatPhoneLoginReq struct { Code string `json:"code"` // wx.login() 得到,用于 code2session 拿 openId PhoneCode string `json:"phoneCode"` // getPhoneNumber 得到,用于换手机号 } // WechatPhoneLogin POST /api/wechat/phone-login // 请求体:code(必填)+ phoneCode(必填)。先 code2session 得到 openId,再 getPhoneNumber 得到手机号,创建/更新用户并返回与 /api/miniprogram/login 一致的数据结构。 func WechatPhoneLogin(c *gin.Context) { var req WechatPhoneLoginReq if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "缺少 code 或 phoneCode"}) return } if req.Code == "" || req.PhoneCode == "" { c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "请提供 code 与 phoneCode"}) return } openID, sessionKey, _, err := wechat.Code2Session(req.Code) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": fmt.Sprintf("微信登录失败: %v", err)}) return } phoneNumber, countryCode, err := wechat.GetPhoneNumber(req.PhoneCode) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": fmt.Sprintf("获取手机号失败: %v", err)}) return } db := database.DB() var user model.User result := db.Where("open_id = ?", openID).First(&user) isNewUser := result.Error != nil if isNewUser { referralCode := "SOUL" + strings.ToUpper(openID[len(openID)-6:]) nickname := "微信用户" + openID[len(openID)-4:] avatar := "" hasFullBook := false earnings := 0.0 pendingEarnings := 0.0 referralCount := 0 purchasedSections := "[]" phone := phoneNumber if countryCode != "" && countryCode != "86" { phone = "+" + countryCode + " " + phoneNumber } user = model.User{ ID: openID, OpenID: &openID, SessionKey: &sessionKey, Nickname: &nickname, Avatar: &avatar, Phone: &phone, ReferralCode: &referralCode, HasFullBook: &hasFullBook, PurchasedSections: &purchasedSections, Earnings: &earnings, PendingEarnings: &pendingEarnings, ReferralCount: &referralCount, } if err := db.Create(&user).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "创建用户失败"}) return } } else { phone := phoneNumber if countryCode != "" && countryCode != "86" { phone = "+" + countryCode + " " + phoneNumber } db.Model(&user).Updates(map[string]interface{}{"session_key": sessionKey, "phone": phone}) user.Phone = &phone } var orderRows []struct { ProductID string `gorm:"column:product_id"` } db.Raw(` SELECT DISTINCT product_id FROM orders WHERE user_id = ? AND status = 'paid' AND product_type = 'section' `, user.ID).Scan(&orderRows) purchasedSections := []string{} for _, row := range orderRows { if row.ProductID != "" { purchasedSections = append(purchasedSections, row.ProductID) } } responseUser := map[string]interface{}{ "id": user.ID, "openId": strVal(user.OpenID), "nickname": strVal(user.Nickname), "avatar": strVal(user.Avatar), "phone": strVal(user.Phone), "wechatId": strVal(user.WechatID), "referralCode": strVal(user.ReferralCode), "hasFullBook": boolVal(user.HasFullBook), "purchasedSections": purchasedSections, "earnings": floatVal(user.Earnings), "pendingEarnings": floatVal(user.PendingEarnings), "referralCount": intVal(user.ReferralCount), "createdAt": user.CreatedAt, } token := fmt.Sprintf("tk_%s_%d", openID[len(openID)-8:], time.Now().Unix()) c.JSON(http.StatusOK, gin.H{ "success": true, "data": map[string]interface{}{ "openId": openID, "user": responseUser, "token": token, }, "isNewUser": isNewUser, }) } func strVal(p *string) string { if p == nil { return "" } return *p } func boolVal(p *bool) bool { if p == nil { return false } return *p } func floatVal(p *float64) float64 { if p == nil { return 0 } return *p } func intVal(p *int) int { if p == nil { return 0 } return *p }