190 lines
4.8 KiB
Go
190 lines
4.8 KiB
Go
|
|
package handler
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"net/http"
|
|||
|
|
"time"
|
|||
|
|
|
|||
|
|
"soul-api/internal/database"
|
|||
|
|
"soul-api/internal/model"
|
|||
|
|
|
|||
|
|
"github.com/gin-gonic/gin"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// VipStatus GET /api/miniprogram/vip/status 小程序-查询用户 VIP 状态
|
|||
|
|
func VipStatus(c *gin.Context) {
|
|||
|
|
userID := c.Query("userId")
|
|||
|
|
if userID == "" {
|
|||
|
|
c.JSON(http.StatusOK, gin.H{"success": false, "error": "缺少 userId"})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
db := database.DB()
|
|||
|
|
|
|||
|
|
// 查是否有 fullbook 或 vip 的已支付订单
|
|||
|
|
var order model.Order
|
|||
|
|
err := db.Where("user_id = ? AND status = ? AND (product_type = ? OR product_type = ?)",
|
|||
|
|
userID, "paid", "fullbook", "vip").
|
|||
|
|
Order("pay_time DESC").First(&order).Error
|
|||
|
|
if err != nil {
|
|||
|
|
c.JSON(http.StatusOK, gin.H{
|
|||
|
|
"success": true,
|
|||
|
|
"data": gin.H{
|
|||
|
|
"isVip": false,
|
|||
|
|
"daysRemaining": 0,
|
|||
|
|
"expireDate": "",
|
|||
|
|
"price": float64(1980),
|
|||
|
|
},
|
|||
|
|
})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
expireDate := time.Now().AddDate(0, 0, 365)
|
|||
|
|
daysRemaining := 365
|
|||
|
|
if order.PayTime != nil {
|
|||
|
|
expireDate = order.PayTime.AddDate(0, 0, 365)
|
|||
|
|
if expireDate.After(time.Now()) {
|
|||
|
|
daysRemaining = int(expireDate.Sub(time.Now()).Hours() / 24)
|
|||
|
|
} else {
|
|||
|
|
daysRemaining = 0
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
expStr := expireDate.Format("2006-01-02")
|
|||
|
|
|
|||
|
|
c.JSON(http.StatusOK, gin.H{
|
|||
|
|
"success": true,
|
|||
|
|
"data": gin.H{
|
|||
|
|
"isVip": true,
|
|||
|
|
"daysRemaining": daysRemaining,
|
|||
|
|
"expireDate": expStr,
|
|||
|
|
"price": float64(1980),
|
|||
|
|
},
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// VipProfileGet GET /api/miniprogram/vip/profile 小程序-获取 VIP 资料
|
|||
|
|
func VipProfileGet(c *gin.Context) {
|
|||
|
|
userID := c.Query("userId")
|
|||
|
|
if userID == "" {
|
|||
|
|
c.JSON(http.StatusOK, gin.H{"success": false, "error": "缺少 userId"})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
db := database.DB()
|
|||
|
|
var user model.User
|
|||
|
|
if err := db.Where("id = ?", userID).First(&user).Error; err != nil {
|
|||
|
|
c.JSON(http.StatusOK, gin.H{"success": true, "data": gin.H{"name": "", "project": "", "contact": "", "bio": ""}})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
name := ""
|
|||
|
|
if user.Nickname != nil {
|
|||
|
|
name = *user.Nickname
|
|||
|
|
}
|
|||
|
|
contact := ""
|
|||
|
|
if user.Phone != nil {
|
|||
|
|
contact = *user.Phone
|
|||
|
|
}
|
|||
|
|
if user.WechatID != nil && contact == "" {
|
|||
|
|
contact = *user.WechatID
|
|||
|
|
}
|
|||
|
|
c.JSON(http.StatusOK, gin.H{
|
|||
|
|
"success": true,
|
|||
|
|
"data": gin.H{
|
|||
|
|
"name": name,
|
|||
|
|
"project": "",
|
|||
|
|
"contact": contact,
|
|||
|
|
"bio": "",
|
|||
|
|
},
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// VipProfilePost POST /api/miniprogram/vip/profile 小程序-更新 VIP 资料
|
|||
|
|
func VipProfilePost(c *gin.Context) {
|
|||
|
|
var req struct {
|
|||
|
|
UserID string `json:"userId" binding:"required"`
|
|||
|
|
Name string `json:"name"`
|
|||
|
|
Project string `json:"project"`
|
|||
|
|
Contact string `json:"contact"`
|
|||
|
|
Bio string `json:"bio"`
|
|||
|
|
}
|
|||
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|||
|
|
c.JSON(http.StatusOK, gin.H{"success": false, "error": "请求体无效"})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
db := database.DB()
|
|||
|
|
updates := map[string]interface{}{}
|
|||
|
|
if req.Name != "" {
|
|||
|
|
updates["nickname"] = req.Name
|
|||
|
|
}
|
|||
|
|
if req.Contact != "" {
|
|||
|
|
updates["phone"] = req.Contact
|
|||
|
|
}
|
|||
|
|
if len(updates) > 0 {
|
|||
|
|
db.Model(&model.User{}).Where("id = ?", req.UserID).Updates(updates)
|
|||
|
|
}
|
|||
|
|
c.JSON(http.StatusOK, gin.H{"success": true})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// VipMembers GET /api/miniprogram/vip/members 小程序-VIP 会员列表(无 id 返回列表;有 id 返回单个)
|
|||
|
|
func VipMembers(c *gin.Context) {
|
|||
|
|
id := c.Query("id")
|
|||
|
|
db := database.DB()
|
|||
|
|
|
|||
|
|
// 有 id 时查单个:优先从已购 fullbook/vip 的用户中找
|
|||
|
|
if id != "" {
|
|||
|
|
var user model.User
|
|||
|
|
if err := db.Where("id = ?", id).First(&user).Error; err != nil {
|
|||
|
|
c.JSON(http.StatusOK, gin.H{"success": true, "data": nil})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
// 检查是否 VIP(有 fullbook 或 vip 订单)
|
|||
|
|
var cnt int64
|
|||
|
|
db.Model(&model.Order{}).Where("user_id = ? AND status = ? AND (product_type = ? OR product_type = ?)",
|
|||
|
|
id, "paid", "fullbook", "vip").Count(&cnt)
|
|||
|
|
item := formatVipMember(&user, cnt > 0)
|
|||
|
|
c.JSON(http.StatusOK, gin.H{"success": true, "data": item})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 无 id:返回 VIP 会员列表(有 fullbook 或 vip 订单的用户)
|
|||
|
|
var userIDs []string
|
|||
|
|
db.Model(&model.Order{}).Select("DISTINCT user_id").
|
|||
|
|
Where("status = ? AND (product_type = ? OR product_type = ?)", "paid", "fullbook", "vip").
|
|||
|
|
Pluck("user_id", &userIDs)
|
|||
|
|
if len(userIDs) == 0 {
|
|||
|
|
c.JSON(http.StatusOK, gin.H{"success": true, "data": []interface{}{}})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
var users []model.User
|
|||
|
|
db.Where("id IN ?", userIDs).Find(&users)
|
|||
|
|
list := make([]gin.H, 0, len(users))
|
|||
|
|
for i := range users {
|
|||
|
|
list = append(list, formatVipMember(&users[i], true))
|
|||
|
|
}
|
|||
|
|
c.JSON(http.StatusOK, gin.H{"success": true, "data": list})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func formatVipMember(u *model.User, isVip bool) gin.H {
|
|||
|
|
name := ""
|
|||
|
|
if u.Nickname != nil {
|
|||
|
|
name = *u.Nickname
|
|||
|
|
}
|
|||
|
|
avatar := ""
|
|||
|
|
if u.Avatar != nil {
|
|||
|
|
avatar = *u.Avatar
|
|||
|
|
}
|
|||
|
|
contact := ""
|
|||
|
|
if u.Phone != nil {
|
|||
|
|
contact = *u.Phone
|
|||
|
|
}
|
|||
|
|
if u.WechatID != nil && contact == "" {
|
|||
|
|
contact = *u.WechatID
|
|||
|
|
}
|
|||
|
|
return gin.H{
|
|||
|
|
"id": u.ID,
|
|||
|
|
"nickname": name,
|
|||
|
|
"avatar": avatar,
|
|||
|
|
"vip_name": name,
|
|||
|
|
"vip_avatar": avatar,
|
|||
|
|
"vip_contact": contact,
|
|||
|
|
"is_vip": isVip,
|
|||
|
|
}
|
|||
|
|
}
|