更新小程序隐私保护机制,新增手机号一键登录功能,用户需同意隐私协议后方可获取手机号。优化多个页面的登录交互,提升用户体验。调整相关配置以支持新功能。

This commit is contained in:
Alex-larget
2026-03-20 13:40:13 +08:00
parent 0bc32deb94
commit 385e47bc55
60 changed files with 2954 additions and 1669 deletions

View File

@@ -240,6 +240,88 @@ func MiniprogramDevLoginAs(c *gin.Context) {
})
}
// MiniprogramDevLoginByPhone POST /api/miniprogram/dev/login-by-phone 开发专用:按手机号登录(仅 APP_ENV=development 可用,密码可空)
func MiniprogramDevLoginByPhone(c *gin.Context) {
if strings.ToLower(strings.TrimSpace(os.Getenv("APP_ENV"))) != "development" {
c.JSON(http.StatusForbidden, gin.H{"success": false, "error": "仅开发环境可用"})
return
}
var req struct {
Phone string `json:"phone" binding:"required"`
Password string `json:"password"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "缺少手机号"})
return
}
phone := strings.TrimSpace(strings.ReplaceAll(req.Phone, " ", ""))
if phone == "" {
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "手机号不能为空"})
return
}
db := database.DB()
var user model.User
// 支持纯数字或带 +86 前缀
if err := db.Where("phone = ? OR phone = ? OR phone = ?", phone, "+86"+phone, "+86 "+phone).First(&user).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"success": false, "error": "该手机号未注册"})
return
}
openID := getStringValue(user.OpenID)
if openID == "" {
openID = user.ID
}
tokenSuffix := openID
if len(openID) >= 8 {
tokenSuffix = openID[len(openID)-8:]
}
token := fmt.Sprintf("tk_%s_%d", tokenSuffix, time.Now().Unix())
var purchasedSections []string
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)
for _, row := range orderRows {
if row.ProductID != "" {
purchasedSections = append(purchasedSections, row.ProductID)
}
}
if purchasedSections == nil {
purchasedSections = []string{}
}
responseUser := map[string]interface{}{
"id": user.ID,
"openId": openID,
"nickname": getStringValue(user.Nickname),
"avatar": resolveAvatarURL(getStringValue(user.Avatar)),
"phone": getStringValue(user.Phone),
"wechatId": getStringValue(user.WechatID),
"referralCode": getStringValue(user.ReferralCode),
"hasFullBook": getBoolValue(user.HasFullBook),
"purchasedSections": purchasedSections,
"earnings": getFloatValue(user.Earnings),
"pendingEarnings": getFloatValue(user.PendingEarnings),
"referralCount": getIntValue(user.ReferralCount),
"createdAt": user.CreatedAt,
}
if user.IsVip != nil {
responseUser["isVip"] = *user.IsVip
}
if user.VipExpireDate != nil {
responseUser["vipExpireDate"] = user.VipExpireDate.Format("2006-01-02")
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": map[string]interface{}{
"openId": openID,
"user": responseUser,
"token": token,
},
})
}
// 辅助函数
func getStringValue(ptr *string) string {
if ptr == nil {