Files
Mycontent/soul-api/internal/handler/open_platform_api.go
Alex-larget d4399a5d7a chore: update project index and meeting notes for March 31, 2026
- Added new entries for meetings discussing the integration of the "超级个体" and "@" lists across various project components.
- Updated the last modified date to March 31, 2026, in multiple project index files.
- Enhanced the read page functionality to include user phone number in navigation to other mini-programs.
- Documented changes in the sync log for assistant-doc-sync regarding the meeting outcomes.
2026-03-31 18:10:02 +08:00

149 lines
4.2 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 (
"encoding/json"
"net/http"
"strings"
"time"
"soul-api/internal/database"
"soul-api/internal/model"
"github.com/gin-gonic/gin"
)
const ctxOpenPlatformApiKeyID = "openPlatformApiKeyID"
// OpenPlatformAuthMiddleware 开放平台Authorization: Bearer op_sk_... 或 X-API-Key
func OpenPlatformAuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
secret := extractOpenPlatformSecretFromRequest(c)
if secret == "" {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"success": false,
"error": "缺少 API KeyAuthorization: Bearer op_sk_... 或 Header X-API-Key",
})
return
}
db := database.DB()
row, ok := OpenPlatformVerifySecret(db, secret)
if !ok || row == nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"success": false, "error": "API Key 无效或已吊销"})
return
}
c.Set(ctxOpenPlatformApiKeyID, row.ID)
c.Next()
}
}
func maskPhoneForLog(p string) string {
if len(p) < 8 {
return "***"
}
return p[:3] + "****" + p[len(p)-4:]
}
func extractOpenPlatformSecretFromRequest(c *gin.Context) string {
auth := strings.TrimSpace(c.GetHeader("Authorization"))
if len(auth) > 7 && strings.EqualFold(auth[:7], "Bearer ") {
return strings.TrimSpace(auth[7:])
}
if x := strings.TrimSpace(c.GetHeader("X-API-Key")); x != "" {
return x
}
return ""
}
// OpenPlatformPatchUserProfile POST /api/open/user/profile
// Body: { "phone": "必填", "mbti"?: "...", "disc"?: "...", "pdp"?: "..." } — 仅更新传入的字段,至少传 mbti/disc/pdp 之一
func OpenPlatformPatchUserProfile(c *gin.Context) {
var body struct {
Phone string `json:"phone"`
Mbti *string `json:"mbti"`
Disc *string `json:"disc"`
Pdp *string `json:"pdp"`
}
if err := c.ShouldBindJSON(&body); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "JSON 格式错误"})
return
}
phone := strings.TrimSpace(strings.ReplaceAll(body.Phone, " ", ""))
if phone == "" {
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "phone 不能为空"})
return
}
if body.Mbti == nil && body.Disc == nil && body.Pdp == nil {
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "请至少提供 mbti、disc、pdp 之一"})
return
}
db := database.DB()
var user model.User
if err := db.Where("phone = ? AND deleted_at IS NULL", phone).First(&user).Error; err != nil {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "未找到该手机号对应的用户"})
return
}
updates := make(map[string]interface{})
if body.Mbti != nil {
v := strings.TrimSpace(*body.Mbti)
updates["mbti"] = v
}
if body.Disc != nil {
v := strings.TrimSpace(*body.Disc)
updates["disc"] = v
}
if body.Pdp != nil {
v := strings.TrimSpace(*body.Pdp)
updates["pdp"] = v
}
if len(updates) == 0 {
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "没有需要更新的字段"})
return
}
updates["updated_at"] = time.Now()
t0 := time.Now()
if err := db.Model(&model.User{}).Where("id = ?", user.ID).Updates(updates).Error; err != nil {
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
return
}
var keyID uint
var keyPrefix string
if v, ok := c.Get(ctxOpenPlatformApiKeyID); ok {
if id, ok := v.(uint); ok {
keyID = id
var k model.OpenPlatformApiKey
if err := db.Select("key_prefix").First(&k, id).Error; err == nil {
keyPrefix = k.KeyPrefix
}
}
}
reqSnap, _ := json.Marshal(gin.H{"phone": maskPhoneForLog(phone), "mbti": body.Mbti, "disc": body.Disc, "pdp": body.Pdp})
respSnap, _ := json.Marshal(gin.H{"success": true, "userId": user.ID})
_ = OpenPlatformInsertLog(db, &model.OpenPlatformApiLog{
ApiKeyID: keyID,
KeyPrefix: keyPrefix,
Method: c.Request.Method,
Path: c.Request.URL.Path,
StatusCode: 200,
ClientIP: c.ClientIP(),
DurationMs: int(time.Since(t0).Milliseconds()),
RequestBody: string(reqSnap),
ResponseBody: string(respSnap),
})
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"userId": user.ID,
"phone": phone,
"updated": gin.H{
"mbti": body.Mbti != nil,
"disc": body.Disc != nil,
"pdp": body.Pdp != nil,
},
},
})
}