747 lines
23 KiB
Go
747 lines
23 KiB
Go
package resource
|
||
|
||
import (
|
||
"database/sql"
|
||
"fmt"
|
||
"sort"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"hyapp-admin-server/internal/appctx"
|
||
"hyapp-admin-server/internal/integration/walletclient"
|
||
"hyapp-admin-server/internal/middleware"
|
||
"hyapp-admin-server/internal/modules/shared"
|
||
"hyapp-admin-server/internal/repository"
|
||
"hyapp-admin-server/internal/response"
|
||
walletv1 "hyapp.local/api/proto/wallet/v1"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
type Handler struct {
|
||
wallet walletclient.Client
|
||
store *repository.Store
|
||
userDB *sql.DB
|
||
audit shared.OperationLogger
|
||
}
|
||
|
||
func New(wallet walletclient.Client, store *repository.Store, userDB *sql.DB, audit shared.OperationLogger) *Handler {
|
||
return &Handler{wallet: wallet, store: store, userDB: userDB, audit: audit}
|
||
}
|
||
|
||
func (h *Handler) ListResources(c *gin.Context) {
|
||
options := shared.ListOptions(c)
|
||
resp, err := h.wallet.ListResources(c.Request.Context(), &walletv1.ListResourcesRequest{
|
||
RequestId: middleware.CurrentRequestID(c),
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
ResourceType: strings.TrimSpace(c.Query("resource_type")),
|
||
Status: options.Status,
|
||
Keyword: options.Keyword,
|
||
Page: int32(options.Page),
|
||
PageSize: int32(options.PageSize),
|
||
})
|
||
if err != nil {
|
||
response.ServerError(c, "获取资源列表失败")
|
||
return
|
||
}
|
||
items := make([]resourceDTO, 0, len(resp.GetResources()))
|
||
for _, item := range resp.GetResources() {
|
||
items = append(items, resourceFromProto(item))
|
||
}
|
||
response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()})
|
||
}
|
||
|
||
func (h *Handler) GetResource(c *gin.Context) {
|
||
resourceID, ok := parseID(c, "resource_id")
|
||
if !ok {
|
||
return
|
||
}
|
||
resp, err := h.wallet.GetResource(c.Request.Context(), &walletv1.GetResourceRequest{
|
||
RequestId: middleware.CurrentRequestID(c),
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
ResourceId: resourceID,
|
||
})
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
response.OK(c, resourceFromProto(resp.GetResource()))
|
||
}
|
||
|
||
func (h *Handler) CreateResource(c *gin.Context) {
|
||
var req resourceRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "资源参数不正确")
|
||
return
|
||
}
|
||
if err := validateResourcePricing(req); err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
if err := validateResourceBadgeForm(req); err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
resp, err := h.wallet.CreateResource(c.Request.Context(), req.createProto(c))
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
resource := resourceFromProto(resp.GetResource())
|
||
h.auditLog(c, "create-resource", "resources", fmt.Sprintf("%d", resource.ResourceID), "success", resource.ResourceCode)
|
||
response.Created(c, resource)
|
||
}
|
||
|
||
func (h *Handler) ListEmojiPacks(c *gin.Context) {
|
||
options := shared.ListOptions(c)
|
||
resp, err := h.wallet.ListResources(c.Request.Context(), &walletv1.ListResourcesRequest{
|
||
RequestId: middleware.CurrentRequestID(c),
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
ResourceType: resourceTypeEmojiPack,
|
||
Status: options.Status,
|
||
Keyword: options.Keyword,
|
||
Page: int32(options.Page),
|
||
PageSize: int32(options.PageSize),
|
||
})
|
||
if err != nil {
|
||
response.ServerError(c, "获取表情包列表失败")
|
||
return
|
||
}
|
||
items := make([]emojiPackDTO, 0, len(resp.GetResources()))
|
||
for _, item := range resp.GetResources() {
|
||
items = append(items, emojiPackFromProto(item))
|
||
}
|
||
response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()})
|
||
}
|
||
|
||
func (h *Handler) ListEmojiPackCategories(c *gin.Context) {
|
||
const pageSize int32 = 100
|
||
categories := map[string]struct{}{}
|
||
for page := int32(1); ; page++ {
|
||
resp, err := h.wallet.ListResources(c.Request.Context(), &walletv1.ListResourcesRequest{
|
||
RequestId: middleware.CurrentRequestID(c),
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
ResourceType: resourceTypeEmojiPack,
|
||
Page: page,
|
||
PageSize: pageSize,
|
||
})
|
||
if err != nil {
|
||
response.ServerError(c, "获取表情包分类失败")
|
||
return
|
||
}
|
||
for _, item := range resp.GetResources() {
|
||
category := emojiPackMetadataFromJSON(item.GetMetadataJson()).Category
|
||
if category != "" {
|
||
categories[category] = struct{}{}
|
||
}
|
||
}
|
||
if len(resp.GetResources()) == 0 || int64(page)*int64(pageSize) >= resp.GetTotal() {
|
||
break
|
||
}
|
||
}
|
||
items := make([]string, 0, len(categories))
|
||
for category := range categories {
|
||
items = append(items, category)
|
||
}
|
||
sort.Strings(items)
|
||
response.OK(c, items)
|
||
}
|
||
|
||
func (h *Handler) CreateEmojiPack(c *gin.Context) {
|
||
var req emojiPackRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "表情包参数不正确")
|
||
return
|
||
}
|
||
protoReq, err := req.createProto(c)
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
resp, err := h.wallet.CreateResource(c.Request.Context(), protoReq)
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
item := emojiPackFromProto(resp.GetResource())
|
||
h.auditLog(c, "create-emoji-pack", "resources", fmt.Sprintf("%d", item.ResourceID), "success", item.ResourceCode)
|
||
response.Created(c, item)
|
||
}
|
||
|
||
func (h *Handler) UpdateResource(c *gin.Context) {
|
||
resourceID, ok := parseID(c, "resource_id")
|
||
if !ok {
|
||
return
|
||
}
|
||
var req resourceRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "资源参数不正确")
|
||
return
|
||
}
|
||
if err := validateResourcePricing(req); err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
if err := validateResourceBadgeForm(req); err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
protoReq := req.updateProto(c, resourceID)
|
||
resp, err := h.wallet.UpdateResource(c.Request.Context(), protoReq)
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
resource := resourceFromProto(resp.GetResource())
|
||
h.auditLog(c, "update-resource", "resources", fmt.Sprintf("%d", resource.ResourceID), "success", resource.ResourceCode)
|
||
response.OK(c, resource)
|
||
}
|
||
|
||
func (h *Handler) EnableResource(c *gin.Context) {
|
||
h.setResourceStatus(c, "active")
|
||
}
|
||
|
||
func (h *Handler) DisableResource(c *gin.Context) {
|
||
h.setResourceStatus(c, "disabled")
|
||
}
|
||
|
||
func (h *Handler) ListResourceGroups(c *gin.Context) {
|
||
options := shared.ListOptions(c)
|
||
resp, err := h.wallet.ListResourceGroups(c.Request.Context(), &walletv1.ListResourceGroupsRequest{
|
||
RequestId: middleware.CurrentRequestID(c),
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
Status: options.Status,
|
||
Keyword: options.Keyword,
|
||
Page: int32(options.Page),
|
||
PageSize: int32(options.PageSize),
|
||
})
|
||
if err != nil {
|
||
response.ServerError(c, "获取资源组列表失败")
|
||
return
|
||
}
|
||
items := make([]resourceGroupDTO, 0, len(resp.GetGroups()))
|
||
for _, item := range resp.GetGroups() {
|
||
items = append(items, resourceGroupFromProto(item))
|
||
}
|
||
response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()})
|
||
}
|
||
|
||
func (h *Handler) GetResourceGroup(c *gin.Context) {
|
||
groupID, ok := parseID(c, "group_id")
|
||
if !ok {
|
||
return
|
||
}
|
||
resp, err := h.wallet.GetResourceGroup(c.Request.Context(), &walletv1.GetResourceGroupRequest{
|
||
RequestId: middleware.CurrentRequestID(c),
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
GroupId: groupID,
|
||
})
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
response.OK(c, resourceGroupFromProto(resp.GetGroup()))
|
||
}
|
||
|
||
func (h *Handler) CreateResourceGroup(c *gin.Context) {
|
||
var req resourceGroupRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "资源组参数不正确")
|
||
return
|
||
}
|
||
resp, err := h.wallet.CreateResourceGroup(c.Request.Context(), req.createProto(c))
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
group := resourceGroupFromProto(resp.GetGroup())
|
||
h.auditLog(c, "create-resource-group", "resource_groups", fmt.Sprintf("%d", group.GroupID), "success", group.GroupCode)
|
||
response.Created(c, group)
|
||
}
|
||
|
||
func (h *Handler) UpdateResourceGroup(c *gin.Context) {
|
||
groupID, ok := parseID(c, "group_id")
|
||
if !ok {
|
||
return
|
||
}
|
||
var req resourceGroupRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "资源组参数不正确")
|
||
return
|
||
}
|
||
resp, err := h.wallet.UpdateResourceGroup(c.Request.Context(), req.updateProto(c, groupID))
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
group := resourceGroupFromProto(resp.GetGroup())
|
||
h.auditLog(c, "update-resource-group", "resource_groups", fmt.Sprintf("%d", group.GroupID), "success", group.GroupCode)
|
||
response.OK(c, group)
|
||
}
|
||
|
||
func (h *Handler) EnableResourceGroup(c *gin.Context) {
|
||
h.setResourceGroupStatus(c, "active")
|
||
}
|
||
|
||
func (h *Handler) DisableResourceGroup(c *gin.Context) {
|
||
h.setResourceGroupStatus(c, "disabled")
|
||
}
|
||
|
||
func (h *Handler) ListGifts(c *gin.Context) {
|
||
options := shared.ListOptions(c)
|
||
regionID, filterRegion, ok := optionalInt64QueryWithPresence(c, "regionId", "region_id")
|
||
if !ok {
|
||
return
|
||
}
|
||
resp, err := h.wallet.ListGiftConfigs(c.Request.Context(), &walletv1.ListGiftConfigsRequest{
|
||
RequestId: middleware.CurrentRequestID(c),
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
Status: options.Status,
|
||
Keyword: options.Keyword,
|
||
Page: int32(options.Page),
|
||
PageSize: int32(options.PageSize),
|
||
RegionId: regionID,
|
||
FilterRegion: filterRegion,
|
||
})
|
||
if err != nil {
|
||
response.ServerError(c, "获取礼物列表失败")
|
||
return
|
||
}
|
||
items := make([]giftDTO, 0, len(resp.GetGifts()))
|
||
for _, item := range resp.GetGifts() {
|
||
items = append(items, giftFromProto(item))
|
||
}
|
||
response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()})
|
||
}
|
||
|
||
func (h *Handler) ListGiftTypes(c *gin.Context) {
|
||
resp, err := h.wallet.ListGiftTypeConfigs(c.Request.Context(), &walletv1.ListGiftTypeConfigsRequest{
|
||
RequestId: middleware.CurrentRequestID(c),
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
})
|
||
if err != nil {
|
||
response.ServerError(c, "获取礼物类型失败")
|
||
return
|
||
}
|
||
items := make([]giftTypeDTO, 0, len(resp.GetGiftTypes()))
|
||
for _, item := range resp.GetGiftTypes() {
|
||
items = append(items, giftTypeFromProto(item))
|
||
}
|
||
response.OK(c, items)
|
||
}
|
||
|
||
func (h *Handler) UpdateGiftType(c *gin.Context) {
|
||
typeCode := strings.TrimSpace(c.Param("type_code"))
|
||
if typeCode == "" {
|
||
response.BadRequest(c, "类型编码不正确")
|
||
return
|
||
}
|
||
var req giftTypeRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "礼物类型参数不正确")
|
||
return
|
||
}
|
||
resp, err := h.wallet.UpsertGiftTypeConfig(c.Request.Context(), req.upsertProto(c, typeCode))
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
item := giftTypeFromProto(resp.GetGiftType())
|
||
h.auditLog(c, "update-gift-type", "gift_type_configs", item.TabKey, "success", item.TabName)
|
||
response.OK(c, item)
|
||
}
|
||
|
||
func (h *Handler) UpdateGiftTypes(c *gin.Context) {
|
||
var req giftTypesRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "礼物类型参数不正确")
|
||
return
|
||
}
|
||
if len(req.Items) == 0 {
|
||
response.BadRequest(c, "礼物类型不能为空")
|
||
return
|
||
}
|
||
items := make([]giftTypeDTO, 0, len(req.Items))
|
||
tabKeys := make([]string, 0, len(req.Items))
|
||
for _, requestItem := range req.Items {
|
||
tabKey := strings.TrimSpace(requestItem.TabKey)
|
||
if tabKey == "" {
|
||
response.BadRequest(c, "类型编码不正确")
|
||
return
|
||
}
|
||
resp, err := h.wallet.UpsertGiftTypeConfig(c.Request.Context(), requestItem.upsertProto(c))
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
item := giftTypeFromProto(resp.GetGiftType())
|
||
items = append(items, item)
|
||
tabKeys = append(tabKeys, item.TabKey)
|
||
}
|
||
h.auditLog(c, "update-gift-types", "gift_type_configs", "batch", "success", strings.Join(tabKeys, ","))
|
||
response.OK(c, items)
|
||
}
|
||
|
||
func (h *Handler) CreateGift(c *gin.Context) {
|
||
var req giftRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "礼物参数不正确")
|
||
return
|
||
}
|
||
resp, err := h.wallet.CreateGiftConfig(c.Request.Context(), req.createProto(c))
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
gift := giftFromProto(resp.GetGift())
|
||
h.auditLog(c, "create-gift", "gift_configs", gift.GiftID, "success", gift.Name)
|
||
response.Created(c, gift)
|
||
}
|
||
|
||
func (h *Handler) UpdateGift(c *gin.Context) {
|
||
giftID := strings.TrimSpace(c.Param("gift_id"))
|
||
if giftID == "" {
|
||
response.BadRequest(c, "ID 参数不正确")
|
||
return
|
||
}
|
||
var req giftRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "礼物参数不正确")
|
||
return
|
||
}
|
||
resp, err := h.wallet.UpdateGiftConfig(c.Request.Context(), req.updateProto(c, giftID))
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
gift := giftFromProto(resp.GetGift())
|
||
h.auditLog(c, "update-gift", "gift_configs", gift.GiftID, "success", gift.Name)
|
||
response.OK(c, gift)
|
||
}
|
||
|
||
func (h *Handler) EnableGift(c *gin.Context) {
|
||
h.setGiftStatus(c, "active")
|
||
}
|
||
|
||
func (h *Handler) DisableGift(c *gin.Context) {
|
||
h.setGiftStatus(c, "disabled")
|
||
}
|
||
|
||
func (h *Handler) GrantResource(c *gin.Context) {
|
||
var req grantResourceRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "资源赠送参数不正确")
|
||
return
|
||
}
|
||
targetUserID, err := parseGrantTargetUserID(req.TargetUserID)
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
resp, err := h.wallet.GrantResource(c.Request.Context(), &walletv1.GrantResourceRequest{
|
||
CommandId: strings.TrimSpace(req.CommandID),
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
TargetUserId: targetUserID,
|
||
ResourceId: req.ResourceID,
|
||
Quantity: req.Quantity,
|
||
DurationMs: req.DurationMS,
|
||
Reason: strings.TrimSpace(req.Reason),
|
||
OperatorUserId: actorID(c),
|
||
GrantSource: "admin",
|
||
})
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
grant := grantFromProto(resp.GetGrant())
|
||
h.auditLog(c, "grant-resource", "resource_grants", grant.GrantID, "success", fmt.Sprintf("target_user_id=%d", grant.TargetUserID))
|
||
response.Created(c, grant)
|
||
}
|
||
|
||
func (h *Handler) GrantResourceGroup(c *gin.Context) {
|
||
var req grantGroupRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "资源组赠送参数不正确")
|
||
return
|
||
}
|
||
targetUserID, err := parseGrantTargetUserID(req.TargetUserID)
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
resp, err := h.wallet.GrantResourceGroup(c.Request.Context(), &walletv1.GrantResourceGroupRequest{
|
||
CommandId: strings.TrimSpace(req.CommandID),
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
TargetUserId: targetUserID,
|
||
GroupId: req.GroupID,
|
||
Reason: strings.TrimSpace(req.Reason),
|
||
OperatorUserId: actorID(c),
|
||
GrantSource: "admin",
|
||
})
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
grant := grantFromProto(resp.GetGrant())
|
||
h.auditLog(c, "grant-resource-group", "resource_grants", grant.GrantID, "success", fmt.Sprintf("target_user_id=%d", grant.TargetUserID))
|
||
response.Created(c, grant)
|
||
}
|
||
|
||
func (h *Handler) LookupResourceGrantTarget(c *gin.Context) {
|
||
keyword := strings.TrimSpace(firstQuery(c, "user_id", "userId", "keyword"))
|
||
if keyword == "" {
|
||
response.BadRequest(c, "请输入用户 ID 或短号")
|
||
return
|
||
}
|
||
if h == nil || h.userDB == nil {
|
||
response.ServerError(c, "用户数据库未配置")
|
||
return
|
||
}
|
||
// 资源赠送页允许输入短号,但 wallet 只认内部 user_id;这里在 admin 侧先解析,
|
||
// 避免前端把短号直接发给 wallet,也避免 JS number 对长 user_id 造成精度损失。
|
||
row := h.userDB.QueryRowContext(c.Request.Context(), `
|
||
SELECT user_id, current_display_user_id, COALESCE(username, ''), COALESCE(avatar, '')
|
||
FROM users
|
||
WHERE app_code = ?
|
||
AND (CAST(user_id AS CHAR) = ? OR current_display_user_id = ?)
|
||
ORDER BY
|
||
CASE
|
||
WHEN CAST(user_id AS CHAR) = ? THEN 0
|
||
WHEN current_display_user_id = ? THEN 1
|
||
ELSE 2
|
||
END,
|
||
user_id DESC
|
||
LIMIT 1`,
|
||
appctx.FromContext(c.Request.Context()), keyword, keyword, keyword, keyword,
|
||
)
|
||
user := grantUserDTO{}
|
||
if err := row.Scan(&user.UserID, &user.DisplayUserID, &user.Username, &user.Avatar); err != nil {
|
||
if err == sql.ErrNoRows {
|
||
response.BadRequest(c, "用户不存在")
|
||
return
|
||
}
|
||
response.ServerError(c, "查询用户失败")
|
||
return
|
||
}
|
||
response.OK(c, user)
|
||
}
|
||
|
||
func (h *Handler) ListResourceGrants(c *gin.Context) {
|
||
options := shared.ListOptions(c)
|
||
targetUserID, ok := optionalInt64Query(c, "target_user_id", "targetUserId")
|
||
if !ok {
|
||
return
|
||
}
|
||
resp, err := h.wallet.ListResourceGrants(c.Request.Context(), &walletv1.ListResourceGrantsRequest{
|
||
RequestId: middleware.CurrentRequestID(c),
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
TargetUserId: targetUserID,
|
||
Status: options.Status,
|
||
Page: int32(options.Page),
|
||
PageSize: int32(options.PageSize),
|
||
})
|
||
if err != nil {
|
||
response.ServerError(c, "获取资源赠送记录失败")
|
||
return
|
||
}
|
||
items := make([]grantDTO, 0, len(resp.GetGrants()))
|
||
for _, item := range resp.GetGrants() {
|
||
items = append(items, grantFromProto(item))
|
||
}
|
||
if err := h.enrichGrantOperators(c.Request.Context(), items); err != nil {
|
||
response.ServerError(c, "获取资源赠送操作人失败")
|
||
return
|
||
}
|
||
if err := h.enrichGrantTargets(c.Request.Context(), items); err != nil {
|
||
response.ServerError(c, "获取资源赠送用户信息失败")
|
||
return
|
||
}
|
||
response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()})
|
||
}
|
||
|
||
func (h *Handler) ListResourceShopItems(c *gin.Context) {
|
||
options := shared.ListOptions(c)
|
||
resp, err := h.wallet.ListResourceShopItems(c.Request.Context(), &walletv1.ListResourceShopItemsRequest{
|
||
RequestId: middleware.CurrentRequestID(c),
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
ResourceType: strings.TrimSpace(c.Query("resource_type")),
|
||
Status: options.Status,
|
||
Keyword: options.Keyword,
|
||
Page: int32(options.Page),
|
||
PageSize: int32(options.PageSize),
|
||
})
|
||
if err != nil {
|
||
response.ServerError(c, "获取道具商店列表失败")
|
||
return
|
||
}
|
||
items := make([]resourceShopItemDTO, 0, len(resp.GetItems()))
|
||
for _, item := range resp.GetItems() {
|
||
items = append(items, resourceShopItemFromProto(item))
|
||
}
|
||
response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()})
|
||
}
|
||
|
||
func (h *Handler) UpsertResourceShopItems(c *gin.Context) {
|
||
var req resourceShopItemsRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "道具商店参数不正确")
|
||
return
|
||
}
|
||
resp, err := h.wallet.UpsertResourceShopItems(c.Request.Context(), req.upsertProto(c))
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
items := make([]resourceShopItemDTO, 0, len(resp.GetItems()))
|
||
resourceIDs := make([]string, 0, len(resp.GetItems()))
|
||
for _, item := range resp.GetItems() {
|
||
dto := resourceShopItemFromProto(item)
|
||
items = append(items, dto)
|
||
resourceIDs = append(resourceIDs, fmt.Sprintf("%d", dto.ResourceID))
|
||
}
|
||
h.auditLog(c, "upsert-resource-shop-items", "resource_shop_items", "batch", "success", strings.Join(resourceIDs, ","))
|
||
response.OK(c, items)
|
||
}
|
||
|
||
func (h *Handler) EnableResourceShopItem(c *gin.Context) {
|
||
h.setResourceShopItemStatus(c, "active")
|
||
}
|
||
|
||
func (h *Handler) DisableResourceShopItem(c *gin.Context) {
|
||
h.setResourceShopItemStatus(c, "disabled")
|
||
}
|
||
|
||
func (h *Handler) setResourceStatus(c *gin.Context, status string) {
|
||
resourceID, ok := parseID(c, "resource_id")
|
||
if !ok {
|
||
return
|
||
}
|
||
resp, err := h.wallet.SetResourceStatus(c.Request.Context(), &walletv1.SetResourceStatusRequest{
|
||
RequestId: middleware.CurrentRequestID(c),
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
ResourceId: resourceID,
|
||
Status: status,
|
||
OperatorUserId: actorID(c),
|
||
})
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
resource := resourceFromProto(resp.GetResource())
|
||
h.auditLog(c, "set-resource-status", "resources", fmt.Sprintf("%d", resource.ResourceID), "success", status)
|
||
response.OK(c, resource)
|
||
}
|
||
|
||
func (h *Handler) setResourceGroupStatus(c *gin.Context, status string) {
|
||
groupID, ok := parseID(c, "group_id")
|
||
if !ok {
|
||
return
|
||
}
|
||
resp, err := h.wallet.SetResourceGroupStatus(c.Request.Context(), &walletv1.SetResourceGroupStatusRequest{
|
||
RequestId: middleware.CurrentRequestID(c),
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
GroupId: groupID,
|
||
Status: status,
|
||
OperatorUserId: actorID(c),
|
||
})
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
group := resourceGroupFromProto(resp.GetGroup())
|
||
h.auditLog(c, "set-resource-group-status", "resource_groups", fmt.Sprintf("%d", group.GroupID), "success", status)
|
||
response.OK(c, group)
|
||
}
|
||
|
||
func (h *Handler) setGiftStatus(c *gin.Context, status string) {
|
||
giftID := strings.TrimSpace(c.Param("gift_id"))
|
||
if giftID == "" {
|
||
response.BadRequest(c, "ID 参数不正确")
|
||
return
|
||
}
|
||
resp, err := h.wallet.SetGiftConfigStatus(c.Request.Context(), &walletv1.SetGiftConfigStatusRequest{
|
||
RequestId: middleware.CurrentRequestID(c),
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
GiftId: giftID,
|
||
Status: status,
|
||
OperatorUserId: actorID(c),
|
||
})
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
gift := giftFromProto(resp.GetGift())
|
||
h.auditLog(c, "set-gift-status", "gift_configs", gift.GiftID, "success", status)
|
||
response.OK(c, gift)
|
||
}
|
||
|
||
func (h *Handler) setResourceShopItemStatus(c *gin.Context, status string) {
|
||
shopItemID, ok := parseID(c, "shop_item_id")
|
||
if !ok {
|
||
return
|
||
}
|
||
resp, err := h.wallet.SetResourceShopItemStatus(c.Request.Context(), &walletv1.SetResourceShopItemStatusRequest{
|
||
RequestId: middleware.CurrentRequestID(c),
|
||
AppCode: appctx.FromContext(c.Request.Context()),
|
||
ShopItemId: shopItemID,
|
||
Status: status,
|
||
OperatorUserId: actorID(c),
|
||
})
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
item := resourceShopItemFromProto(resp.GetItem())
|
||
h.auditLog(c, "set-resource-shop-item-status", "resource_shop_items", fmt.Sprintf("%d", item.ShopItemID), "success", status)
|
||
response.OK(c, item)
|
||
}
|
||
|
||
func (h *Handler) auditLog(c *gin.Context, action string, resource string, resourceID string, status string, detail string) {
|
||
shared.OperationLogWithResourceID(c, h.audit, action, resource, resourceID, status, detail)
|
||
}
|
||
|
||
func actorID(c *gin.Context) int64 {
|
||
return int64(shared.ActorFromContext(c).UserID)
|
||
}
|
||
|
||
func parseID(c *gin.Context, name string) (int64, bool) {
|
||
raw := strings.TrimSpace(c.Param(name))
|
||
value, err := strconv.ParseInt(raw, 10, 64)
|
||
if err != nil || value <= 0 {
|
||
response.BadRequest(c, "ID 参数不正确")
|
||
return 0, false
|
||
}
|
||
return value, true
|
||
}
|
||
|
||
func optionalInt64Query(c *gin.Context, primary string, fallback string) (int64, bool) {
|
||
value, _, ok := optionalInt64QueryWithPresence(c, primary, fallback)
|
||
return value, ok
|
||
}
|
||
|
||
func optionalInt64QueryWithPresence(c *gin.Context, primary string, fallback string) (int64, bool, bool) {
|
||
raw := strings.TrimSpace(c.Query(primary))
|
||
if raw == "" {
|
||
raw = strings.TrimSpace(c.Query(fallback))
|
||
}
|
||
if raw == "" {
|
||
return 0, false, true
|
||
}
|
||
value, err := strconv.ParseInt(raw, 10, 64)
|
||
if err != nil || value < 0 {
|
||
response.BadRequest(c, primary+" 参数不正确")
|
||
return 0, false, false
|
||
}
|
||
return value, true, true
|
||
}
|
||
|
||
func firstQuery(c *gin.Context, keys ...string) string {
|
||
for _, key := range keys {
|
||
value := strings.TrimSpace(c.Query(key))
|
||
if value != "" {
|
||
return value
|
||
}
|
||
}
|
||
return ""
|
||
}
|