b78ce42922
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 16s
CI / ui (pull_request) Successful in 54s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m19s
Each feedback submission now carries the client app version (__APP_VERSION__), snapshotted like the interface language: FlatBuffers FeedbackSubmitRequest gains a version field → gateway transcode → backend, persisted in a new nullable feedback_messages.app_version column (migration 00003, additive so an image rollback stays DB-safe). The operator console detail shows the app version and renders the Filed time in UTC plus the sender's time zone (fmtTimeIn). Touches: fbs schema + regenerated Go/TS codegen, codec + transport (the client attaches its build), gateway transcode + backendclient, feedback store/service, admin view + template, docs (ARCHITECTURE §15, FUNCTIONAL + _ru). Verified: feedback integration tests (migration + version round-trip), codec round-trip, check/unit/build green.
58 lines
2.0 KiB
Go
58 lines
2.0 KiB
Go
package backendclient
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"net/http"
|
|
)
|
|
|
|
// FeedbackReplyResp is the operator reply shown back to the player.
|
|
type FeedbackReplyResp struct {
|
|
Body string `json:"body"`
|
|
RepliedAtUnix int64 `json:"replied_at_unix"`
|
|
}
|
|
|
|
// FeedbackStateResp is the player's feedback screen state. Reply is nil when there
|
|
// is none to show.
|
|
type FeedbackStateResp struct {
|
|
CanSend bool `json:"can_send"`
|
|
BlockedReason string `json:"blocked_reason"`
|
|
Reply *FeedbackReplyResp `json:"reply,omitempty"`
|
|
}
|
|
|
|
// FeedbackUnreadResp reports whether the player has an undelivered operator reply.
|
|
type FeedbackUnreadResp struct {
|
|
ReplyUnread bool `json:"reply_unread"`
|
|
}
|
|
|
|
// FeedbackSubmit posts a feedback message. The attachment bytes are base64-encoded
|
|
// into the JSON body for the internal hop; clientIP rides X-Forwarded-For.
|
|
func (c *Client) FeedbackSubmit(ctx context.Context, userID, body string, attachment []byte, attachmentName, channel, version, clientIP string) error {
|
|
payload := map[string]string{
|
|
"body": body,
|
|
"attachment": "",
|
|
"attachment_name": attachmentName,
|
|
"channel": channel,
|
|
"version": version,
|
|
}
|
|
if len(attachment) > 0 {
|
|
payload["attachment"] = base64.StdEncoding.EncodeToString(attachment)
|
|
}
|
|
return c.do(ctx, http.MethodPost, "/api/v1/user/feedback", userID, clientIP, payload, nil)
|
|
}
|
|
|
|
// FeedbackGet returns the player's feedback screen state, marking any pending reply
|
|
// delivered (clearing the badge).
|
|
func (c *Client) FeedbackGet(ctx context.Context, userID string) (FeedbackStateResp, error) {
|
|
var out FeedbackStateResp
|
|
err := c.do(ctx, http.MethodGet, "/api/v1/user/feedback", userID, "", nil, &out)
|
|
return out, err
|
|
}
|
|
|
|
// FeedbackUnread reports whether the player has an undelivered operator reply.
|
|
func (c *Client) FeedbackUnread(ctx context.Context, userID string) (FeedbackUnreadResp, error) {
|
|
var out FeedbackUnreadResp
|
|
err := c.do(ctx, http.MethodGet, "/api/v1/user/feedback/unread", userID, "", nil, &out)
|
|
return out, err
|
|
}
|