Files
soul-yongping/soul-api/internal/handler/wechat.go

161 lines
4.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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() 的 codephoneCode 为 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
}