package handler import ( "encoding/json" "fmt" "net/http" "time" "soul-api/internal/database" "soul-api/internal/model" "github.com/gin-gonic/gin" ) // DBConfigGet GET /api/db/config func DBConfigGet(c *gin.Context) { key := c.Query("key") db := database.DB() var list []model.SystemConfig q := db.Table("system_config") if key != "" { q = q.Where("config_key = ?", key) } if err := q.Find(&list).Error; err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()}) return } if key != "" && len(list) == 1 { var val interface{} _ = json.Unmarshal(list[0].ConfigValue, &val) c.JSON(http.StatusOK, gin.H{"success": true, "data": val}) return } data := make([]gin.H, 0, len(list)) for _, row := range list { var val interface{} _ = json.Unmarshal(row.ConfigValue, &val) data = append(data, gin.H{"configKey": row.ConfigKey, "configValue": val}) } c.JSON(http.StatusOK, gin.H{"success": true, "data": data}) } // DBConfigPost POST /api/db/config func DBConfigPost(c *gin.Context) { var body struct { Key string `json:"key"` Value interface{} `json:"value"` Description string `json:"description"` } if err := c.ShouldBindJSON(&body); err != nil || body.Key == "" { c.JSON(http.StatusOK, gin.H{"success": false, "error": "配置键不能为空"}) return } valBytes, err := json.Marshal(body.Value) if err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()}) return } db := database.DB() desc := body.Description var row model.SystemConfig err = db.Where("config_key = ?", body.Key).First(&row).Error if err != nil { row = model.SystemConfig{ConfigKey: body.Key, ConfigValue: valBytes, Description: &desc} err = db.Create(&row).Error } else { row.ConfigValue = valBytes if body.Description != "" { row.Description = &desc } err = db.Save(&row).Error } if err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "message": "配置保存成功"}) } // DBUsersList GET /api/db/users func DBUsersList(c *gin.Context) { var users []model.User if err := database.DB().Find(&users).Error; err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error(), "users": []interface{}{}}) return } c.JSON(http.StatusOK, gin.H{"success": true, "users": users}) } // DBUsersAction POST /api/db/users(创建)、PUT /api/db/users(更新) func DBUsersAction(c *gin.Context) { db := database.DB() if c.Request.Method == http.MethodPost { var body struct { OpenID *string `json:"openId"` Phone *string `json:"phone"` Nickname *string `json:"nickname"` WechatID *string `json:"wechatId"` Avatar *string `json:"avatar"` IsAdmin *bool `json:"isAdmin"` } if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": "请求体无效"}) return } userID := "user_" + randomSuffix() code := "SOUL" + randomSuffix()[:4] nick := "用户" if body.Nickname != nil && *body.Nickname != "" { nick = *body.Nickname } else { nick = nick + userID[len(userID)-4:] } u := model.User{ ID: userID, Nickname: &nick, ReferralCode: &code, OpenID: body.OpenID, Phone: body.Phone, WechatID: body.WechatID, Avatar: body.Avatar, } if body.IsAdmin != nil { u.IsAdmin = body.IsAdmin } if err := db.Create(&u).Error; err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "user": u, "isNew": true, "message": "用户创建成功"}) return } // PUT 更新 var body struct { ID string `json:"id"` Nickname *string `json:"nickname"` Phone *string `json:"phone"` WechatID *string `json:"wechatId"` Avatar *string `json:"avatar"` HasFullBook *bool `json:"hasFullBook"` IsAdmin *bool `json:"isAdmin"` Earnings *float64 `json:"earnings"` PendingEarnings *float64 `json:"pendingEarnings"` } if err := c.ShouldBindJSON(&body); err != nil || body.ID == "" { c.JSON(http.StatusOK, gin.H{"success": false, "error": "用户ID不能为空"}) return } updates := map[string]interface{}{} if body.Nickname != nil { updates["nickname"] = *body.Nickname } if body.Phone != nil { updates["phone"] = *body.Phone } if body.WechatID != nil { updates["wechat_id"] = *body.WechatID } if body.Avatar != nil { updates["avatar"] = *body.Avatar } if body.HasFullBook != nil { updates["has_full_book"] = *body.HasFullBook } if body.IsAdmin != nil { updates["is_admin"] = *body.IsAdmin } if body.Earnings != nil { updates["earnings"] = *body.Earnings } if body.PendingEarnings != nil { updates["pending_earnings"] = *body.PendingEarnings } if len(updates) == 0 { c.JSON(http.StatusOK, gin.H{"success": true, "message": "没有需要更新的字段"}) return } if err := db.Model(&model.User{}).Where("id = ?", body.ID).Updates(updates).Error; err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "message": "用户更新成功"}) } func randomSuffix() string { return fmt.Sprintf("%d%x", time.Now().UnixNano()%100000, time.Now().UnixNano()&0xfff) } // DBUsersDelete DELETE /api/db/users func DBUsersDelete(c *gin.Context) { id := c.Query("id") if id == "" { c.JSON(http.StatusOK, gin.H{"success": false, "error": "用户ID不能为空"}) return } if err := database.DB().Where("id = ?", id).Delete(&model.User{}).Error; err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "message": "用户删除成功"}) } // DBUsersReferrals GET /api/db/users/referrals func DBUsersReferrals(c *gin.Context) { userId := c.Query("userId") if userId == "" { c.JSON(http.StatusOK, gin.H{"success": false, "error": "缺少 userId"}) return } db := database.DB() var bindings []model.ReferralBinding if err := db.Where("referrer_id = ?", userId).Order("binding_date DESC").Find(&bindings).Error; err != nil { c.JSON(http.StatusOK, gin.H{"success": true, "referrals": []interface{}{}, "stats": gin.H{"total": 0, "purchased": 0, "free": 0, "earnings": 0, "pendingEarnings": 0, "withdrawnEarnings": 0}}) return } refereeIds := make([]string, 0, len(bindings)) for _, b := range bindings { refereeIds = append(refereeIds, b.RefereeID) } var users []model.User if len(refereeIds) > 0 { db.Where("id IN ?", refereeIds).Find(&users) } userMap := make(map[string]*model.User) for i := range users { userMap[users[i].ID] = &users[i] } referrals := make([]gin.H, 0, len(bindings)) for _, b := range bindings { u := userMap[b.RefereeID] nick := "微信用户" var avatar *string var phone *string hasFullBook := false if u != nil { if u.Nickname != nil { nick = *u.Nickname } avatar, phone = u.Avatar, u.Phone if u.HasFullBook != nil { hasFullBook = *u.HasFullBook } } status := "active" if b.Status != nil { status = *b.Status } daysRemaining := 0 if b.ExpiryDate.After(time.Now()) { daysRemaining = int(b.ExpiryDate.Sub(time.Now()).Hours() / 24) } referrals = append(referrals, gin.H{ "id": b.RefereeID, "nickname": nick, "avatar": avatar, "phone": phone, "hasFullBook": hasFullBook || status == "converted", "createdAt": b.BindingDate, "bindingStatus": status, "daysRemaining": daysRemaining, "commission": b.CommissionAmount, "status": status, }) } var referrer model.User earningsE, pendingE, withdrawnE := 0.0, 0.0, 0.0 if err := db.Where("id = ?", userId).Select("earnings", "pending_earnings", "withdrawn_earnings").First(&referrer).Error; err == nil { if referrer.Earnings != nil { earningsE = *referrer.Earnings } if referrer.PendingEarnings != nil { pendingE = *referrer.PendingEarnings } if referrer.WithdrawnEarnings != nil { withdrawnE = *referrer.WithdrawnEarnings } } purchased := 0 for _, b := range bindings { u := userMap[b.RefereeID] if (u != nil && u.HasFullBook != nil && *u.HasFullBook) || (b.Status != nil && *b.Status == "converted") { purchased++ } } c.JSON(http.StatusOK, gin.H{ "success": true, "referrals": referrals, "stats": gin.H{ "total": len(bindings), "purchased": purchased, "free": len(bindings) - purchased, "earnings": earningsE, "pendingEarnings": pendingE, "withdrawnEarnings": withdrawnE, }, }) } // DBInit POST /api/db/init func DBInit(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"success": true, "data": gin.H{"message": "初始化接口已就绪(表结构由迁移维护)"}}) } // DBDistribution GET /api/db/distribution func DBDistribution(c *gin.Context) { userId := c.Query("userId") db := database.DB() var bindings []model.ReferralBinding q := db.Order("binding_date DESC").Limit(500) if userId != "" { q = q.Where("referrer_id = ?", userId) } if err := q.Find(&bindings).Error; err != nil { c.JSON(http.StatusOK, gin.H{"success": true, "bindings": []interface{}{}, "total": 0}) return } referrerIds := make(map[string]bool) refereeIds := make(map[string]bool) for _, b := range bindings { referrerIds[b.ReferrerID] = true refereeIds[b.RefereeID] = true } allIds := make([]string, 0, len(referrerIds)+len(refereeIds)) for id := range referrerIds { allIds = append(allIds, id) } for id := range refereeIds { if !referrerIds[id] { allIds = append(allIds, id) } } var users []model.User if len(allIds) > 0 { db.Where("id IN ?", allIds).Find(&users) } userMap := make(map[string]*model.User) for i := range users { userMap[users[i].ID] = &users[i] } out := make([]gin.H, 0, len(bindings)) for _, b := range bindings { refNick := "用户" if u := userMap[b.RefereeID]; u != nil && u.Nickname != nil { refNick = *u.Nickname } else { refNick = refNick + b.RefereeID } var referrerName *string if u := userMap[b.ReferrerID]; u != nil { referrerName = u.Nickname } days := 0 if b.ExpiryDate.After(time.Now()) { days = int(b.ExpiryDate.Sub(time.Now()).Hours() / 24) } var refereePhone *string if u := userMap[b.RefereeID]; u != nil { refereePhone = u.Phone } out = append(out, gin.H{ "id": b.ID, "referrer_id": b.ReferrerID, "referrer_name": referrerName, "referrer_code": b.ReferralCode, "referee_id": b.RefereeID, "referee_nickname": refNick, "referee_phone": refereePhone, "bound_at": b.BindingDate, "expires_at": b.ExpiryDate, "status": b.Status, "days_remaining": days, "commission": b.CommissionAmount, "source": "miniprogram", }) } c.JSON(http.StatusOK, gin.H{"success": true, "bindings": out, "total": len(out)}) } // DBChapters GET/POST /api/db/chapters func DBChapters(c *gin.Context) { var list []model.Chapter if err := database.DB().Order("sort_order ASC, id ASC").Find(&list).Error; err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error(), "data": []interface{}{}}) return } c.JSON(http.StatusOK, gin.H{"success": true, "data": list}) } // DBConfigDelete DELETE /api/db/config func DBConfigDelete(c *gin.Context) { key := c.Query("key") if key == "" { c.JSON(http.StatusOK, gin.H{"success": false, "error": "配置键不能为空"}) return } if err := database.DB().Where("config_key = ?", key).Delete(&model.SystemConfig{}).Error; err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true}) } // DBInitGet GET /api/db/init func DBInitGet(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"success": true, "data": gin.H{"message": "ok"}}) } // DBMigrateGet GET /api/db/migrate func DBMigrateGet(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"success": true, "message": "迁移状态查询(由 Prisma/外部维护)"}) } // DBMigratePost POST /api/db/migrate func DBMigratePost(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"success": true, "message": "迁移由 Prisma/外部执行"}) }