spark-server/.cursor/skills/spark-im-v3/enums.md
2026-09-18 14:50:41 +08:00

5.7 KiB
Raw Blame History

Enums: typed closed sets for JSON, TCP, and SQL

Closed type/status values live in internal/module/{biz}/entity as named types. OpenAPI names the same set once and $refs it. Do not invent a parallel string vocabulary.

GET /v1/meta/enums is the client bootstrap (no auth). Human pages: /docs/enums (jumpable sets) and /docs/errors (envelope_code). value is the wire number or string; name is a stable English token; label is i18n for the request language. Values are built from entity consts — do not hand-copy numbers into the handler.

Wire JSON and TCP stay numbers (or the existing strings below). Do not turn message types into strings. Do not change persisted numeric values.

Template (message/entity.Type)

type Type int16

const (
    TypeText Type = 1
    // ...
    TypeSystem Type = 9
)

func (t Type) Valid() bool {
    return t >= TypeText && t <= TypeSystem
}

Input DTOs that bind the field directly use the same numbers in validate:"oneof=...":

Gender *entity.Gender `json:"gender" validate:"omitempty,oneof=0 1 2" label:"field.gender" errmsg:"oneof:validate.user.gender_invalid"`

Nested JSON (e.g. content.t) is not a validator field; gate it with Valid() inside UnmarshalContent / Content.Validate.

Layers

Layer Rule
entity type X int / int16 / string + const + Valid(). Domain structs use the typed field.
model GORM rows stay int / int16 / string. Convert at entity() / rowFrom. Never store a Go typed enum in a GORM struct if the column is numeric.
logic JSON Response/request DTOs use the entity type so encoding/json emits the number (or the string).
HTTP validator oneof lists the same numbers or strings as the consts. i18n errmsg for oneof.
OpenAPI One named schema in components/schemas; fields $ref it. Do not inline enum: on the field.
HTTP catalog Add the set to meta/logic.Catalog and i18n enum.{set}.{name}. Served by GET /v1/meta/enums.

Name clashes: the JSON viewer relation is RelationKind; the table column is EdgeStatus. Do not name both Status in the same package if two tables differ.

Numeric catalog

Values are stable. Do not reuse a retired number.

OpenAPI schema Go JSON / TCP SQL Values
Gender user/entity.Gender gender users.gender 0未知 1男 2女
UserStatus user/entity.Status status users.status 0停用 1正常
ConversationType conversation/entity.Type (internal / conv type) conversations.type 1单聊 2群聊
MessageKind message/entity.Kind kind not a column; selects private_message vs group_message 1 / 2, same numbers as ConversationType
MessageType message/entity.Type message_type, content.t *_message.message_type 1文本 2图片 3语音 4视频 5表情 6GIF 7文件 8位置 9系统
MessageStatus message/entity.Status status *_message.status 1正常 2已撤回 3审核隐藏
RelationKind friend/entity.RelationKind relation not a column (viewer-centric) 0无 1好友 2已拉黑
— friend/entity.EdgeStatus not on the wire friend_relations.status 0非好友 1好友
FriendRequestStatus friend/entity.RequestStatus apply status friend_requests.status 1待处理 2已同意 3已拒绝
FriendSource friend/entity.Source source request + edge source 0未知 1二维码 2邮箱 3spark_id
ReportReason friend/entity.ReportReason reason friend_reports.reason 1垃圾 2骚扰 3不实身份 4其他
GroupStatus group/entity.Status (internal) groups.status 1正常 2已解散
GroupRole group/entity.Role role group_members.role 1群主 2管理员 3成员。PATCH 只能 2 或 3
MailboxEventType sync/entity.EventType mailbox event_type mailbox_events.event_type 1漏推 2撤回 3删除
TcpCommand tcp/protocol.Cmd* Frame.Command — 1–15 see protocol
EnvelopeCode pkg/errs codes envelope code — "0" + 40001…

POST /v1/friends/requests returns FriendApplyData.status as 1 or 2, not "pending" / "accepted".

Compare MessageKind with conversation.entity.Type, never with message.entity.Type.

String catalog (keep as strings)

Do not convert these to integers.

OpenAPI schema Go JSON Values
DeviceType login DTO string + oneof device_type / platform ios android harmony windows macos web
Language i18n.Lang language, Accept-Language, X-Language zh-CN en zh-TW (default zh-CN)
UploadPurpose upload/entity.Purpose* consts + LookupSpec purpose avatar group_avatar chat_image chat_voice chat_video chat_gif chat_file
SystemKind friend/entity.SystemKind* / group/entity.SystemKind* content.system.kind friend.became group.created group.invite group.leave group.kick group.dissolve group.transfer

Health status (ok / not_ready) is process liveness, not a domain enum.

Adding a closed set

  1. Put type X int (or keep a string const set) in the owning module entity. Add Valid().
  2. Use X on entity + JSON DTOs. Convert at the GORM boundary.
  3. Bind request fields with oneof= listing the same values; add i18n errmsg.
  4. Add a named schema in internal/transport/http/docs/openapi.yaml and $ref every field.
  5. Add the set to GET /v1/meta/enums (meta/logic) and i18n labels enum.{set}.{name} (zh-CN / en / zh-TW).
  6. Update this catalog.

Do not: change existing numbers; stringify a numeric set; duplicate enum lists in handlers.