189 lines
6.5 KiB
Go
189 lines
6.5 KiB
Go
package activityapi
|
||
|
||
import (
|
||
"hyapp/services/gateway-service/internal/transport/http/httpkit"
|
||
"net/http"
|
||
"strings"
|
||
|
||
activityv1 "hyapp.local/api/proto/activity/v1"
|
||
"hyapp/services/gateway-service/internal/auth"
|
||
)
|
||
|
||
type taskSectionData struct {
|
||
Section string `json:"section"`
|
||
Items []taskItemData `json:"items"`
|
||
ServerTimeMS int64 `json:"server_time_ms"`
|
||
NextRefreshAtMS int64 `json:"next_refresh_at_ms"`
|
||
}
|
||
|
||
type taskItemData struct {
|
||
TaskID string `json:"task_id"`
|
||
TaskType string `json:"task_type"`
|
||
Category string `json:"category"`
|
||
MetricType string `json:"metric_type"`
|
||
Title string `json:"title"`
|
||
Description string `json:"description"`
|
||
TargetValue int64 `json:"target_value"`
|
||
TargetUnit string `json:"target_unit"`
|
||
ProgressValue int64 `json:"progress_value"`
|
||
RewardCoinAmount int64 `json:"reward_coin_amount"`
|
||
Status string `json:"status"`
|
||
Claimable bool `json:"claimable"`
|
||
TaskDay string `json:"task_day"`
|
||
ServerTimeMS int64 `json:"server_time_ms"`
|
||
NextRefreshAtMS int64 `json:"next_refresh_at_ms"`
|
||
SortOrder int32 `json:"sort_order"`
|
||
Version int64 `json:"version"`
|
||
}
|
||
|
||
type taskClaimData struct {
|
||
ClaimID string `json:"claim_id"`
|
||
TaskID string `json:"task_id"`
|
||
TaskType string `json:"task_type"`
|
||
TaskDay string `json:"task_day"`
|
||
RewardCoinAmount int64 `json:"reward_coin_amount"`
|
||
Status string `json:"status"`
|
||
WalletTransactionID string `json:"wallet_transaction_id"`
|
||
GrantedAtMS int64 `json:"granted_at_ms"`
|
||
Claimed bool `json:"claimed"`
|
||
}
|
||
|
||
type taskClaimRequestBody struct {
|
||
TaskID string `json:"task_id"`
|
||
TaskIDCamel string `json:"taskId"`
|
||
TaskType string `json:"task_type"`
|
||
TaskTypeCamel string `json:"taskType"`
|
||
TaskDay string `json:"task_day"`
|
||
TaskDayCamel string `json:"taskDay"`
|
||
CommandID string `json:"command_id"`
|
||
CommandIDCamel string `json:"commandId"`
|
||
}
|
||
|
||
func (b taskClaimRequestBody) normalizedTaskID() string {
|
||
if value := strings.TrimSpace(b.TaskID); value != "" {
|
||
return value
|
||
}
|
||
return strings.TrimSpace(b.TaskIDCamel)
|
||
}
|
||
|
||
func (b taskClaimRequestBody) normalizedTaskType() string {
|
||
if value := strings.TrimSpace(b.TaskType); value != "" {
|
||
return value
|
||
}
|
||
return strings.TrimSpace(b.TaskTypeCamel)
|
||
}
|
||
|
||
func (b taskClaimRequestBody) normalizedTaskDay() string {
|
||
if value := strings.TrimSpace(b.TaskDay); value != "" {
|
||
return value
|
||
}
|
||
return strings.TrimSpace(b.TaskDayCamel)
|
||
}
|
||
|
||
func (b taskClaimRequestBody) normalizedCommandID() string {
|
||
if value := strings.TrimSpace(b.CommandID); value != "" {
|
||
return value
|
||
}
|
||
return strings.TrimSpace(b.CommandIDCamel)
|
||
}
|
||
|
||
// listTaskTabs 返回当前用户任务页;进度由 activity-service 事实表决定。
|
||
func (h *Handler) listTaskTabs(writer http.ResponseWriter, request *http.Request) {
|
||
if h.taskClient == nil {
|
||
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
|
||
return
|
||
}
|
||
resp, err := h.taskClient.ListUserTasks(request.Context(), &activityv1.ListUserTasksRequest{
|
||
Meta: httpkit.ActivityMeta(request),
|
||
UserId: auth.UserIDFromContext(request.Context()),
|
||
})
|
||
if err != nil {
|
||
httpkit.WriteRPCError(writer, request, err)
|
||
return
|
||
}
|
||
sections := make([]taskSectionData, 0, len(resp.GetSections()))
|
||
for _, section := range resp.GetSections() {
|
||
items := make([]taskItemData, 0, len(section.GetItems()))
|
||
for _, item := range section.GetItems() {
|
||
items = append(items, taskItemFromProto(item))
|
||
}
|
||
sections = append(sections, taskSectionData{
|
||
Section: section.GetSection(),
|
||
Items: items,
|
||
ServerTimeMS: section.GetServerTimeMs(),
|
||
NextRefreshAtMS: section.GetNextRefreshAtMs(),
|
||
})
|
||
}
|
||
httpkit.WriteOK(writer, request, map[string]any{
|
||
"sections": sections,
|
||
"server_time_ms": resp.GetServerTimeMs(),
|
||
"next_refresh_at_ms": resp.GetNextRefreshAtMs(),
|
||
})
|
||
}
|
||
|
||
// claimTaskReward 只透传当前用户身份和幂等命令,完成校验及发奖在 activity-service 内完成。
|
||
func (h *Handler) claimTaskReward(writer http.ResponseWriter, request *http.Request) {
|
||
if h.taskClient == nil {
|
||
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
|
||
return
|
||
}
|
||
var body taskClaimRequestBody
|
||
if !httpkit.Decode(writer, request, &body) {
|
||
return
|
||
}
|
||
taskID := body.normalizedTaskID()
|
||
taskType := body.normalizedTaskType()
|
||
taskDay := body.normalizedTaskDay()
|
||
// command_id 是客户端动作的幂等锚点,gateway 只裁剪空白,不替客户端补值。
|
||
commandID := strings.TrimSpace(body.normalizedCommandID())
|
||
if commandID == "" || taskID == "" || taskType == "" || taskDay == "" {
|
||
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
|
||
return
|
||
}
|
||
resp, err := h.taskClient.ClaimTaskReward(request.Context(), &activityv1.ClaimTaskRewardRequest{
|
||
Meta: httpkit.ActivityMeta(request),
|
||
UserId: auth.UserIDFromContext(request.Context()),
|
||
TaskId: taskID,
|
||
TaskType: taskType,
|
||
TaskDay: taskDay,
|
||
CommandId: commandID,
|
||
})
|
||
if err != nil {
|
||
httpkit.WriteRPCError(writer, request, err)
|
||
return
|
||
}
|
||
httpkit.WriteOK(writer, request, taskClaimData{
|
||
ClaimID: resp.GetClaimId(),
|
||
TaskID: resp.GetTaskId(),
|
||
TaskType: resp.GetTaskType(),
|
||
TaskDay: resp.GetTaskDay(),
|
||
RewardCoinAmount: resp.GetRewardCoinAmount(),
|
||
Status: resp.GetStatus(),
|
||
WalletTransactionID: resp.GetWalletTransactionId(),
|
||
GrantedAtMS: resp.GetGrantedAtMs(),
|
||
Claimed: resp.GetClaimed(),
|
||
})
|
||
}
|
||
|
||
func taskItemFromProto(item *activityv1.TaskItem) taskItemData {
|
||
return taskItemData{
|
||
TaskID: item.GetTaskId(),
|
||
TaskType: item.GetTaskType(),
|
||
Category: item.GetCategory(),
|
||
MetricType: item.GetMetricType(),
|
||
Title: item.GetTitle(),
|
||
Description: item.GetDescription(),
|
||
TargetValue: item.GetTargetValue(),
|
||
TargetUnit: item.GetTargetUnit(),
|
||
ProgressValue: item.GetProgressValue(),
|
||
RewardCoinAmount: item.GetRewardCoinAmount(),
|
||
Status: item.GetStatus(),
|
||
Claimable: item.GetClaimable(),
|
||
TaskDay: item.GetTaskDay(),
|
||
ServerTimeMS: item.GetServerTimeMs(),
|
||
NextRefreshAtMS: item.GetNextRefreshAtMs(),
|
||
SortOrder: item.GetSortOrder(),
|
||
Version: item.GetVersion(),
|
||
}
|
||
}
|