Coze用戶賬號設置修改用戶頭像-后端源碼

前言

本文將深入分析Coze Studio項目的用戶頭像修改功能后端實現,通過源碼解讀來理解整個頭像上傳和更新流程的架構設計和技術實現。用戶頭像修改作為用戶個人信息管理系統的重要組成部分,主要負責處理圖片文件上傳、存儲和用戶信息更新,提升用戶個性化體驗。

頭像修改功能涉及文件上傳、圖片處理、云存儲和數據庫更新等多個技術環節,在用戶體驗和系統性能方面都有重要意義。本文將從IDL接口定義開始,逐層深入到API網關、應用服務、領域服務、數據訪問等各個層次,全面解析頭像修改流程的技術實現。

項目架構概覽

整體架構設計

Coze Studio后端采用了經典的分層架構模式,將用戶頭像修改功能劃分為以下幾個核心層次:

┌─────────────────────────────────────────────────────────────┐
│                    IDL接口定義層                             │
│  ┌─────────────┐  ┌─────────────  ┐    ┌─────────────┐      │
│  │ base.thrift │  │passport.thrift│    │ api.thrift  │      │
│  └─────────────┘  └─────────────  ┘    └─────────────┘      │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│                    API網關層                                 │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐          │
│  │   Model     │  │   Service   │  │   Router    │          │
│  │   定義      │  │   處理器     │  │   路由       │          │
│  └─────────────┘  └─────────────┘  └─────────────┘          │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│                   應用服務層                                 │
│  ┌─────────────────────────────────────────────────────┐    │
│  │            UserApplicationService                   │    │
│  │            UserUpdateAvatar                         │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│                   領域服務層                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │              UserDomain                             │   │
│  │         UpdateAvatar + OSS存儲                       │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│                   數據訪問層                                 │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐          │
│  │ Repository  │  │    DAO      │  │ query&Model │          │
│  │   接口       │ │    實現      │  │  查詢和數據  │          │
│  └─────────────┘  └─────────────┘  └─────────────┘          │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│                   云存儲服務層                               │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                OSS對象存儲                           │   │
│  │           文件上傳 + URL生成                         │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

用戶頭像修改流程概述

用戶頭像修改的完整流程如下:

前端發起頭像上傳請求↓
API網關層接收multipart/form-data請求↓
文件類型驗證和內容讀取↓
應用服務層處理業務邏輯↓
領域服務層執行頭像更新邏輯↓
上傳文件到OSS對象存儲↓
數據訪問層更新用戶頭像URI↓
生成頭像訪問URL并返回↓
前端更新頭像顯示

1. IDL接口定義層

IDL基礎類型定義(base.thrift)

文件位置:idl/base.thrift
核心代碼:

namespace py base
namespace go base
namespace java com.bytedance.thrift.basestruct TrafficEnv {1: bool   Open = false,2: string Env  = ""   ,
}struct Base {1:          string             LogID      = "",2:          string             Caller     = "",3:          string             Addr       = "",4:          string             Client     = "",5: optional TrafficEnv         TrafficEnv     ,6: optional map<string,string> Extra          ,
}struct BaseResp {1:          string             StatusMessage = "",2:          i32                StatusCode    = 0 ,3: optional map<string,string> Extra             ,
}struct EmptyReq {
}struct EmptyData {}struct EmptyResp {1: i64       code,2: string    msg ,3: EmptyData data,
}struct EmptyRpcReq {255: optional Base Base,
}struct EmptyRpcResp {255: optional BaseResp BaseResp,
}

文件作用:
定義了項目中所有接口的基礎數據結構,作為其他IDL文件的依賴基礎。

IDL用戶認證接口定義(passport.thrift)

文件位置:idl/passport/passport.thrift
核心代碼:

namespace py passport
namespace go passport
namespace java com.bytedance.thrift.passportinclude "base.thrift"struct User {// Align with the original interface field name1: required i64 user_id_str (agw.js_conv="str", api.js_conv="true")2: required string name3: required string user_unique_name4: required string email5: required string description6: required string avatar_url7: optional string screen_name8: optional AppUserInfo app_user_info9: optional string locale10: i64 user_create_time // unix timestamp in seconds
}struct UserUpdateAvatarRequest {3: required binary avatar (api.form="avatar")
}struct UserUpdateAvatarResponseData {1: required string web_uri
}struct UserUpdateAvatarResponse {1: required UserUpdateAvatarResponseData data253: required i32 code254: required string msg
}service PassportService {UserUpdateAvatarResponse UserUpdateAvatar(1: UserUpdateAvatarRequest req) (api.post="/api/web/user/update/upload_avatar/", api.serializer="form")
}

文件作用:
定義了用戶頭像修改相關的數據結構和服務接口,包括:

  • User結構體:包含用戶基本信息,其中avatar_url字段存儲頭像訪問URL
  • UserUpdateAvatarRequest:頭像上傳請求,包含二進制文件數據
  • UserUpdateAvatarResponse:頭像上傳響應,返回新的頭像URI
  • PassportService:定義頭像上傳接口,使用POST方法和form序列化

IDL主API服務聚合文件(api.thrift)

文件位置:idl/api.thrift
核心代碼:

include "./passport/passport.thrift"namespace go coze// 聚合多個業務服務接口
service PassportService extends passport.PassportService {}
// 其他服務接口也會在此文件中聚合

文件作用:
項目的API聚合文件,統一組織所有業務服務接口,作為Hertz代碼生成的入口點。

這里使用了Apache Thrift作為IDL(接口定義語言),定義了頭像上傳接口的請求和響應結構。Thrift的優勢在于:

  • 跨語言支持
  • 自動代碼生成
  • 強類型約束
  • 高效的序列化
  • 支持二進制數據傳輸

2. API網關層

接口定義-passport.go文件詳細分析

文件位置:backend/api/model/passport/passport.go
核心代碼:

type UserUpdateAvatarRequest struct {Avatar []byte `thrift:"avatar,1,required" form:"avatar,required" json:"avatar,required" query:"avatar,required"`
}type UserUpdateAvatarResponseData struct {WebURI string `thrift:"web_uri,1,required" form:"web_uri,required" json:"web_uri,required" query:"web_uri,required"`
}type UserUpdateAvatarResponse struct {Data UserUpdateAvatarResponseData `thrift:"data,1,required" form:"data,required" json:"data,required" query:"data,required"`Code int32                        `thrift:"code,253,required" form:"code,required" json:"code,required" query:"code,required"`Msg  string                       `thrift:"msg,254,required" form:"msg,required" json:"msg,required" query:"msg,required"`
}type PassportService interface {// UserUpdateAvatar update user avatarUserUpdateAvatar(ctx context.Context, req *UserUpdateAvatarRequest) (r *UserUpdateAvatarResponse, err error)
}

文件作用:
由thriftgo自動生成的Go代碼文件,基于IDL定義生成對應的Go結構體和接口,提供類型安全的API模型。

接口實現-passport_service.go文件詳細分析

文件位置:backend/api/handler/coze/passport_service.go
核心代碼:

// UserUpdateAvatar .
// @router /web/user/update/upload_avatar/ [POST]
func UserUpdateAvatar(ctx context.Context, c *app.RequestContext) {var err errorvar req passport.UserUpdateAvatarRequest// Get the uploaded filefile, err := c.FormFile("avatar")if err != nil {logs.CtxErrorf(ctx, "Get Avatar Fail failed, err=%v", err)invalidParamRequestResponse(c, "missing avatar file")return}// Check file typeif !strings.HasPrefix(file.Header.Get("Content-Type"), "image/") {invalidParamRequestResponse(c, "invalid file type, only image allowed")return}// Read file contentsrc, err := file.Open()if err != nil {internalServerErrorResponse(ctx, c, err)return}defer src.Close()fileContent, err := io.ReadAll(src)if err != nil {internalServerErrorResponse(ctx, c, err)return}req.Avatar = fileContentmimeType := file.Header.Get("Content-Type")resp, err := user.UserApplicationSVC.UserUpdateAvatar(ctx, mimeType, &req)if err != nil {internalServerErrorResponse(ctx, c, err)return}c.JSON(http.StatusOK, resp)
}

文件作用:
實現了Passport服務的頭像上傳接口處理器,負責:

  1. 文件接收:從multipart/form-data請求中獲取上傳的文件
  2. 文件驗證:檢查文件類型,確保是圖片格式
  3. 文件讀取:將文件內容讀取為字節數組
  4. 業務調用:調用應用服務處理頭像更新邏輯
  5. 響應返回:返回JSON格式的響應結果

@router注解的作用

在passport_service.go中,我們可以看到:

// @router /web/user/update/upload_avatar/ [POST]
func UserUpdateAvatar

這個@router注解告訴Hertz代碼生成器:

  • URL路徑:/web/user/update/upload_avatar/
  • HTTP方法:POST
  • 處理函數:UserUpdateAvatar

代碼生成機制

Hertz框架使用IDL驅動的代碼生成機制:

  1. IDL文件定義:項目中的api.thrift和相關thrift文件定義了API接口
  2. 注解解析:Hertz生成器掃描所有帶有@router注解的函數
  3. 路由代碼生成:自動生成api.go文件

路由注冊實現-api.go文件詳細分析

文件位置:backend/api/router/coze/api.go
核心代碼:

// Code generated by hertz generator. DO NOT EDIT.func Register(r *server.Hertz) {root := r.Group("/", rootMw()...){_api := root.Group("/api", _apiMw()...){_web := _api.Group("/web", _webMw()...){_user := _web.Group("/user", _userMw()...){_update := _user.Group("/update", _updateMw()...){_upload_avatar := _update.Group("/upload_avatar", _upload_avatarMw()...)_upload_avatar.POST("/", append(_userupdateavatarMw(), coze.UserUpdateAvatar)...)}}}}}
}

文件作用:
此文件是Coze Studio后端的核心路由注冊文件,由hertz generator自動生成,負責將所有HTTP API接口路由與對應的處理函數進行綁定和注冊。該文件構建了完整的RESTful API路由樹結構。

注意:實際文件中包含了項目的所有路由定義,這里僅展示頭像上傳相關的路由部分。

中間件系統-middleware.go文件詳細分析

文件位置:backend/api/router/coze/middleware.go
核心代碼:

func _userupdateavatarMw() []app.HandlerFunc {// 頭像上傳接口專用中間件return nil
}

文件作用:

  1. 中間件函數定義:為項目中的每個路由組和特定路由提供中間件掛載點
  2. 路由層級管理:按照路由的層級結構組織中間件函數
  3. 開發者擴展接口:提供統一的接口供開發者添加自定義中間件邏輯

API網關層Restful接口路由-Coze+Hertz

Hertz為每個HTTP方法維護獨立的路由樹,通過分組路由的方式構建層次化的API結構:

/api/web/user/update/upload_avatar/ [POST]
├── _userupdateavatarMw() # 接口級中間件
└── coze.UserUpdateAvatar  # 處理函數

這種設計的優勢:

  • 層次化管理:不同層級的中間件處理不同的關注點
  • 可擴展性:每個層級都可以獨立添加中間件
  • 性能優化:中間件按需執行,避免不必要的開銷
  • 文件上傳支持:專門處理multipart/form-data格式的文件上傳

3. 應用服務層

UserApplicationService初始化

文件位置:backend/application/user/user.go
核心代碼:

type UserApplicationService struct {oss       storage.StorageDomainSVC user.User
}var UserApplicationSVC *UserApplicationServicefunc InitUserApplicationService(oss storage.Storage, domainSVC user.User) {UserApplicationSVC = &UserApplicationService{oss:       oss,DomainSVC: domainSVC,}
}

應用服務實現-user.go文件詳細分析

文件位置:backend/application/user/user.go
核心代碼:

// UserUpdateAvatar Update user avatar
func (u *UserApplicationService) UserUpdateAvatar(ctx context.Context, mimeType string, req *passport.UserUpdateAvatarRequest) (resp *passport.UserUpdateAvatarResponse, err error,
) {// Get file suffix by MIME typevar ext stringswitch mimeType {case "image/jpeg", "image/jpg":ext = "jpg"case "image/png":ext = "png"case "image/gif":ext = "gif"case "image/webp":ext = "webp"default:return nil, errorx.WrapByCode(err, errno.ErrUserInvalidParamCode,errorx.KV("msg", "unsupported image type"))}uid := ctxutil.MustGetUIDFromCtx(ctx)url, err := u.DomainSVC.UpdateAvatar(ctx, uid, ext, req.GetAvatar())if err != nil {return nil, err}return &passport.UserUpdateAvatarResponse{Data: &passport.UserUpdateAvatarResponseData{WebURI: url,},Code: 0,}, nil
}

文件作用:
應用服務層的核心實現,負責:

  1. MIME類型處理:接收并處理從API層傳遞的MIME類型參數
  2. 文件擴展名映射:根據MIME類型確定文件擴展名
  3. 格式驗證:驗證上傳的文件是否為支持的圖片格式
  4. 業務協調:調用領域服務執行頭像更新邏輯
  5. 響應構建:構建標準化的響應結構

用戶ID獲取機制

uid := ctxutil.MustGetUIDFromCtx(ctx)

這里使用了ctxutil.MustGetUIDFromCtx函數從請求上下文中獲取用戶ID。這個用戶ID通常是在認證中間件中設置的,確保只有已認證的用戶才能執行頭像更新操作。

文件類型檢測機制

應用服務層接收從API層傳遞的MIME類型參數,并將其映射為文件擴展名,支持常見的圖片格式:

  • JPEG (image/jpeg, image/jpg)
  • PNG (image/png)
  • GIF (image/gif)
  • WebP (image/webp)

實際的文件類型檢測在API層的UserUpdateAvatar處理函數中進行:

// Check file type
if !strings.HasPrefix(file.Header.Get("Content-Type"), "image/") {invalidParamRequestResponse(c, "invalid file type, only image allowed")return
}

應用服務結構

type UserApplicationService struct {oss       storage.StorageDomainSVC user.User
}

應用服務通過依賴注入的方式獲取OSS存儲服務和領域服務實例,實現了層次間的解耦。

4. 領域服務層

領域服務接口定義

文件位置:backend/domain/user/service/user.go
核心代碼:

type User interface {// UpdateAvatar 更新用戶頭像UpdateAvatar(ctx context.Context, userID int64, ext string, imagePayload []byte) (url string, err error)// 其他方法...
}

領域服務實現-user_impl.go文件詳細分析

文件位置:backend/domain/user/service/user_impl.go
核心代碼:

func (u *userImpl) UpdateAvatar(ctx context.Context, userID int64, ext string, imagePayload []byte) (url string, err error) {// 生成頭像存儲鍵名avatarKey := "user_avatar/" + strconv.FormatInt(userID, 10) + "." + ext// 上傳文件到OSSerr = u.IconOSS.PutObject(ctx, avatarKey, imagePayload)if err != nil {return "", err}// 更新數據庫中的頭像URIerr = u.UserRepo.UpdateAvatar(ctx, userID, avatarKey)if err != nil {return "", err}// 生成頭像訪問URLurl, err = u.IconOSS.GetObjectUrl(ctx, avatarKey)if err != nil {return "", err}return url, nil
}

文件作用:
領域服務層的核心實現,負責:

  1. 存儲鍵生成:根據用戶ID和文件擴展名生成唯一的存儲鍵
  2. 文件上傳:將圖片文件上傳到OSS對象存儲
  3. 數據更新:更新數據庫中用戶的頭像URI字段
  4. URL生成:生成頭像的訪問URL供前端使用
  5. 事務處理:確保文件上傳和數據庫更新的一致性

領域服務層實現-業務實體

文件位置:backend/domain/user/entity/user.go
核心代碼:

package entitytype User struct {UserID       int64Name         string // User NicknameUniqueName   string // User Unique NameEmail        string // EmailDescription  string // User DescriptionIconURI      string // Avatar URIIconURL      string // Avatar URLUserVerified bool   // User Verification StatusLocale       string // LocaleSessionKey   string // Session KeyCreatedAt    int64  // Creation Time (Milliseconds)UpdatedAt    int64  // Update Time (Milliseconds)
}

文件作用:是用戶領域的實體(Entity)定義文件,屬于 DDD(領域驅動設計)架構中的實體層。該文件定義了用戶的核心數據結構,其中IconURI存儲頭像在OSS中的路徑,IconURL存儲頭像的訪問URL。實體層通過userPo2Do函數將數據庫模型轉換為領域實體。

領域服務組件結構

type Components struct {IconOSS   storage.StorageIDGen     idgen.IDGeneratorUserRepo  repository.UserRepositorySpaceRepo repository.SpaceRepository
}type userImpl struct {*Components
}

領域服務通過組件注入的方式獲取所需的依賴,包括:

  • IconOSS:OSS對象存儲服務,用于文件上傳和URL生成
  • UserRepo:用戶倉儲接口,用于數據庫操作
  • IDGen:ID生成器
  • SpaceRepo:空間倉儲接口

頭像存儲策略

頭像文件在OSS中的存儲路徑規則:

user_avatar/{userID}.{ext}

例如:user_avatar/123456.jpg

這種命名策略的優勢:

  • 唯一性:每個用戶只有一個頭像文件
  • 可預測性:根據用戶ID可以直接構造文件路徑
  • 覆蓋更新:新頭像會自動覆蓋舊頭像,無需清理舊文件
  • 擴展名保留:保留原始文件的擴展名,便于識別文件類型

5. 數據訪問層

倉儲接口定義

根據搜索結果,用戶倉儲接口定義如下:
文件位置:backend/domain/user/repository/repository.go
核心代碼:

type UserRepository interface {// UpdateAvatar 更新用戶頭像URIUpdateAvatar(ctx context.Context, userID int64, iconURI string) error// 其他方法...GetUserByID(ctx context.Context, userID int64) (*model.User, error)CreateUser(ctx context.Context, user *model.User) errorGetUsersByIDs(ctx context.Context, userIDs []int64) ([]*model.User, error)
}

DAO實現-user.go文件詳細分析

文件位置:backend/domain/user/internal/dal/user.go
核心代碼:

func (dao *UserDAO) UpdateAvatar(ctx context.Context, userID int64, iconURI string) error {_, err := dao.query.User.WithContext(ctx).Where(dao.query.User.ID.Eq(userID),).Updates(map[string]interface{}{"icon_uri":   iconURI,"updated_at": time.Now().UnixMilli(),})return err
}

文件作用:
數據訪問層的核心實現,負責:

  1. 數據庫操作:使用GORM執行SQL更新操作
  2. 頭像URI更新:將用戶表中的icon_uri字段更新為新的OSS存儲路徑
  3. 時間戳更新:同時更新updated_at字段記錄修改時間
  4. 條件查詢:根據用戶ID精確定位要更新的記錄

實現特點

  • 類型安全查詢:使用GORM生成的查詢構建器,避免SQL注入
  • 原子更新:同時更新icon_uriupdated_at字段,保證數據一致性
  • 時間戳管理:使用毫秒級時間戳,與前端JavaScript時間格式兼容
  • 上下文傳遞:支持請求上下文,便于鏈路追蹤和超時控制

DAO結構設計

type UserDAO struct {query *query.Query
}func NewUserDAO(db *gorm.DB) *UserDAO {return &UserDAO{query: query.Use(db),}
}

DAO通過GORM的查詢構建器實現類型安全的數據庫操作。

數據模型層

用戶模型定義

文件位置:backend/domain/user/internal/dal/model/user.gen.go
核心代碼:

const TableNameUser = "user"// User User Table
type User struct {ID           int64          `gorm:"column:id;primaryKey;autoIncrement:true;comment:Primary Key ID" json:"id"`                               // Primary Key IDName         string         `gorm:"column:name;not null;comment:User Nickname" json:"name"`                                                 // User NicknameUniqueName   string         `gorm:"column:unique_name;not null;comment:User Unique Name" json:"unique_name"`                                // User Unique NameEmail        string         `gorm:"column:email;not null;comment:Email" json:"email"`                                                       // EmailPassword     string         `gorm:"column:password;not null;comment:Password (Encrypted)" json:"password"`                                  // Password (Encrypted)Description  string         `gorm:"column:description;not null;comment:User Description" json:"description"`                                // User DescriptionIconURI      string         `gorm:"column:icon_uri;not null;comment:Avatar URI" json:"icon_uri"`                                            // Avatar URIUserVerified bool           `gorm:"column:user_verified;not null;comment:User Verification Status" json:"user_verified"`                    // User Verification StatusLocale       string         `gorm:"column:locale;not null;comment:Locale" json:"locale"`                                                    // LocaleSessionKey   string         `gorm:"column:session_key;not null;comment:Session Key" json:"session_key"`                                     // Session KeyCreatedAt    int64          `gorm:"column:created_at;not null;autoCreateTime:milli;comment:Creation Time (Milliseconds)" json:"created_at"` // Creation Time (Milliseconds)UpdatedAt    int64          `gorm:"column:updated_at;not null;autoUpdateTime:milli;comment:Update Time (Milliseconds)" json:"updated_at"`   // Update Time (Milliseconds)DeletedAt    int64          `gorm:"column:deleted_at;comment:Deletion Time (Milliseconds)" json:"deleted_at"`
}func (*User) TableName() string {return TableNameUser
}

其中IconURI字段用于存儲用戶頭像在OSS中的存儲路徑,這是頭像功能的核心數據字段。

用戶模型查詢方法
  • 基于 User 模型生成查詢結構體
  • 包含 user 結構體和 IUserDo 接口
  • 生成所有 CRUD 方法和查詢構建器
    文件位置:backend\domain\user\internal\dal\query\user.gen.go
    示例代碼:
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.package queryimport ("context""gorm.io/gorm""gorm.io/gorm/clause""gorm.io/gorm/schema""gorm.io/gen""gorm.io/gen/field""gorm.io/plugin/dbresolver""github.com/coze-dev/coze-studio/backend/domain/user/internal/dal/model"
)func newUser(db *gorm.DB, opts ...gen.DOOption) user {_user := user{}_user.userDo.UseDB(db, opts...)_user.userDo.UseModel(&model.User{})tableName := _user.userDo.TableName()_user.ALL = field.NewAsterisk(tableName)_user.ID = field.NewInt64(tableName, "id")_user.Name = field.NewString(tableName, "name")_user.UniqueName = field.NewString(tableName, "unique_name")_user.Email = field.NewString(tableName, "email")_user.Password = field.NewString(tableName, "password")_user.Description = field.NewString(tableName, "description")_user.IconURI = field.NewString(tableName, "icon_uri")_user.UserVerified = field.NewBool(tableName, "user_verified")_user.Locale = field.NewString(tableName, "locale")_user.SessionKey = field.NewString(tableName, "session_key")_user.CreatedAt = field.NewInt64(tableName, "created_at")_user.UpdatedAt = field.NewInt64(tableName, "updated_at")_user.DeletedAt = field.NewField(tableName, "deleted_at")_user.fillFieldMap()return _user
}// user User Table
type user struct {userDoALL          field.AsteriskID           field.Int64  // Primary Key IDName         field.String // User NicknameUniqueName   field.String // User Unique NameEmail        field.String // EmailPassword     field.String // Password (Encrypted)Description  field.String // User DescriptionIconURI      field.String // Avatar URIUserVerified field.Bool   // User Verification StatusLocale       field.String // LocaleSessionKey   field.String // Session KeyCreatedAt    field.Int64  // Creation Time (Milliseconds)UpdatedAt    field.Int64  // Update Time (Milliseconds)DeletedAt    field.Field  // Deletion Time (Milliseconds)fieldMap map[string]field.Expr
}func (u user) Table(newTableName string) *user {u.userDo.UseTable(newTableName)return u.updateTableName(newTableName)
}func (u user) As(alias string) *user {u.userDo.DO = *(u.userDo.As(alias).(*gen.DO))return u.updateTableName(alias)
}
統一查詢入口生成

文件位置:backend/domain/user/internal/dal/query/gen.go
示例代碼:

// Code generated by gorm.io/gen. DO NOT EDIT.package queryimport ("context""database/sql""gorm.io/gorm""gorm.io/gen""gorm.io/plugin/dbresolver"
)var (Q         = new(Query)Space     *spaceSpaceUser *spaceUserUser      *user
)func SetDefault(db *gorm.DB, opts ...gen.DOOption) {*Q = *Use(db, opts...)Space = &Q.SpaceSpaceUser = &Q.SpaceUserUser = &Q.User
}func Use(db *gorm.DB, opts ...gen.DOOption) *Query {return &Query{db:        db,Space:     newSpace(db, opts...),SpaceUser: newSpaceUser(db, opts...),User:      newUser(db, opts...),}
}type Query struct {db *gorm.DBSpace     spaceSpaceUser spaceUserUser      user
}func (q *Query) Available() bool { return q.db != nil }func (q *Query) clone(db *gorm.DB) *Query {return &Query{db:        db,Space:     q.Space.clone(db),SpaceUser: q.SpaceUser.clone(db),User:      q.User.clone(db),}
}func (q *Query) ReadDB() *Query {return q.ReplaceDB(q.db.Clauses(dbresolver.Read))
}func (q *Query) WriteDB() *Query {return q.ReplaceDB(q.db.Clauses(dbresolver.Write))
}func (q *Query) ReplaceDB(db *gorm.DB) *Query {return &Query{db:        db,Space:     q.Space.replaceDB(db),SpaceUser: q.SpaceUser.replaceDB(db),User:      q.User.replaceDB(db),}
}type queryCtx struct {Space     ISpaceDoSpaceUser ISpaceUserDoUser      IUserDo
}func (q *Query) WithContext(ctx context.Context) *queryCtx {return &queryCtx{Space:     q.Space.WithContext(ctx),SpaceUser: q.SpaceUser.WithContext(ctx),User:      q.User.WithContext(ctx),}
}func (q *Query) Transaction(fc func(tx *Query) error, opts ...*sql.TxOptions) error {return q.db.Transaction(func(tx *gorm.DB) error { return fc(q.clone(tx)) }, opts...)
}

6.基礎設施層

database.go文件詳解

文件位置:backend/infra/contract/orm/database.go
核心代碼:

package ormimport ("gorm.io/gorm"
)type DB = gorm.DB

文件作用:

  • 定義了 type DB = gorm.DB ,為 GORM 數據庫對象提供類型別名
  • 作為契約層(Contract),為上層提供統一的數據庫接口抽象
  • 便于后續可能的數據庫實現替換(如從 MySQL 切換到 PostgreSQL)

mysql.go文件詳解

文件位置:backend/infra/impl/mysql/mysql.go
核心代碼:

package mysqlimport ("fmt""os""gorm.io/driver/mysql""gorm.io/gorm"
)func New() (*gorm.DB, error) {dsn := os.Getenv("MYSQL_DSN")db, err := gorm.Open(mysql.Open(dsn))if err != nil {return nil, fmt.Errorf("mysql open, dsn: %s, err: %w", dsn, err)}return db, nil
}

文件作用:

  • 定義了 New() 函數,負責建立 GORM MySQL 數據庫連接
  • 使用環境變量 MYSQL_DSN 配置數據庫連接字符串
  • 返回 *gorm.DB 實例,作為整個應用的數據庫連接對象
  • 后端服務啟動時,調用 mysql.New() 初始化數據庫連接
main.go → application.Init() → appinfra.Init() → mysql.New()

gen_orm_query.go文件詳解

文件位置:backend/types/ddl/gen_orm_query.go
核心代碼:

// 用戶領域查詢生成配置
"domain/user/internal/dal/query": {"user":       {},"space":      {},"space_user": {},
},

文件作用:
這個文件實際上包含 5 個函數(包括匿名函數),它們協同工作完成 GORM ORM 代碼的自動生成:

  • main() 是核心控制流程
  • resolveType() 處理類型解析
  • genModify() 和 timeModify() 提供字段修飾功能
  • findProjectRoot() 提供路徑查找支持

整個腳本的設計體現了函數式編程和閉包的使用,通過高階函數和修飾器模式實現了靈活的字段類型映射和標簽配置。

文件依賴關系

依賴層次:
數據庫表結構 (schema.sql)↓    gen_orm_query.go
模型文件 (model/user.gen.go) - 模型先生成↓
查詢文件 (query/user.gen.go) - 依賴對應模型↓
統一入口 (query/gen.go) - 依賴所有查詢文件

重新生成注意事項

  • 清理舊文件:生成前會自動刪除所有 .gen.go 文件
  • 數據庫連接:確保 MySQL 服務運行且包含最新表結構
  • 依賴順序:GORM Gen 自動處理文件間的依賴關系
  • 原子操作:整個生成過程是原子的,要么全部成功要么全部失敗

這種分層生成機制確保了代碼的一致性和類型安全,同時通過依賴關系保證了生成文件的正確性。

7.數據存儲層-MYSQL數據庫表

數據庫表結構

文件位置:docker/volumes/mysql/schema.sql
核心代碼:

-- Create 'user' table
CREATE TABLE IF NOT EXISTS `user` (`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'Primary Key ID',`name` varchar(128) NOT NULL DEFAULT '' COMMENT 'User Nickname',`unique_name` varchar(128) NOT NULL DEFAULT '' COMMENT 'User Unique Name',`email` varchar(128) NOT NULL DEFAULT '' COMMENT 'Email',`password` varchar(128) NOT NULL DEFAULT '' COMMENT 'Password (Encrypted)',`description` varchar(512) NOT NULL DEFAULT '' COMMENT 'User Description',`icon_uri` varchar(512) NOT NULL DEFAULT '' COMMENT 'Avatar URI',`user_verified` bool NOT NULL DEFAULT 0 COMMENT 'User Verification Status',`locale` varchar(128) NOT NULL DEFAULT '' COMMENT 'Locale',`session_key` varchar(256) NOT NULL DEFAULT '' COMMENT 'Session Key',`created_at` bigint unsigned NOT NULL DEFAULT 0 COMMENT 'Creation Time (Milliseconds)',`updated_at` bigint unsigned NOT NULL DEFAULT 0 COMMENT 'Update Time (Milliseconds)',`deleted_at` bigint unsigned NULL COMMENT 'Deletion Time (Milliseconds)',PRIMARY KEY (`id`),INDEX `idx_session_key` (`session_key`),UNIQUE INDEX `uniq_email` (`email`),UNIQUE INDEX `uniq_unique_name` (`unique_name`)
) ENGINE=InnoDB CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT 'User Table';
文件作用:
該文件是 **Coze Studio 項目的 MySQL 數據庫初始化腳本**,用于在 Docker 環境中創建和初始化數據庫結構。**表結構特點**:
- **字段類型優化**:`icon_uri`字段使用`varchar(512)`,支持較長的OSS路徑
- **索引設計**:為`session_key`、`email`、`unique_name`創建索引,優化查詢性能
- **字符集設置**:使用`utf8mb4_unicode_ci`排序規則,支持完整的Unicode字符集
- **默認值設計**:為字符串字段設置空字符串默認值,避免NULL值處理這個 schema.sql 文件是gen_orm_query.go腳本的數據源:1. 表結構定義 → Go 模型生成
2. 字段類型映射 → Go 類型轉換
3. 索引信息 → 查詢優化提示
4. 約束條件 → 數據驗證邏輯當 schema.sql 中的表結構發生變化時,需要相應更新 gen_orm_query.go 中的配置映射,然后重新生成 ORM 代碼。## 8. 云存儲服務層### OSS存儲接口
根據代碼分析,OSS存儲服務通過`storage.Storage`接口提供:
```go
type Storage interface {// PutObject 上傳文件到對象存儲PutObject(ctx context.Context, key string, data []byte) error// GetObjectUrl 獲取文件訪問URLGetObjectUrl(ctx context.Context, key string) (string, error)
}

OSS存儲實現特點

  1. 文件上傳PutObject方法將字節數組直接上傳到OSS
  2. URL生成GetObjectUrl方法生成文件的訪問URL
  3. 路徑管理:使用統一的key命名規則管理文件
  4. 異步處理:支持上下文取消和超時控制

存儲架構優勢

  1. 解耦設計:通過接口抽象,業務邏輯與具體存儲實現解耦
  2. 可擴展性:可以輕松切換不同的對象存儲服務
  3. 高可用性:OSS提供高可用的文件存儲服務
  4. 成本優化:按需付費,無需維護文件服務器

9. 安全機制分析

文件上傳安全

文件類型驗證

系統在多個層次進行文件類型驗證:

  1. API網關層驗證
if !strings.HasPrefix(file.Header.Get("Content-Type"), "image/") {c.String(http.StatusBadRequest, "File must be an image")return
}
  1. 應用服務層驗證
// Get file suffix by MIME type
var ext string
switch mimeType {
case "image/jpeg", "image/jpg":ext = "jpg"
case "image/png":ext = "png"
case "image/gif":ext = "gif"
case "image/webp":ext = "webp"
default:return nil, errorx.WrapByCode(err, errno.ErrUserInvalidParamCode,errorx.KV("msg", "unsupported image type"))
}
文件大小限制

雖然代碼中沒有顯式的文件大小檢查,但可以通過以下方式實現:

  • HTTP服務器配置最大請求體大小
  • 中間件層添加文件大小驗證
  • OSS服務配置上傳大小限制
用戶認證驗證
uid := ctxutil.MustGetUIDFromCtx(ctx)

確保只有已認證的用戶才能上傳頭像,防止未授權訪問。

數據安全

存儲路徑隔離

每個用戶的頭像使用獨立的存儲路徑:

avatarKey := "user_avatar/" + strconv.FormatInt(userID, 10) + "." + ext

這確保了用戶之間的文件隔離,防止文件沖突和越權訪問。

數據庫事務安全

雖然代碼中沒有顯式的事務處理,但在實際生產環境中應該考慮:

  • 文件上傳和數據庫更新的原子性
  • 失敗回滾機制
  • 并發更新的處理

10. 性能優化策略

文件上傳性能

  1. 流式處理
src, err := file.Open()
if err != nil {return err
}
defer src.Close()fileBytes, err := io.ReadAll(src)

使用流式讀取,避免大文件占用過多內存。

  1. 并發上傳
    OSS上傳和數據庫更新可以考慮并發處理,但需要處理好一致性問題。

  2. CDN加速
    OSS生成的URL可以配置CDN加速,提高頭像加載速度。

數據庫性能

  1. 索引優化
    在user_id字段上建立索引,提高更新操作性能。

  2. 批量操作
    如果需要批量更新頭像,可以使用批量更新操作。

  3. 緩存策略
    可以考慮緩存用戶頭像URL,減少數據庫查詢。

存儲性能

  1. 文件壓縮
    可以在上傳前對圖片進行壓縮,減少存儲空間和傳輸時間。

  2. 多規格生成
    可以生成多種尺寸的頭像,適應不同場景的顯示需求。

  3. 預簽名URL
    對于頻繁訪問的頭像,可以使用預簽名URL減少服務器壓力。

11. 錯誤處理機制

錯誤分類

  1. 文件相關錯誤

    • 文件格式不支持
    • 文件大小超限
    • 文件讀取失敗
    • 文件上傳失敗
  2. 認證錯誤

    • 用戶未登錄
    • 會話已過期
    • 權限不足
  3. 系統錯誤

    • 數據庫連接失敗
    • OSS服務不可用
    • 網絡超時
  4. 業務錯誤

    • 用戶不存在
    • 重復上傳
    • 并發沖突

錯誤處理策略

  1. 分層錯誤處理

    • API層:HTTP狀態碼和錯誤響應
    • 應用層:業務錯誤碼轉換
    • 領域層:領域異常處理
    • 數據層:數據訪問異常處理
  2. 統一錯誤響應

type UserUpdateAvatarResponse struct {Data UserUpdateAvatarResponseData `json:"data"`Code int32                        `json:"code"`Msg  string                       `json:"msg"`
}
  1. 錯誤日志記錄
if err != nil {logs.CtxErrorf(ctx, "Get Avatar Fail failed, err=%v", err)invalidParamRequestResponse(c, "missing avatar file")return
}

12. 與其他用戶信息修改功能的對比分析

功能復雜度對比

頭像修改流程

  1. 文件上傳處理
  2. 文件類型驗證
  3. OSS存儲上傳
  4. 數據庫URI更新
  5. URL生成返回

用戶名修改流程

  1. 參數驗證
  2. 唯一性檢查
  3. 數據庫字段更新
  4. 返回成功響應

密碼修改流程

  1. 舊密碼驗證
  2. 新密碼強度檢查
  3. 密碼哈希計算
  4. 數據庫更新
  5. 會話清理

技術棧對比

頭像修改技術特點

  • 文件上傳處理
  • 對象存儲集成
  • 二進制數據處理
  • 多媒體格式支持

其他信息修改技術特點

  • 文本數據處理
  • 數據驗證邏輯
  • 數據庫事務處理
  • 緩存更新機制

性能特點對比

頭像修改性能特點

  • IO密集型(文件上傳)
  • 網絡帶寬依賴
  • 存儲空間消耗
  • CDN緩存優化

其他信息修改性能特點

  • CPU密集型(驗證計算)
  • 數據庫操作為主
  • 內存使用較少
  • 緩存命中優化

總結

Coze Studio的用戶頭像修改系統展現了現代Web應用在文件上傳和用戶體驗方面的最佳實踐:

架構亮點

  1. 完整的分層架構:從IDL定義到云存儲,每一層職責明確,代碼結構清晰
  2. 安全的文件處理:多層次的文件類型驗證和用戶認證保護
  3. 高效的存儲方案:基于OSS的對象存儲,提供高可用和可擴展的文件存儲服務
  4. 優雅的錯誤處理:統一的錯誤響應格式和完善的異常處理機制

工程實踐優勢

  1. 自動化代碼生成:基于IDL的代碼生成機制,確保前后端接口一致性
  2. 類型安全保障:從IDL到Go的完整類型鏈路,減少運行時錯誤
  3. 模塊化設計:清晰的模塊邊界和依賴注入,便于測試和維護
  4. 標準化流程:統一的開發流程和代碼規范,提高開發效率

技術創新點

  1. 多格式支持:支持JPEG、PNG、GIF、WebP等主流圖片格式
  2. 智能類型檢測:基于文件內容而非擴展名的類型檢測機制
  3. 路徑規范化:統一的文件命名規則,便于管理和維護
  4. URL動態生成:實時生成訪問URL,支持CDN和緩存策略

安全性保障

  1. 多層驗證機制:API層和應用層的雙重文件類型驗證
  2. 用戶隔離存儲:基于用戶ID的文件路徑隔離,防止越權訪問
  3. 認證狀態檢查:確保只有已認證用戶才能執行頭像更新操作
  4. 數據一致性:文件上傳和數據庫更新的協調處理

性能優化策略

  1. 存儲層面:OSS對象存儲提供高性能的文件存儲和訪問服務
  2. 傳輸層面:支持流式文件上傳,減少內存占用
  3. 緩存層面:URL生成機制支持CDN緩存加速
  4. 數據庫層面:精確的更新操作和合理的索引設計

整體技術價值

  1. 企業級標準:符合企業級應用的安全性、可靠性和可擴展性要求
  2. 用戶體驗優化:快速的上傳響應和即時的頭像更新顯示
  3. 開發效率提升:自動化工具鏈和標準化開發流程
  4. 運維友好性:清晰的日志記錄和錯誤處理,便于問題排查
  5. 成本控制:基于云服務的按需付費模式,降低運維成本

擴展性考慮

  1. 多規格支持:可擴展支持不同尺寸的頭像生成
  2. 批量處理:可擴展支持批量頭像上傳和處理
  3. 格式轉換:可擴展支持圖片格式自動轉換和優化
  4. 審核機制:可擴展集成內容審核服務,確保頭像內容合規

用戶頭像修改功能作為用戶個人信息管理的重要組成部分,其設計思路體現了系統在用戶體驗、安全性和性能方面的綜合考慮。通過與其他用戶信息修改功能的對比分析,我們可以看到Coze Studio在文件處理和云服務集成方面的技術優勢。這套頭像修改系統的設計為構建現代化的用戶管理系統提供了很好的參考價值,特別是在文件上傳、云存儲集成和用戶體驗優化方面的最佳實踐。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/94604.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/94604.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/94604.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Day 33】Linux-Mysql日志

一、數據庫日志的核心作用故障恢復&#xff1a;當數據庫因崩潰&#xff08;如斷電、進程異常&#xff09;、誤操作&#xff08;如刪表&#xff09;導致數據丟失時&#xff0c;通過日志恢復數據至一致狀態。數據一致性保障&#xff1a;確保事務的 ACID 特性&#xff08;尤其是原…

服務器支持IPv6嗎?如何讓服務器支持IPv6

服務器是否支持 IPv6 需要視具體的服務商、服務器配置和網絡環境而定。以下是關于 服務器支持 IPv6 的相關知識、如何檢查支持情況&#xff0c;以及如何配置服務器以支持 IPv6 的詳細指南。 1. 什么是 IPv6&#xff1f; IPv6&#xff08;Internet Protocol Version 6&#xff…

37、需求預測與庫存優化 (快消品) - /供應鏈管理組件/fmcg-inventory-optimization

76個工業組件庫示例匯總 需求預測與庫存優化 (快消品) - 自定義組件 概述 這是一個用于模擬和可視化快消品 (FMCG) 需求預測與庫存優化流程的組件。用戶可以選擇不同的產品和區域&#xff0c;調整預測參數和庫存策略&#xff0c;然后運行模擬以查看歷史銷售、預測需求以及基…

vuex如何在js文件中使用

文章目錄前言代碼實現1. 導出2. 使用總結前言 vue項目中比較常用的vuex&#xff0c;一般是在.vue文件中使用&#xff0c;那如何在.js文件中使用呢? 代碼實現 1. 導出 vuex-store.js&#xff1a; export default {state: {isLogin: false,},mutations: {updateIsLogin(stat…

ESP32開發WSL_VSCODE環境搭建

系列文章目錄 ESP32開發WSL_VSCODE環境搭建 文章目錄 系列文章目錄 前言 一、ESP32 WSL開發環境是啥子? 二、搭建步驟 1.啟用WSL功能 2.安裝Ubuntu系統 2.1 微軟商店下載 2.2 下載發行版 2.3 安裝完成后的密碼設置 3. 環境配置 3.1 更新 apt工具 3.2 安裝ESP-IDF相關應用 3.3…

分布式鎖設計實戰:多級緩存防御設計優化同步性能

JVM層的雙重檢查鎖(Double-Checked Locking)是一種在多線程環境下優化同步性能的設計模式,主要用于減少鎖競爭和提高緩存訪問效率。其核心原理如下: ?工作流程? 第一次檢查:線程先無鎖讀取緩存,若命中則直接返回數據 加鎖同步:若未命中,線程進入同步代碼塊 第二次檢查…

C#自定義工具類-時間日期工具類

目錄 時間日期工具類DateTimeHelper 功能說明 日期格式化 時間戳轉換 時間間隔計算 日期邊界與調整 時區轉換 日期解析 時間相等性判斷 時間范圍與先后判斷 日期合法性與特殊判斷 截斷時間到指定精度 完整代碼 本篇文章分享一下時間日期工具類DateTimeHelper&…

ComfyUI AI一鍵換裝工作流無私分享

1 模型文件準備 clip_l.safetensors &#xff1a;CLIP 文本編碼器&#xff0c;將你的提示詞轉換為文本向量&#xff0c;用于指導圖像生成。下載到text_encoderst5xxl_fp16.safetensors&#xff1a;處理復雜文本任務&#xff0c;如提示重寫、文本理解、caption 生成。用于處理半…

Windows 下 Qt 獲取系統唯一 ID

前言 隨著信息化和智能化的發展,軟件產品的分發與使用環境日益多樣化,軟件盜版、非法復制和未經授權的使用問題愈發突出。為了有效保護軟件知識產權,同時確保系統和用戶數據的安全,軟件開發者需要一種可靠的方法來識別每一臺設備的唯一性,從而實現“一機一碼”的驗證機制…

Devops之Jenkins:Jenkins服務器中的slave節點是什么?我們為什么要使用slave節點?如何添加一個windows slave節點?

Jenkins服務器中的slave節點是什么&#xff1f;Jenkins 的slave節點是一臺可以在其上執行作業的計算機。從屬作業的文件系統、環境變量、操作系統和內存都基于slave節點。您無需在slave節點上安裝 Jenkins&#xff0c;它就可以正常工作。我們為什么要使用slave節點&#xff1f;…

大數據世界的開拓者:深入淺出MapReduce分布式計算經典范式

在我們這個數據爆炸的時代,單臺計算機的處理能力早已無法應對PB(Petabyte)乃至EB(Exabyte)級別數據的處理需求。想象一下,要在一臺普通的電腦上統計全互聯網所有網頁中出現頻率最高的100個詞匯,這可能需要耗費數年時間。于是,我們需要一種方法,能將一個巨大的任務拆解…

神經網絡|(十一)概率論基礎知識-協方差

【1】引言 前序學習進程中&#xff0c;已經對概率論的基礎知識做了學習&#xff0c;比如貝特斯公式、樸素貝葉斯算法拉普拉斯平滑計算條件概率等。 在此基礎上&#xff0c;我們又對scikit-learn的使用進行了初步探索。 隨著學習的深入&#xff0c;對樣本數據的處理越來越重要&…

K8s存儲與微服務實戰精解

K8s存儲這樣生成的pod名字不固定&#xff0c;IP不固定此時是訪問一個無狀態的服務&#xff0c;那沒什么影響&#xff0c;訪問到訪問不到都沒啥影響但是如果有一個有狀態的服務&#xff0c;他要指定master&#xff0c;那此時的pod做不了負載均衡statefulset控制器無頭服務創建一…

深度學習-----《PyTorch深度學習核心應用解析:從環境搭建到模型優化的完整實踐指南》

一、深度學習框架對比核心框架對比PyTorch&#xff1a;支持GPU加速&#xff0c;底層基于NumPy&#xff0c;Meta&#xff08;原Facebook&#xff09;開發&#xff0c;生態完善&#xff08;如Llama大模型&#xff09;。TensorFlow&#xff1a;谷歌開發&#xff0c;存在2個不兼容版…

Ubuntu Server 系統安裝 Docker

文章目錄簡介Ubuntu Server 簡介VirtualBox 安裝 Ubuntu Server 系統安裝 DockerDocker 配置重啟 Docker驗證鏡像源把用戶加入 docker 組開啟 Docker Api卸載 Docker簡介 本文詳細介紹了在Ubuntu系統上安裝和配置Docker的完整流程&#xff0c;包括Docker官方源添加、依賴包安裝…

從0到1:用 Qwen3-Coder 和 高德MCP 助力數字文旅建造——國慶山西游

從0到1&#xff1a;用 Qwen3-Coder 和 高德MCP 助力數字文旅建造——國慶山西游 1. 背景 “技術不是替代旅行&#xff0c;而是讓旅途更有把握&#xff0c;讓每一次選擇更符合你的期待。” 隨著大模型與地圖服務能力的成熟&#xff0c;圍繞旅游場景的“智能行程助理”成為低門檻…

RabbitMQ--消費端異常處理與 Spring Retry

1. 消息確認機制&#xff08;ack&#xff09;RabbitMQ 消息投遞到消費者后&#xff0c;必須確認&#xff08;ack&#xff09;才能從隊列中移除&#xff1a;auto-ack true消息一投遞就算消費成功。如果消費者宕機&#xff0c;消息會丟失。一般不用。manual-ack false&#xff…

eniac:世界上第一臺通用電子計算機的傳奇

本文由「大千AI助手」原創發布&#xff0c;專注用真話講AI&#xff0c;回歸技術本質。拒絕神話或妖魔化。搜索「大千AI助手」關注我&#xff0c;一起撕掉過度包裝&#xff0c;學習真實的AI技術&#xff01; ? 1. eniac概述&#xff1a;計算機時代的黎明 eniac&#xff08;ele…

網絡與信息安全有哪些崗位:(6)安全開發工程師

想知道網絡與信息安全領域有哪些具體崗位嗎&#xff1f; 網絡與信息安全有哪些崗位&#xff1a;&#xff08;1&#xff09;網絡安全工程師-CSDN博客 網絡與信息安全有哪些崗位&#xff1a;&#xff08;2&#xff09;滲透測試工程師_網絡安全滲透工程師-CSDN博客 網絡與信息安…

C-JSON接口的使用

一、cJSON 核心數據結構cJSON 的所有操作都圍繞 cJSON 結構體展開&#xff0c;它代表 JSON 中的一個節點&#xff08;可以是對象、數組、字符串、數字等&#xff09;&#xff1a;typedef struct cJSON {struct cJSON *next, *prev; // 用于鏈表&#xff08;數組/對象的子節點…