286 lines
8.0 KiB
Go

package integration
import (
"bytes"
"chatapp-cron/internal/config"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"strings"
)
type Client struct {
cfg config.Config
httpClient *http.Client
}
type SendActivityRewardRequest struct {
TrackID int64 `json:"trackId"`
Origin string `json:"origin"`
SysOrigin string `json:"sysOrigin"`
SourceGroupID int64 `json:"sourceGroupId"`
AcceptUserID int64 `json:"acceptUserId"`
}
type UserProfile struct {
ID Int64Value `json:"id"`
UserAvatar string `json:"userAvatar"`
UserNickname string `json:"userNickname"`
}
type RoomContributionActivityCount struct {
ID string `json:"id"`
RoomID Int64Value `json:"roomId"`
ContributionValue AmountValue `json:"contributionValue"`
DateNumber int `json:"dateNumber"`
Received bool `json:"received"`
RewardCoinsSent bool `json:"rewardCoinsSent"`
}
type RoomProfile struct {
ID Int64Value `json:"id"`
RoomAccount string `json:"roomAccount"`
UserID Int64Value `json:"userId"`
RoomCover string `json:"roomCover"`
RoomName string `json:"roomName"`
SysOrigin string `json:"sysOrigin"`
CountryCode string `json:"countryCode"`
CountryName string `json:"countryName"`
}
type GoldReceiptCommand struct {
ReceiptType string `json:"receiptType"`
UserID int64 `json:"userId"`
SysOrigin string `json:"sysOrigin"`
EventID string `json:"eventId"`
Origin string `json:"origin,omitempty"`
Remark string `json:"remark,omitempty"`
Amount PennyAmountPayload `json:"amount"`
CloseDelayAsset bool `json:"closeDelayAsset"`
OpUserType string `json:"opUserType"`
CustomizeOrigin string `json:"customizeOrigin,omitempty"`
CustomizeOriginDesc string `json:"customizeOriginDesc,omitempty"`
}
type PennyAmountPayload struct {
PennyAmount int64 `json:"pennyAmount,string"`
DollarAmount int64 `json:"dollarAmount"`
}
type resultResponse[T any] struct {
Code int `json:"code"`
Message string `json:"message"`
Body T `json:"body"`
Success *bool `json:"success"`
}
type Int64Value int64
func (v *Int64Value) UnmarshalJSON(data []byte) error {
raw := strings.TrimSpace(string(data))
if raw == "" || raw == "null" {
*v = 0
return nil
}
if strings.HasPrefix(raw, "\"") && strings.HasSuffix(raw, "\"") {
raw = strings.Trim(raw, "\"")
}
parsed, err := strconv.ParseInt(raw, 10, 64)
if err != nil {
return err
}
*v = Int64Value(parsed)
return nil
}
type AmountValue int64
func (v *AmountValue) UnmarshalJSON(data []byte) error {
raw := strings.TrimSpace(string(data))
if raw == "" || raw == "null" {
*v = 0
return nil
}
if strings.HasPrefix(raw, "\"") && strings.HasSuffix(raw, "\"") {
raw = strings.Trim(raw, "\"")
}
if raw == "" {
*v = 0
return nil
}
floatRaw := raw
if dot := strings.Index(raw, "."); dot >= 0 {
raw = raw[:dot]
}
if parsed, err := strconv.ParseInt(raw, 10, 64); err == nil {
*v = AmountValue(parsed)
return nil
}
parsed, err := strconv.ParseFloat(floatRaw, 64)
if err != nil {
return err
}
*v = AmountValue(int64(parsed))
return nil
}
func New(cfg config.Config) *Client {
return &Client{
cfg: cfg,
httpClient: &http.Client{
Timeout: cfg.Timeout,
},
}
}
func (c *Client) SendActivityReward(ctx context.Context, req SendActivityRewardRequest) error {
return c.postJSON(ctx, c.cfg.JavaOtherBaseURL+"/props-activity-cnf/client/sendActivityReward", req, nil)
}
func (c *Client) ListLastWeekRoomContribution(ctx context.Context) ([]RoomContributionActivityCount, error) {
endpoint := c.cfg.JavaOtherBaseURL + "/room-contribution-activity-count/client/listLastWeek"
var resp resultResponse[[]RoomContributionActivityCount]
if err := c.getJSON(ctx, endpoint, &resp); err != nil {
return nil, err
}
return resp.Body, nil
}
func (c *Client) MarkRoomTurnoverRewardCoinsSent(ctx context.Context, id string) (bool, error) {
id = strings.TrimSpace(id)
if id == "" {
return false, fmt.Errorf("empty room contribution id")
}
endpoint := c.cfg.JavaOtherBaseURL + "/room-contribution-activity-count/client/rewardCoinsSent?id=" + url.QueryEscape(id)
var resp resultResponse[bool]
if err := c.postEmpty(ctx, endpoint, &resp); err != nil {
return false, err
}
return resp.Body, nil
}
func (c *Client) MapRoomProfiles(ctx context.Context, roomIDs []int64) (map[int64]RoomProfile, error) {
if len(roomIDs) == 0 {
return map[int64]RoomProfile{}, nil
}
endpoint := c.cfg.JavaOtherBaseURL + "/room-manager/client/mapProfileByRoomIds"
var resp resultResponse[map[string]RoomProfile]
if err := c.postJSON(ctx, endpoint, roomIDs, &resp); err != nil {
return nil, err
}
result := make(map[int64]RoomProfile, len(resp.Body))
for key, value := range resp.Body {
roomID, err := strconv.ParseInt(strings.TrimSpace(key), 10, 64)
if err != nil {
roomID = int64(value.ID)
}
if roomID <= 0 {
continue
}
result[roomID] = value
}
return result, nil
}
func (c *Client) ChangeGoldBalance(ctx context.Context, cmd GoldReceiptCommand) error {
var resp resultResponse[json.RawMessage]
if err := c.postJSON(ctx, c.cfg.JavaWalletBaseURL+"/wallet/gold/client/balance/change", cmd, &resp); err != nil {
return err
}
return ensureSuccessfulResult(resp.Code, resp.Message, resp.Success)
}
func NewPennyAmountPayloadFromDollar(amount int64) PennyAmountPayload {
return PennyAmountPayload{
PennyAmount: amount * 100,
DollarAmount: amount,
}
}
func (c *Client) GetUserProfile(ctx context.Context, userID int64) (UserProfile, error) {
endpoint := c.cfg.JavaOtherBaseURL + "/user-profile/client/getByUserId?userId=" + url.QueryEscape(strconv.FormatInt(userID, 10))
var resp resultResponse[UserProfile]
if err := c.getJSON(ctx, endpoint, &resp); err != nil {
return UserProfile{}, err
}
if int64(resp.Body.ID) == 0 {
return UserProfile{}, fmt.Errorf("empty user profile response")
}
return resp.Body, nil
}
func (c *Client) getJSON(ctx context.Context, endpoint string, out any) error {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
if err != nil {
return err
}
resp, err := c.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return decodeResponse(resp, out)
}
func (c *Client) postJSON(ctx context.Context, endpoint string, payload any, out any) error {
data, err := json.Marshal(payload)
if err != nil {
return err
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(data))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
resp, err := c.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return decodeResponse(resp, out)
}
func (c *Client) postEmpty(ctx context.Context, endpoint string, out any) error {
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, nil)
if err != nil {
return err
}
resp, err := c.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return decodeResponse(resp, out)
}
func decodeResponse(resp *http.Response, out any) error {
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode >= http.StatusBadRequest {
return fmt.Errorf("java api failed: status=%d body=%s", resp.StatusCode, strings.TrimSpace(string(body)))
}
if out == nil || len(body) == 0 {
return nil
}
if err := json.Unmarshal(body, out); err != nil {
return fmt.Errorf("decode response failed: %w body=%s", err, strings.TrimSpace(string(body)))
}
return nil
}
func ensureSuccessfulResult(code int, message string, success *bool) error {
if success != nil && !*success {
return fmt.Errorf("java api returned unsuccessful result: code=%d message=%s", code, strings.TrimSpace(message))
}
if code != 0 && code != 200 {
return fmt.Errorf("java api returned error result: code=%d message=%s", code, strings.TrimSpace(message))
}
return nil
}