Update wallet resources and admin host flows
This commit is contained in:
parent
6de77ee02d
commit
427d335e4a
@ -174,9 +174,10 @@ type DebitGiftResponse struct {
|
|||||||
BillingReceiptId string `protobuf:"bytes,1,opt,name=billing_receipt_id,json=billingReceiptId,proto3" json:"billing_receipt_id,omitempty"`
|
BillingReceiptId string `protobuf:"bytes,1,opt,name=billing_receipt_id,json=billingReceiptId,proto3" json:"billing_receipt_id,omitempty"`
|
||||||
TransactionId string `protobuf:"bytes,2,opt,name=transaction_id,json=transactionId,proto3" json:"transaction_id,omitempty"`
|
TransactionId string `protobuf:"bytes,2,opt,name=transaction_id,json=transactionId,proto3" json:"transaction_id,omitempty"`
|
||||||
CoinSpent int64 `protobuf:"varint,3,opt,name=coin_spent,json=coinSpent,proto3" json:"coin_spent,omitempty"`
|
CoinSpent int64 `protobuf:"varint,3,opt,name=coin_spent,json=coinSpent,proto3" json:"coin_spent,omitempty"`
|
||||||
GiftPointAdded int64 `protobuf:"varint,4,opt,name=gift_point_added,json=giftPointAdded,proto3" json:"gift_point_added,omitempty"`
|
// gift_point_added 是历史回执字段;GIFT_POINT 已下线,新送礼固定返回 0。
|
||||||
HeatValue int64 `protobuf:"varint,5,opt,name=heat_value,json=heatValue,proto3" json:"heat_value,omitempty"`
|
GiftPointAdded int64 `protobuf:"varint,4,opt,name=gift_point_added,json=giftPointAdded,proto3" json:"gift_point_added,omitempty"`
|
||||||
PriceVersion string `protobuf:"bytes,6,opt,name=price_version,json=priceVersion,proto3" json:"price_version,omitempty"`
|
HeatValue int64 `protobuf:"varint,5,opt,name=heat_value,json=heatValue,proto3" json:"heat_value,omitempty"`
|
||||||
|
PriceVersion string `protobuf:"bytes,6,opt,name=price_version,json=priceVersion,proto3" json:"price_version,omitempty"`
|
||||||
// balance_after 是 sender 被扣费资产 available_amount 账后余额。
|
// balance_after 是 sender 被扣费资产 available_amount 账后余额。
|
||||||
BalanceAfter int64 `protobuf:"varint,7,opt,name=balance_after,json=balanceAfter,proto3" json:"balance_after,omitempty"`
|
BalanceAfter int64 `protobuf:"varint,7,opt,name=balance_after,json=balanceAfter,proto3" json:"balance_after,omitempty"`
|
||||||
ChargeAssetType string `protobuf:"bytes,8,opt,name=charge_asset_type,json=chargeAssetType,proto3" json:"charge_asset_type,omitempty"`
|
ChargeAssetType string `protobuf:"bytes,8,opt,name=charge_asset_type,json=chargeAssetType,proto3" json:"charge_asset_type,omitempty"`
|
||||||
@ -2574,9 +2575,10 @@ type Resource struct {
|
|||||||
ManagerGrantEnabled bool `protobuf:"varint,21,opt,name=manager_grant_enabled,json=managerGrantEnabled,proto3" json:"manager_grant_enabled,omitempty"`
|
ManagerGrantEnabled bool `protobuf:"varint,21,opt,name=manager_grant_enabled,json=managerGrantEnabled,proto3" json:"manager_grant_enabled,omitempty"`
|
||||||
PriceType string `protobuf:"bytes,22,opt,name=price_type,json=priceType,proto3" json:"price_type,omitempty"`
|
PriceType string `protobuf:"bytes,22,opt,name=price_type,json=priceType,proto3" json:"price_type,omitempty"`
|
||||||
CoinPrice int64 `protobuf:"varint,23,opt,name=coin_price,json=coinPrice,proto3" json:"coin_price,omitempty"`
|
CoinPrice int64 `protobuf:"varint,23,opt,name=coin_price,json=coinPrice,proto3" json:"coin_price,omitempty"`
|
||||||
GiftPointAmount int64 `protobuf:"varint,24,opt,name=gift_point_amount,json=giftPointAmount,proto3" json:"gift_point_amount,omitempty"`
|
// gift_point_amount 是历史字段;资源和礼物价格现在只维护真实 COIN 价格,新写入固定为 0。
|
||||||
unknownFields protoimpl.UnknownFields
|
GiftPointAmount int64 `protobuf:"varint,24,opt,name=gift_point_amount,json=giftPointAmount,proto3" json:"gift_point_amount,omitempty"`
|
||||||
sizeCache protoimpl.SizeCache
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Resource) Reset() {
|
func (x *Resource) Reset() {
|
||||||
@ -3053,20 +3055,21 @@ type GiftConfig struct {
|
|||||||
PresentationJson string `protobuf:"bytes,8,opt,name=presentation_json,json=presentationJson,proto3" json:"presentation_json,omitempty"`
|
PresentationJson string `protobuf:"bytes,8,opt,name=presentation_json,json=presentationJson,proto3" json:"presentation_json,omitempty"`
|
||||||
PriceVersion string `protobuf:"bytes,9,opt,name=price_version,json=priceVersion,proto3" json:"price_version,omitempty"`
|
PriceVersion string `protobuf:"bytes,9,opt,name=price_version,json=priceVersion,proto3" json:"price_version,omitempty"`
|
||||||
CoinPrice int64 `protobuf:"varint,10,opt,name=coin_price,json=coinPrice,proto3" json:"coin_price,omitempty"`
|
CoinPrice int64 `protobuf:"varint,10,opt,name=coin_price,json=coinPrice,proto3" json:"coin_price,omitempty"`
|
||||||
GiftPointAmount int64 `protobuf:"varint,11,opt,name=gift_point_amount,json=giftPointAmount,proto3" json:"gift_point_amount,omitempty"`
|
// gift_point_amount 是历史字段;送礼结算不再读取该值,新写入固定为 0。
|
||||||
HeatValue int64 `protobuf:"varint,12,opt,name=heat_value,json=heatValue,proto3" json:"heat_value,omitempty"`
|
GiftPointAmount int64 `protobuf:"varint,11,opt,name=gift_point_amount,json=giftPointAmount,proto3" json:"gift_point_amount,omitempty"`
|
||||||
CreatedByUserId int64 `protobuf:"varint,13,opt,name=created_by_user_id,json=createdByUserId,proto3" json:"created_by_user_id,omitempty"`
|
HeatValue int64 `protobuf:"varint,12,opt,name=heat_value,json=heatValue,proto3" json:"heat_value,omitempty"`
|
||||||
UpdatedByUserId int64 `protobuf:"varint,14,opt,name=updated_by_user_id,json=updatedByUserId,proto3" json:"updated_by_user_id,omitempty"`
|
CreatedByUserId int64 `protobuf:"varint,13,opt,name=created_by_user_id,json=createdByUserId,proto3" json:"created_by_user_id,omitempty"`
|
||||||
CreatedAtMs int64 `protobuf:"varint,15,opt,name=created_at_ms,json=createdAtMs,proto3" json:"created_at_ms,omitempty"`
|
UpdatedByUserId int64 `protobuf:"varint,14,opt,name=updated_by_user_id,json=updatedByUserId,proto3" json:"updated_by_user_id,omitempty"`
|
||||||
UpdatedAtMs int64 `protobuf:"varint,16,opt,name=updated_at_ms,json=updatedAtMs,proto3" json:"updated_at_ms,omitempty"`
|
CreatedAtMs int64 `protobuf:"varint,15,opt,name=created_at_ms,json=createdAtMs,proto3" json:"created_at_ms,omitempty"`
|
||||||
RegionIds []int64 `protobuf:"varint,17,rep,packed,name=region_ids,json=regionIds,proto3" json:"region_ids,omitempty"`
|
UpdatedAtMs int64 `protobuf:"varint,16,opt,name=updated_at_ms,json=updatedAtMs,proto3" json:"updated_at_ms,omitempty"`
|
||||||
GiftTypeCode string `protobuf:"bytes,18,opt,name=gift_type_code,json=giftTypeCode,proto3" json:"gift_type_code,omitempty"`
|
RegionIds []int64 `protobuf:"varint,17,rep,packed,name=region_ids,json=regionIds,proto3" json:"region_ids,omitempty"`
|
||||||
ChargeAssetType string `protobuf:"bytes,19,opt,name=charge_asset_type,json=chargeAssetType,proto3" json:"charge_asset_type,omitempty"`
|
GiftTypeCode string `protobuf:"bytes,18,opt,name=gift_type_code,json=giftTypeCode,proto3" json:"gift_type_code,omitempty"`
|
||||||
EffectiveFromMs int64 `protobuf:"varint,20,opt,name=effective_from_ms,json=effectiveFromMs,proto3" json:"effective_from_ms,omitempty"`
|
ChargeAssetType string `protobuf:"bytes,19,opt,name=charge_asset_type,json=chargeAssetType,proto3" json:"charge_asset_type,omitempty"`
|
||||||
EffectiveToMs int64 `protobuf:"varint,21,opt,name=effective_to_ms,json=effectiveToMs,proto3" json:"effective_to_ms,omitempty"`
|
EffectiveFromMs int64 `protobuf:"varint,20,opt,name=effective_from_ms,json=effectiveFromMs,proto3" json:"effective_from_ms,omitempty"`
|
||||||
EffectTypes []string `protobuf:"bytes,22,rep,name=effect_types,json=effectTypes,proto3" json:"effect_types,omitempty"`
|
EffectiveToMs int64 `protobuf:"varint,21,opt,name=effective_to_ms,json=effectiveToMs,proto3" json:"effective_to_ms,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
EffectTypes []string `protobuf:"bytes,22,rep,name=effect_types,json=effectTypes,proto3" json:"effect_types,omitempty"`
|
||||||
sizeCache protoimpl.SizeCache
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *GiftConfig) Reset() {
|
func (x *GiftConfig) Reset() {
|
||||||
@ -4307,9 +4310,10 @@ type CreateResourceRequest struct {
|
|||||||
ManagerGrantEnabled *bool `protobuf:"varint,18,opt,name=manager_grant_enabled,json=managerGrantEnabled,proto3,oneof" json:"manager_grant_enabled,omitempty"`
|
ManagerGrantEnabled *bool `protobuf:"varint,18,opt,name=manager_grant_enabled,json=managerGrantEnabled,proto3,oneof" json:"manager_grant_enabled,omitempty"`
|
||||||
PriceType string `protobuf:"bytes,19,opt,name=price_type,json=priceType,proto3" json:"price_type,omitempty"`
|
PriceType string `protobuf:"bytes,19,opt,name=price_type,json=priceType,proto3" json:"price_type,omitempty"`
|
||||||
CoinPrice int64 `protobuf:"varint,20,opt,name=coin_price,json=coinPrice,proto3" json:"coin_price,omitempty"`
|
CoinPrice int64 `protobuf:"varint,20,opt,name=coin_price,json=coinPrice,proto3" json:"coin_price,omitempty"`
|
||||||
GiftPointAmount int64 `protobuf:"varint,21,opt,name=gift_point_amount,json=giftPointAmount,proto3" json:"gift_point_amount,omitempty"`
|
// gift_point_amount 是历史字段;服务端忽略请求值并固定写入 0。
|
||||||
unknownFields protoimpl.UnknownFields
|
GiftPointAmount int64 `protobuf:"varint,21,opt,name=gift_point_amount,json=giftPointAmount,proto3" json:"gift_point_amount,omitempty"`
|
||||||
sizeCache protoimpl.SizeCache
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *CreateResourceRequest) Reset() {
|
func (x *CreateResourceRequest) Reset() {
|
||||||
@ -4512,9 +4516,10 @@ type UpdateResourceRequest struct {
|
|||||||
ManagerGrantEnabled *bool `protobuf:"varint,19,opt,name=manager_grant_enabled,json=managerGrantEnabled,proto3,oneof" json:"manager_grant_enabled,omitempty"`
|
ManagerGrantEnabled *bool `protobuf:"varint,19,opt,name=manager_grant_enabled,json=managerGrantEnabled,proto3,oneof" json:"manager_grant_enabled,omitempty"`
|
||||||
PriceType string `protobuf:"bytes,20,opt,name=price_type,json=priceType,proto3" json:"price_type,omitempty"`
|
PriceType string `protobuf:"bytes,20,opt,name=price_type,json=priceType,proto3" json:"price_type,omitempty"`
|
||||||
CoinPrice int64 `protobuf:"varint,21,opt,name=coin_price,json=coinPrice,proto3" json:"coin_price,omitempty"`
|
CoinPrice int64 `protobuf:"varint,21,opt,name=coin_price,json=coinPrice,proto3" json:"coin_price,omitempty"`
|
||||||
GiftPointAmount int64 `protobuf:"varint,22,opt,name=gift_point_amount,json=giftPointAmount,proto3" json:"gift_point_amount,omitempty"`
|
// gift_point_amount 是历史字段;服务端忽略请求值并固定写入 0。
|
||||||
unknownFields protoimpl.UnknownFields
|
GiftPointAmount int64 `protobuf:"varint,22,opt,name=gift_point_amount,json=giftPointAmount,proto3" json:"gift_point_amount,omitempty"`
|
||||||
sizeCache protoimpl.SizeCache
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *UpdateResourceRequest) Reset() {
|
func (x *UpdateResourceRequest) Reset() {
|
||||||
@ -5424,6 +5429,7 @@ type ListGiftConfigsRequest struct {
|
|||||||
ActiveOnly bool `protobuf:"varint,7,opt,name=active_only,json=activeOnly,proto3" json:"active_only,omitempty"`
|
ActiveOnly bool `protobuf:"varint,7,opt,name=active_only,json=activeOnly,proto3" json:"active_only,omitempty"`
|
||||||
RegionId int64 `protobuf:"varint,8,opt,name=region_id,json=regionId,proto3" json:"region_id,omitempty"`
|
RegionId int64 `protobuf:"varint,8,opt,name=region_id,json=regionId,proto3" json:"region_id,omitempty"`
|
||||||
FilterRegion bool `protobuf:"varint,9,opt,name=filter_region,json=filterRegion,proto3" json:"filter_region,omitempty"`
|
FilterRegion bool `protobuf:"varint,9,opt,name=filter_region,json=filterRegion,proto3" json:"filter_region,omitempty"`
|
||||||
|
GiftTypeCode string `protobuf:"bytes,10,opt,name=gift_type_code,json=giftTypeCode,proto3" json:"gift_type_code,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@ -5521,6 +5527,13 @@ func (x *ListGiftConfigsRequest) GetFilterRegion() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *ListGiftConfigsRequest) GetGiftTypeCode() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.GiftTypeCode
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
type ListGiftConfigsResponse struct {
|
type ListGiftConfigsResponse struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Gifts []*GiftConfig `protobuf:"bytes,1,rep,name=gifts,proto3" json:"gifts,omitempty"`
|
Gifts []*GiftConfig `protobuf:"bytes,1,rep,name=gifts,proto3" json:"gifts,omitempty"`
|
||||||
@ -5841,18 +5854,19 @@ type CreateGiftConfigRequest struct {
|
|||||||
PresentationJson string `protobuf:"bytes,8,opt,name=presentation_json,json=presentationJson,proto3" json:"presentation_json,omitempty"`
|
PresentationJson string `protobuf:"bytes,8,opt,name=presentation_json,json=presentationJson,proto3" json:"presentation_json,omitempty"`
|
||||||
PriceVersion string `protobuf:"bytes,9,opt,name=price_version,json=priceVersion,proto3" json:"price_version,omitempty"`
|
PriceVersion string `protobuf:"bytes,9,opt,name=price_version,json=priceVersion,proto3" json:"price_version,omitempty"`
|
||||||
CoinPrice int64 `protobuf:"varint,10,opt,name=coin_price,json=coinPrice,proto3" json:"coin_price,omitempty"`
|
CoinPrice int64 `protobuf:"varint,10,opt,name=coin_price,json=coinPrice,proto3" json:"coin_price,omitempty"`
|
||||||
GiftPointAmount int64 `protobuf:"varint,11,opt,name=gift_point_amount,json=giftPointAmount,proto3" json:"gift_point_amount,omitempty"`
|
// gift_point_amount 是历史字段;服务端忽略请求值并固定写入 0。
|
||||||
HeatValue int64 `protobuf:"varint,12,opt,name=heat_value,json=heatValue,proto3" json:"heat_value,omitempty"`
|
GiftPointAmount int64 `protobuf:"varint,11,opt,name=gift_point_amount,json=giftPointAmount,proto3" json:"gift_point_amount,omitempty"`
|
||||||
EffectiveAtMs int64 `protobuf:"varint,13,opt,name=effective_at_ms,json=effectiveAtMs,proto3" json:"effective_at_ms,omitempty"`
|
HeatValue int64 `protobuf:"varint,12,opt,name=heat_value,json=heatValue,proto3" json:"heat_value,omitempty"`
|
||||||
OperatorUserId int64 `protobuf:"varint,14,opt,name=operator_user_id,json=operatorUserId,proto3" json:"operator_user_id,omitempty"`
|
EffectiveAtMs int64 `protobuf:"varint,13,opt,name=effective_at_ms,json=effectiveAtMs,proto3" json:"effective_at_ms,omitempty"`
|
||||||
RegionIds []int64 `protobuf:"varint,15,rep,packed,name=region_ids,json=regionIds,proto3" json:"region_ids,omitempty"`
|
OperatorUserId int64 `protobuf:"varint,14,opt,name=operator_user_id,json=operatorUserId,proto3" json:"operator_user_id,omitempty"`
|
||||||
GiftTypeCode string `protobuf:"bytes,16,opt,name=gift_type_code,json=giftTypeCode,proto3" json:"gift_type_code,omitempty"`
|
RegionIds []int64 `protobuf:"varint,15,rep,packed,name=region_ids,json=regionIds,proto3" json:"region_ids,omitempty"`
|
||||||
ChargeAssetType string `protobuf:"bytes,17,opt,name=charge_asset_type,json=chargeAssetType,proto3" json:"charge_asset_type,omitempty"`
|
GiftTypeCode string `protobuf:"bytes,16,opt,name=gift_type_code,json=giftTypeCode,proto3" json:"gift_type_code,omitempty"`
|
||||||
EffectiveFromMs int64 `protobuf:"varint,18,opt,name=effective_from_ms,json=effectiveFromMs,proto3" json:"effective_from_ms,omitempty"`
|
ChargeAssetType string `protobuf:"bytes,17,opt,name=charge_asset_type,json=chargeAssetType,proto3" json:"charge_asset_type,omitempty"`
|
||||||
EffectiveToMs int64 `protobuf:"varint,19,opt,name=effective_to_ms,json=effectiveToMs,proto3" json:"effective_to_ms,omitempty"`
|
EffectiveFromMs int64 `protobuf:"varint,18,opt,name=effective_from_ms,json=effectiveFromMs,proto3" json:"effective_from_ms,omitempty"`
|
||||||
EffectTypes []string `protobuf:"bytes,20,rep,name=effect_types,json=effectTypes,proto3" json:"effect_types,omitempty"`
|
EffectiveToMs int64 `protobuf:"varint,19,opt,name=effective_to_ms,json=effectiveToMs,proto3" json:"effective_to_ms,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
EffectTypes []string `protobuf:"bytes,20,rep,name=effect_types,json=effectTypes,proto3" json:"effect_types,omitempty"`
|
||||||
sizeCache protoimpl.SizeCache
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *CreateGiftConfigRequest) Reset() {
|
func (x *CreateGiftConfigRequest) Reset() {
|
||||||
@ -6037,18 +6051,19 @@ type UpdateGiftConfigRequest struct {
|
|||||||
PresentationJson string `protobuf:"bytes,8,opt,name=presentation_json,json=presentationJson,proto3" json:"presentation_json,omitempty"`
|
PresentationJson string `protobuf:"bytes,8,opt,name=presentation_json,json=presentationJson,proto3" json:"presentation_json,omitempty"`
|
||||||
PriceVersion string `protobuf:"bytes,9,opt,name=price_version,json=priceVersion,proto3" json:"price_version,omitempty"`
|
PriceVersion string `protobuf:"bytes,9,opt,name=price_version,json=priceVersion,proto3" json:"price_version,omitempty"`
|
||||||
CoinPrice int64 `protobuf:"varint,10,opt,name=coin_price,json=coinPrice,proto3" json:"coin_price,omitempty"`
|
CoinPrice int64 `protobuf:"varint,10,opt,name=coin_price,json=coinPrice,proto3" json:"coin_price,omitempty"`
|
||||||
GiftPointAmount int64 `protobuf:"varint,11,opt,name=gift_point_amount,json=giftPointAmount,proto3" json:"gift_point_amount,omitempty"`
|
// gift_point_amount 是历史字段;服务端忽略请求值并固定写入 0。
|
||||||
HeatValue int64 `protobuf:"varint,12,opt,name=heat_value,json=heatValue,proto3" json:"heat_value,omitempty"`
|
GiftPointAmount int64 `protobuf:"varint,11,opt,name=gift_point_amount,json=giftPointAmount,proto3" json:"gift_point_amount,omitempty"`
|
||||||
EffectiveAtMs int64 `protobuf:"varint,13,opt,name=effective_at_ms,json=effectiveAtMs,proto3" json:"effective_at_ms,omitempty"`
|
HeatValue int64 `protobuf:"varint,12,opt,name=heat_value,json=heatValue,proto3" json:"heat_value,omitempty"`
|
||||||
OperatorUserId int64 `protobuf:"varint,14,opt,name=operator_user_id,json=operatorUserId,proto3" json:"operator_user_id,omitempty"`
|
EffectiveAtMs int64 `protobuf:"varint,13,opt,name=effective_at_ms,json=effectiveAtMs,proto3" json:"effective_at_ms,omitempty"`
|
||||||
RegionIds []int64 `protobuf:"varint,15,rep,packed,name=region_ids,json=regionIds,proto3" json:"region_ids,omitempty"`
|
OperatorUserId int64 `protobuf:"varint,14,opt,name=operator_user_id,json=operatorUserId,proto3" json:"operator_user_id,omitempty"`
|
||||||
GiftTypeCode string `protobuf:"bytes,16,opt,name=gift_type_code,json=giftTypeCode,proto3" json:"gift_type_code,omitempty"`
|
RegionIds []int64 `protobuf:"varint,15,rep,packed,name=region_ids,json=regionIds,proto3" json:"region_ids,omitempty"`
|
||||||
ChargeAssetType string `protobuf:"bytes,17,opt,name=charge_asset_type,json=chargeAssetType,proto3" json:"charge_asset_type,omitempty"`
|
GiftTypeCode string `protobuf:"bytes,16,opt,name=gift_type_code,json=giftTypeCode,proto3" json:"gift_type_code,omitempty"`
|
||||||
EffectiveFromMs int64 `protobuf:"varint,18,opt,name=effective_from_ms,json=effectiveFromMs,proto3" json:"effective_from_ms,omitempty"`
|
ChargeAssetType string `protobuf:"bytes,17,opt,name=charge_asset_type,json=chargeAssetType,proto3" json:"charge_asset_type,omitempty"`
|
||||||
EffectiveToMs int64 `protobuf:"varint,19,opt,name=effective_to_ms,json=effectiveToMs,proto3" json:"effective_to_ms,omitempty"`
|
EffectiveFromMs int64 `protobuf:"varint,18,opt,name=effective_from_ms,json=effectiveFromMs,proto3" json:"effective_from_ms,omitempty"`
|
||||||
EffectTypes []string `protobuf:"bytes,20,rep,name=effect_types,json=effectTypes,proto3" json:"effect_types,omitempty"`
|
EffectiveToMs int64 `protobuf:"varint,19,opt,name=effective_to_ms,json=effectiveToMs,proto3" json:"effective_to_ms,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
EffectTypes []string `protobuf:"bytes,20,rep,name=effect_types,json=effectTypes,proto3" json:"effect_types,omitempty"`
|
||||||
sizeCache protoimpl.SizeCache
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *UpdateGiftConfigRequest) Reset() {
|
func (x *UpdateGiftConfigRequest) Reset() {
|
||||||
@ -8589,15 +8604,16 @@ type GiftWallItem struct {
|
|||||||
TotalValue int64 `protobuf:"varint,9,opt,name=total_value,json=totalValue,proto3" json:"total_value,omitempty"`
|
TotalValue int64 `protobuf:"varint,9,opt,name=total_value,json=totalValue,proto3" json:"total_value,omitempty"`
|
||||||
TotalCoinValue int64 `protobuf:"varint,10,opt,name=total_coin_value,json=totalCoinValue,proto3" json:"total_coin_value,omitempty"`
|
TotalCoinValue int64 `protobuf:"varint,10,opt,name=total_coin_value,json=totalCoinValue,proto3" json:"total_coin_value,omitempty"`
|
||||||
TotalDiamondValue int64 `protobuf:"varint,11,opt,name=total_diamond_value,json=totalDiamondValue,proto3" json:"total_diamond_value,omitempty"`
|
TotalDiamondValue int64 `protobuf:"varint,11,opt,name=total_diamond_value,json=totalDiamondValue,proto3" json:"total_diamond_value,omitempty"`
|
||||||
TotalGiftPoint int64 `protobuf:"varint,12,opt,name=total_gift_point,json=totalGiftPoint,proto3" json:"total_gift_point,omitempty"`
|
// total_gift_point 是历史礼物墙字段;新送礼不再累加。
|
||||||
TotalHeatValue int64 `protobuf:"varint,13,opt,name=total_heat_value,json=totalHeatValue,proto3" json:"total_heat_value,omitempty"`
|
TotalGiftPoint int64 `protobuf:"varint,12,opt,name=total_gift_point,json=totalGiftPoint,proto3" json:"total_gift_point,omitempty"`
|
||||||
ChargeAssetType string `protobuf:"bytes,14,opt,name=charge_asset_type,json=chargeAssetType,proto3" json:"charge_asset_type,omitempty"`
|
TotalHeatValue int64 `protobuf:"varint,13,opt,name=total_heat_value,json=totalHeatValue,proto3" json:"total_heat_value,omitempty"`
|
||||||
LastPriceVersion string `protobuf:"bytes,15,opt,name=last_price_version,json=lastPriceVersion,proto3" json:"last_price_version,omitempty"`
|
ChargeAssetType string `protobuf:"bytes,14,opt,name=charge_asset_type,json=chargeAssetType,proto3" json:"charge_asset_type,omitempty"`
|
||||||
FirstReceivedAtMs int64 `protobuf:"varint,16,opt,name=first_received_at_ms,json=firstReceivedAtMs,proto3" json:"first_received_at_ms,omitempty"`
|
LastPriceVersion string `protobuf:"bytes,15,opt,name=last_price_version,json=lastPriceVersion,proto3" json:"last_price_version,omitempty"`
|
||||||
LastReceivedAtMs int64 `protobuf:"varint,17,opt,name=last_received_at_ms,json=lastReceivedAtMs,proto3" json:"last_received_at_ms,omitempty"`
|
FirstReceivedAtMs int64 `protobuf:"varint,16,opt,name=first_received_at_ms,json=firstReceivedAtMs,proto3" json:"first_received_at_ms,omitempty"`
|
||||||
SortOrder int32 `protobuf:"varint,18,opt,name=sort_order,json=sortOrder,proto3" json:"sort_order,omitempty"`
|
LastReceivedAtMs int64 `protobuf:"varint,17,opt,name=last_received_at_ms,json=lastReceivedAtMs,proto3" json:"last_received_at_ms,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
SortOrder int32 `protobuf:"varint,18,opt,name=sort_order,json=sortOrder,proto3" json:"sort_order,omitempty"`
|
||||||
sizeCache protoimpl.SizeCache
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *GiftWallItem) Reset() {
|
func (x *GiftWallItem) Reset() {
|
||||||
@ -8824,10 +8840,11 @@ type GetUserGiftWallResponse struct {
|
|||||||
TotalValue int64 `protobuf:"varint,4,opt,name=total_value,json=totalValue,proto3" json:"total_value,omitempty"`
|
TotalValue int64 `protobuf:"varint,4,opt,name=total_value,json=totalValue,proto3" json:"total_value,omitempty"`
|
||||||
TotalCoinValue int64 `protobuf:"varint,5,opt,name=total_coin_value,json=totalCoinValue,proto3" json:"total_coin_value,omitempty"`
|
TotalCoinValue int64 `protobuf:"varint,5,opt,name=total_coin_value,json=totalCoinValue,proto3" json:"total_coin_value,omitempty"`
|
||||||
TotalDiamondValue int64 `protobuf:"varint,6,opt,name=total_diamond_value,json=totalDiamondValue,proto3" json:"total_diamond_value,omitempty"`
|
TotalDiamondValue int64 `protobuf:"varint,6,opt,name=total_diamond_value,json=totalDiamondValue,proto3" json:"total_diamond_value,omitempty"`
|
||||||
TotalGiftPoint int64 `protobuf:"varint,7,opt,name=total_gift_point,json=totalGiftPoint,proto3" json:"total_gift_point,omitempty"`
|
// total_gift_point 是历史礼物墙汇总字段;新送礼不再累加。
|
||||||
TotalHeatValue int64 `protobuf:"varint,8,opt,name=total_heat_value,json=totalHeatValue,proto3" json:"total_heat_value,omitempty"`
|
TotalGiftPoint int64 `protobuf:"varint,7,opt,name=total_gift_point,json=totalGiftPoint,proto3" json:"total_gift_point,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
TotalHeatValue int64 `protobuf:"varint,8,opt,name=total_heat_value,json=totalHeatValue,proto3" json:"total_heat_value,omitempty"`
|
||||||
sizeCache protoimpl.SizeCache
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *GetUserGiftWallResponse) Reset() {
|
func (x *GetUserGiftWallResponse) Reset() {
|
||||||
@ -14606,7 +14623,7 @@ const file_proto_wallet_v1_wallet_proto_rawDesc = "" +
|
|||||||
"\x06status\x18\x04 \x01(\tR\x06status\x12(\n" +
|
"\x06status\x18\x04 \x01(\tR\x06status\x12(\n" +
|
||||||
"\x10operator_user_id\x18\x05 \x01(\x03R\x0eoperatorUserId\"M\n" +
|
"\x10operator_user_id\x18\x05 \x01(\x03R\x0eoperatorUserId\"M\n" +
|
||||||
"\x15ResourceGroupResponse\x124\n" +
|
"\x15ResourceGroupResponse\x124\n" +
|
||||||
"\x05group\x18\x01 \x01(\v2\x1e.hyapp.wallet.v1.ResourceGroupR\x05group\"\x98\x02\n" +
|
"\x05group\x18\x01 \x01(\v2\x1e.hyapp.wallet.v1.ResourceGroupR\x05group\"\xbe\x02\n" +
|
||||||
"\x16ListGiftConfigsRequest\x12\x1d\n" +
|
"\x16ListGiftConfigsRequest\x12\x1d\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"request_id\x18\x01 \x01(\tR\trequestId\x12\x19\n" +
|
"request_id\x18\x01 \x01(\tR\trequestId\x12\x19\n" +
|
||||||
@ -14618,7 +14635,9 @@ const file_proto_wallet_v1_wallet_proto_rawDesc = "" +
|
|||||||
"\vactive_only\x18\a \x01(\bR\n" +
|
"\vactive_only\x18\a \x01(\bR\n" +
|
||||||
"activeOnly\x12\x1b\n" +
|
"activeOnly\x12\x1b\n" +
|
||||||
"\tregion_id\x18\b \x01(\x03R\bregionId\x12#\n" +
|
"\tregion_id\x18\b \x01(\x03R\bregionId\x12#\n" +
|
||||||
"\rfilter_region\x18\t \x01(\bR\ffilterRegion\"b\n" +
|
"\rfilter_region\x18\t \x01(\bR\ffilterRegion\x12$\n" +
|
||||||
|
"\x0egift_type_code\x18\n" +
|
||||||
|
" \x01(\tR\fgiftTypeCode\"b\n" +
|
||||||
"\x17ListGiftConfigsResponse\x121\n" +
|
"\x17ListGiftConfigsResponse\x121\n" +
|
||||||
"\x05gifts\x18\x01 \x03(\v2\x1b.hyapp.wallet.v1.GiftConfigR\x05gifts\x12\x14\n" +
|
"\x05gifts\x18\x01 \x03(\v2\x1b.hyapp.wallet.v1.GiftConfigR\x05gifts\x12\x14\n" +
|
||||||
"\x05total\x18\x02 \x01(\x03R\x05total\"\x8f\x01\n" +
|
"\x05total\x18\x02 \x01(\x03R\x05total\"\x8f\x01\n" +
|
||||||
|
|||||||
@ -32,6 +32,7 @@ message DebitGiftResponse {
|
|||||||
string billing_receipt_id = 1;
|
string billing_receipt_id = 1;
|
||||||
string transaction_id = 2;
|
string transaction_id = 2;
|
||||||
int64 coin_spent = 3;
|
int64 coin_spent = 3;
|
||||||
|
// gift_point_added 是历史回执字段;GIFT_POINT 已下线,新送礼固定返回 0。
|
||||||
int64 gift_point_added = 4;
|
int64 gift_point_added = 4;
|
||||||
int64 heat_value = 5;
|
int64 heat_value = 5;
|
||||||
string price_version = 6;
|
string price_version = 6;
|
||||||
@ -325,6 +326,7 @@ message Resource {
|
|||||||
bool manager_grant_enabled = 21;
|
bool manager_grant_enabled = 21;
|
||||||
string price_type = 22;
|
string price_type = 22;
|
||||||
int64 coin_price = 23;
|
int64 coin_price = 23;
|
||||||
|
// gift_point_amount 是历史字段;资源和礼物价格现在只维护真实 COIN 价格,新写入固定为 0。
|
||||||
int64 gift_point_amount = 24;
|
int64 gift_point_amount = 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,6 +371,7 @@ message GiftConfig {
|
|||||||
string presentation_json = 8;
|
string presentation_json = 8;
|
||||||
string price_version = 9;
|
string price_version = 9;
|
||||||
int64 coin_price = 10;
|
int64 coin_price = 10;
|
||||||
|
// gift_point_amount 是历史字段;送礼结算不再读取该值,新写入固定为 0。
|
||||||
int64 gift_point_amount = 11;
|
int64 gift_point_amount = 11;
|
||||||
int64 heat_value = 12;
|
int64 heat_value = 12;
|
||||||
int64 created_by_user_id = 13;
|
int64 created_by_user_id = 13;
|
||||||
@ -518,6 +521,7 @@ message CreateResourceRequest {
|
|||||||
optional bool manager_grant_enabled = 18;
|
optional bool manager_grant_enabled = 18;
|
||||||
string price_type = 19;
|
string price_type = 19;
|
||||||
int64 coin_price = 20;
|
int64 coin_price = 20;
|
||||||
|
// gift_point_amount 是历史字段;服务端忽略请求值并固定写入 0。
|
||||||
int64 gift_point_amount = 21;
|
int64 gift_point_amount = 21;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -543,6 +547,7 @@ message UpdateResourceRequest {
|
|||||||
optional bool manager_grant_enabled = 19;
|
optional bool manager_grant_enabled = 19;
|
||||||
string price_type = 20;
|
string price_type = 20;
|
||||||
int64 coin_price = 21;
|
int64 coin_price = 21;
|
||||||
|
// gift_point_amount 是历史字段;服务端忽略请求值并固定写入 0。
|
||||||
int64 gift_point_amount = 22;
|
int64 gift_point_amount = 22;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -630,6 +635,7 @@ message ListGiftConfigsRequest {
|
|||||||
bool active_only = 7;
|
bool active_only = 7;
|
||||||
int64 region_id = 8;
|
int64 region_id = 8;
|
||||||
bool filter_region = 9;
|
bool filter_region = 9;
|
||||||
|
string gift_type_code = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ListGiftConfigsResponse {
|
message ListGiftConfigsResponse {
|
||||||
@ -674,6 +680,7 @@ message CreateGiftConfigRequest {
|
|||||||
string presentation_json = 8;
|
string presentation_json = 8;
|
||||||
string price_version = 9;
|
string price_version = 9;
|
||||||
int64 coin_price = 10;
|
int64 coin_price = 10;
|
||||||
|
// gift_point_amount 是历史字段;服务端忽略请求值并固定写入 0。
|
||||||
int64 gift_point_amount = 11;
|
int64 gift_point_amount = 11;
|
||||||
int64 heat_value = 12;
|
int64 heat_value = 12;
|
||||||
int64 effective_at_ms = 13;
|
int64 effective_at_ms = 13;
|
||||||
@ -697,6 +704,7 @@ message UpdateGiftConfigRequest {
|
|||||||
string presentation_json = 8;
|
string presentation_json = 8;
|
||||||
string price_version = 9;
|
string price_version = 9;
|
||||||
int64 coin_price = 10;
|
int64 coin_price = 10;
|
||||||
|
// gift_point_amount 是历史字段;服务端忽略请求值并固定写入 0。
|
||||||
int64 gift_point_amount = 11;
|
int64 gift_point_amount = 11;
|
||||||
int64 heat_value = 12;
|
int64 heat_value = 12;
|
||||||
int64 effective_at_ms = 13;
|
int64 effective_at_ms = 13;
|
||||||
@ -967,6 +975,7 @@ message GiftWallItem {
|
|||||||
int64 total_value = 9;
|
int64 total_value = 9;
|
||||||
int64 total_coin_value = 10;
|
int64 total_coin_value = 10;
|
||||||
int64 total_diamond_value = 11;
|
int64 total_diamond_value = 11;
|
||||||
|
// total_gift_point 是历史礼物墙字段;新送礼不再累加。
|
||||||
int64 total_gift_point = 12;
|
int64 total_gift_point = 12;
|
||||||
int64 total_heat_value = 13;
|
int64 total_heat_value = 13;
|
||||||
string charge_asset_type = 14;
|
string charge_asset_type = 14;
|
||||||
@ -989,6 +998,7 @@ message GetUserGiftWallResponse {
|
|||||||
int64 total_value = 4;
|
int64 total_value = 4;
|
||||||
int64 total_coin_value = 5;
|
int64 total_coin_value = 5;
|
||||||
int64 total_diamond_value = 6;
|
int64 total_diamond_value = 6;
|
||||||
|
// total_gift_point 是历史礼物墙汇总字段;新送礼不再累加。
|
||||||
int64 total_gift_point = 7;
|
int64 total_gift_point = 7;
|
||||||
int64 total_heat_value = 8;
|
int64 total_heat_value = 8;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -386,21 +386,25 @@ type Agency struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type HostProfile struct {
|
type HostProfile struct {
|
||||||
UserID int64 `json:"userId,string"`
|
UserID int64 `json:"userId,string"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
RegionID int64 `json:"regionId"`
|
RegionID int64 `json:"regionId"`
|
||||||
CurrentAgencyID int64 `json:"currentAgencyId"`
|
CurrentAgencyID int64 `json:"currentAgencyId"`
|
||||||
CurrentMembershipID int64 `json:"currentMembershipId"`
|
CurrentMembershipID int64 `json:"currentMembershipId"`
|
||||||
Source string `json:"source"`
|
Source string `json:"source"`
|
||||||
FirstBecameHostAtMs int64 `json:"firstBecameHostAtMs"`
|
FirstBecameHostAtMs int64 `json:"firstBecameHostAtMs"`
|
||||||
CreatedAtMs int64 `json:"createdAtMs"`
|
CreatedAtMs int64 `json:"createdAtMs"`
|
||||||
UpdatedAtMs int64 `json:"updatedAtMs"`
|
UpdatedAtMs int64 `json:"updatedAtMs"`
|
||||||
DisplayUserID string `json:"displayUserId"`
|
DisplayUserID string `json:"displayUserId"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Avatar string `json:"avatar"`
|
Avatar string `json:"avatar"`
|
||||||
RegionName string `json:"regionName"`
|
RegionName string `json:"regionName"`
|
||||||
CurrentAgencyName string `json:"currentAgencyName"`
|
CurrentAgencyName string `json:"currentAgencyName"`
|
||||||
Diamond int64 `json:"diamond"`
|
Diamond int64 `json:"diamond"`
|
||||||
|
CurrentAgencyOwnerUserID int64 `json:"currentAgencyOwnerUserId,string"`
|
||||||
|
CurrentAgencyOwnerDisplayUserID string `json:"currentAgencyOwnerDisplayUserId"`
|
||||||
|
CurrentAgencyOwnerUsername string `json:"currentAgencyOwnerUsername"`
|
||||||
|
CurrentAgencyOwnerAvatar string `json:"currentAgencyOwnerAvatar"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CoinSellerProfile struct {
|
type CoinSellerProfile struct {
|
||||||
|
|||||||
@ -12,7 +12,11 @@ import (
|
|||||||
authcore "hyapp-admin-server/internal/service"
|
authcore "hyapp-admin-server/internal/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
const refreshCookieName = "hyapp_admin_refresh"
|
const (
|
||||||
|
refreshCookieName = "hyapp_admin_refresh"
|
||||||
|
refreshCookiePath = "/"
|
||||||
|
legacyRefreshCookiePath = "/api/v1/auth"
|
||||||
|
)
|
||||||
|
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
service *AuthFlowService
|
service *AuthFlowService
|
||||||
@ -52,7 +56,7 @@ func (h *Handler) Login(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) Logout(c *gin.Context) {
|
func (h *Handler) Logout(c *gin.Context) {
|
||||||
if token, err := c.Cookie(refreshCookieName); err == nil && token != "" {
|
for _, token := range refreshCookieCandidates(c) {
|
||||||
_ = h.service.Logout(token)
|
_ = h.service.Logout(token)
|
||||||
}
|
}
|
||||||
h.setRefreshCookie(c, "", -1)
|
h.setRefreshCookie(c, "", -1)
|
||||||
@ -60,28 +64,32 @@ func (h *Handler) Logout(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) Refresh(c *gin.Context) {
|
func (h *Handler) Refresh(c *gin.Context) {
|
||||||
token, err := c.Cookie(refreshCookieName)
|
tokens := refreshCookieCandidates(c)
|
||||||
if err != nil || token == "" {
|
if len(tokens) == 0 {
|
||||||
response.Unauthorized(c, "刷新会话不存在")
|
response.Unauthorized(c, "刷新会话不存在")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := h.service.Refresh(token, LoginInput{
|
var result LoginResult
|
||||||
IP: c.ClientIP(),
|
var err error
|
||||||
UserAgent: c.Request.UserAgent(),
|
for _, token := range tokens {
|
||||||
})
|
result, err = h.service.Refresh(token, LoginInput{
|
||||||
if err != nil {
|
IP: c.ClientIP(),
|
||||||
response.Unauthorized(c, "刷新会话已失效")
|
UserAgent: c.Request.UserAgent(),
|
||||||
return
|
})
|
||||||
|
if err == nil {
|
||||||
|
h.setRefreshCookie(c, result.RefreshToken, int(h.cfg.RefreshTokenTTL.Seconds()))
|
||||||
|
response.OK(c, gin.H{
|
||||||
|
"accessToken": result.AccessToken,
|
||||||
|
"expiresAtMs": result.ExpiresAtMS,
|
||||||
|
"user": shared.UserDTO(result.User),
|
||||||
|
"permissions": result.Permissions,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h.setRefreshCookie(c, result.RefreshToken, int(h.cfg.RefreshTokenTTL.Seconds()))
|
response.Unauthorized(c, "刷新会话已失效")
|
||||||
response.OK(c, gin.H{
|
|
||||||
"accessToken": result.AccessToken,
|
|
||||||
"expiresAtMs": result.ExpiresAtMS,
|
|
||||||
"user": shared.UserDTO(result.User),
|
|
||||||
"permissions": result.Permissions,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) Me(c *gin.Context) {
|
func (h *Handler) Me(c *gin.Context) {
|
||||||
@ -114,7 +122,24 @@ func (h *Handler) ChangePassword(c *gin.Context) {
|
|||||||
|
|
||||||
func (h *Handler) setRefreshCookie(c *gin.Context, token string, maxAge int) {
|
func (h *Handler) setRefreshCookie(c *gin.Context, token string, maxAge int) {
|
||||||
c.SetSameSite(refreshCookieSameSite(h.cfg.RefreshCookieSameSite))
|
c.SetSameSite(refreshCookieSameSite(h.cfg.RefreshCookieSameSite))
|
||||||
c.SetCookie(refreshCookieName, token, maxAge, "/api/v1/auth", "", h.cfg.RefreshCookieSecure, true)
|
c.SetCookie(refreshCookieName, token, maxAge, refreshCookiePath, "", h.cfg.RefreshCookieSecure, true)
|
||||||
|
c.SetCookie(refreshCookieName, "", -1, legacyRefreshCookiePath, "", h.cfg.RefreshCookieSecure, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func refreshCookieCandidates(c *gin.Context) []string {
|
||||||
|
values := make([]string, 0, 2)
|
||||||
|
seen := map[string]struct{}{}
|
||||||
|
for _, cookie := range c.Request.Cookies() {
|
||||||
|
if cookie.Name != refreshCookieName || cookie.Value == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := seen[cookie.Value]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[cookie.Value] = struct{}{}
|
||||||
|
values = append(values, cookie.Value)
|
||||||
|
}
|
||||||
|
return values
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshCookieSameSite(value string) http.SameSite {
|
func refreshCookieSameSite(value string) http.SameSite {
|
||||||
|
|||||||
95
server/admin/internal/modules/auth/handler_test.go
Normal file
95
server/admin/internal/modules/auth/handler_test.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"hyapp-admin-server/internal/config"
|
||||||
|
authcore "hyapp-admin-server/internal/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRefreshUsesRootCookieAndClearsLegacyCookiePath(t *testing.T) {
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
store := newFakeAuthStore(t)
|
||||||
|
handler := testAuthHandler(store)
|
||||||
|
login, err := handler.service.Login(LoginInput{Username: "admin", Password: "secret", IP: "127.0.0.1", UserAgent: "browser"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("login: %v", err)
|
||||||
|
}
|
||||||
|
router := gin.New()
|
||||||
|
router.POST("/api/v1/auth/refresh", handler.Refresh)
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/auth/refresh", nil)
|
||||||
|
req.AddCookie(&http.Cookie{Name: refreshCookieName, Value: login.RefreshToken})
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
router.ServeHTTP(rec, req)
|
||||||
|
|
||||||
|
if rec.Code != http.StatusOK {
|
||||||
|
t.Fatalf("refresh status = %d body=%s", rec.Code, rec.Body.String())
|
||||||
|
}
|
||||||
|
setCookies := rec.Result().Cookies()
|
||||||
|
if !hasCookieWithPath(setCookies, refreshCookieName, refreshCookiePath) {
|
||||||
|
t.Fatalf("refresh must set root refresh cookie: %+v", setCookies)
|
||||||
|
}
|
||||||
|
if !hasExpiredCookieWithPath(setCookies, refreshCookieName, legacyRefreshCookiePath) {
|
||||||
|
t.Fatalf("refresh must clear legacy path cookie: %+v", setCookies)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRefreshTriesDuplicateRefreshCookiesUntilOneWorks(t *testing.T) {
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
store := newFakeAuthStore(t)
|
||||||
|
handler := testAuthHandler(store)
|
||||||
|
first, err := handler.service.Login(LoginInput{Username: "admin", Password: "secret", IP: "127.0.0.1", UserAgent: "browser"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("login: %v", err)
|
||||||
|
}
|
||||||
|
refreshed, err := handler.service.Refresh(first.RefreshToken, LoginInput{IP: "127.0.0.1", UserAgent: "browser"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("prime refresh: %v", err)
|
||||||
|
}
|
||||||
|
router := gin.New()
|
||||||
|
router.POST("/api/v1/auth/refresh", handler.Refresh)
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/auth/refresh", nil)
|
||||||
|
req.Header.Set("Cookie", refreshCookieName+"="+first.RefreshToken+"; "+refreshCookieName+"="+refreshed.RefreshToken)
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
router.ServeHTTP(rec, req)
|
||||||
|
|
||||||
|
if rec.Code != http.StatusOK {
|
||||||
|
t.Fatalf("refresh status = %d body=%s", rec.Code, rec.Body.String())
|
||||||
|
}
|
||||||
|
if strings.Contains(rec.Body.String(), "刷新会话已失效") {
|
||||||
|
t.Fatalf("refresh should skip stale duplicate cookie: %s", rec.Body.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAuthHandler(store *fakeAuthStore) *Handler {
|
||||||
|
cfg := config.Config{RefreshTokenTTL: 7 * 24 * time.Hour, RefreshCookieSameSite: "lax"}
|
||||||
|
return &Handler{
|
||||||
|
service: NewService(store, authcore.NewAuthService("test-secret", time.Hour), cfg),
|
||||||
|
cfg: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasCookieWithPath(cookies []*http.Cookie, name string, path string) bool {
|
||||||
|
for _, cookie := range cookies {
|
||||||
|
if cookie.Name == name && cookie.Path == path && cookie.MaxAge >= 0 && cookie.Value != "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasExpiredCookieWithPath(cookies []*http.Cookie, name string, path string) bool {
|
||||||
|
for _, cookie := range cookies {
|
||||||
|
if cookie.Name == name && cookie.Path == path && cookie.MaxAge < 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@ -318,10 +318,12 @@ func parseInt64ID(c *gin.Context, name string) (int64, bool) {
|
|||||||
func parseListQuery(c *gin.Context) (listQuery, bool) {
|
func parseListQuery(c *gin.Context) (listQuery, bool) {
|
||||||
options := shared.ListOptions(c)
|
options := shared.ListOptions(c)
|
||||||
query := listQuery{
|
query := listQuery{
|
||||||
Page: options.Page,
|
Page: options.Page,
|
||||||
PageSize: options.PageSize,
|
PageSize: options.PageSize,
|
||||||
Keyword: options.Keyword,
|
Keyword: options.Keyword,
|
||||||
Status: options.Status,
|
Status: options.Status,
|
||||||
|
SortBy: firstNonBlank(c.Query("sortBy"), c.Query("sort_by")),
|
||||||
|
SortDirection: firstNonBlank(c.Query("sortDirection"), c.Query("sort_direction"), c.Query("order")),
|
||||||
}
|
}
|
||||||
var ok bool
|
var ok bool
|
||||||
if query.RegionID, ok = parseOptionalInt64Query(c, "regionId", "region_id"); !ok {
|
if query.RegionID, ok = parseOptionalInt64Query(c, "regionId", "region_id"); !ok {
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -341,6 +342,7 @@ func (r *Reader) ListHostProfiles(ctx context.Context, query listQuery) ([]*user
|
|||||||
FROM host_profiles hp
|
FROM host_profiles hp
|
||||||
LEFT JOIN users u ON u.app_code = hp.app_code AND u.user_id = hp.user_id
|
LEFT JOIN users u ON u.app_code = hp.app_code AND u.user_id = hp.user_id
|
||||||
LEFT JOIN agencies a ON a.app_code = hp.app_code AND a.agency_id = hp.current_agency_id
|
LEFT JOIN agencies a ON a.app_code = hp.app_code AND a.agency_id = hp.current_agency_id
|
||||||
|
LEFT JOIN users agency_owner ON agency_owner.app_code = a.app_code AND agency_owner.user_id = a.owner_user_id
|
||||||
LEFT JOIN regions r ON r.app_code = hp.app_code AND r.region_id = hp.region_id
|
LEFT JOIN regions r ON r.app_code = hp.app_code AND r.region_id = hp.region_id
|
||||||
WHERE hp.app_code = ?`
|
WHERE hp.app_code = ?`
|
||||||
args := []any{appCode}
|
args := []any{appCode}
|
||||||
@ -358,24 +360,32 @@ func (r *Reader) ListHostProfiles(ctx context.Context, query listQuery) ([]*user
|
|||||||
}
|
}
|
||||||
if keyword := strings.TrimSpace(query.Keyword); keyword != "" {
|
if keyword := strings.TrimSpace(query.Keyword); keyword != "" {
|
||||||
like := "%" + keyword + "%"
|
like := "%" + keyword + "%"
|
||||||
whereSQL += " AND (CAST(hp.user_id AS CHAR) LIKE ? OR u.current_display_user_id LIKE ? OR u.username LIKE ? OR a.name LIKE ? OR r.name LIKE ?)"
|
whereSQL += " AND (CAST(hp.user_id AS CHAR) LIKE ? OR u.current_display_user_id LIKE ? OR u.username LIKE ? OR a.name LIKE ? OR agency_owner.current_display_user_id LIKE ? OR agency_owner.username LIKE ? OR r.name LIKE ?)"
|
||||||
args = append(args, like, like, like, like, like)
|
args = append(args, like, like, like, like, like, like, like)
|
||||||
}
|
}
|
||||||
|
|
||||||
total, err := countRows(ctx, r.db, whereSQL, args...)
|
total, err := countRows(ctx, r.db, whereSQL, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
limitSQL := "LIMIT ? OFFSET ?"
|
||||||
|
queryArgs := append(args, query.PageSize, offset(query.Page, query.PageSize))
|
||||||
|
if query.SortBy == "diamond" {
|
||||||
|
limitSQL = ""
|
||||||
|
queryArgs = args
|
||||||
|
}
|
||||||
rows, err := r.db.QueryContext(ctx, fmt.Sprintf(`
|
rows, err := r.db.QueryContext(ctx, fmt.Sprintf(`
|
||||||
SELECT hp.user_id, hp.status, hp.region_id, COALESCE(hp.current_agency_id, 0),
|
SELECT hp.user_id, hp.status, hp.region_id, COALESCE(hp.current_agency_id, 0),
|
||||||
COALESCE(hp.current_membership_id, 0), hp.source,
|
COALESCE(hp.current_membership_id, 0), hp.source,
|
||||||
hp.first_became_host_at_ms, hp.created_at_ms, hp.updated_at_ms,
|
hp.first_became_host_at_ms, hp.created_at_ms, hp.updated_at_ms,
|
||||||
COALESCE(u.current_display_user_id, ''), COALESCE(u.username, ''),
|
COALESCE(u.current_display_user_id, ''), COALESCE(u.username, ''),
|
||||||
COALESCE(u.avatar, ''), COALESCE(r.name, ''), COALESCE(a.name, '')
|
COALESCE(u.avatar, ''), COALESCE(r.name, ''), COALESCE(a.name, ''),
|
||||||
|
COALESCE(a.owner_user_id, 0), COALESCE(agency_owner.current_display_user_id, ''),
|
||||||
|
COALESCE(agency_owner.username, ''), COALESCE(agency_owner.avatar, '')
|
||||||
%s
|
%s
|
||||||
ORDER BY hp.created_at_ms DESC, hp.user_id DESC
|
ORDER BY hp.created_at_ms DESC, hp.user_id DESC
|
||||||
LIMIT ? OFFSET ?
|
%s
|
||||||
`, whereSQL), append(args, query.PageSize, offset(query.Page, query.PageSize))...)
|
`, whereSQL, limitSQL), queryArgs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
@ -400,6 +410,10 @@ func (r *Reader) ListHostProfiles(ctx context.Context, query listQuery) ([]*user
|
|||||||
&item.Avatar,
|
&item.Avatar,
|
||||||
&item.RegionName,
|
&item.RegionName,
|
||||||
&item.CurrentAgencyName,
|
&item.CurrentAgencyName,
|
||||||
|
&item.CurrentAgencyOwnerUserID,
|
||||||
|
&item.CurrentAgencyOwnerDisplayUserID,
|
||||||
|
&item.CurrentAgencyOwnerUsername,
|
||||||
|
&item.CurrentAgencyOwnerAvatar,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
@ -416,6 +430,10 @@ func (r *Reader) ListHostProfiles(ctx context.Context, query listQuery) ([]*user
|
|||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
item.Diamond = diamonds[item.UserID]
|
item.Diamond = diamonds[item.UserID]
|
||||||
}
|
}
|
||||||
|
if query.SortBy == "diamond" {
|
||||||
|
sortHostProfilesByDiamond(items, query.SortDirection)
|
||||||
|
items = paginateHostProfiles(items, query.Page, query.PageSize)
|
||||||
|
}
|
||||||
return items, total, nil
|
return items, total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -718,6 +736,36 @@ func (r *Reader) hostPeriodDiamonds(ctx context.Context, appCode string, userIDs
|
|||||||
return result, rows.Err()
|
return result, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sortHostProfilesByDiamond(items []*userclient.HostProfile, direction string) {
|
||||||
|
sort.SliceStable(items, func(i, j int) bool {
|
||||||
|
if items[i].Diamond == items[j].Diamond {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if direction == "asc" {
|
||||||
|
return items[i].Diamond < items[j].Diamond
|
||||||
|
}
|
||||||
|
return items[i].Diamond > items[j].Diamond
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func paginateHostProfiles(items []*userclient.HostProfile, page int, pageSize int) []*userclient.HostProfile {
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
if pageSize < 1 {
|
||||||
|
pageSize = 20
|
||||||
|
}
|
||||||
|
start := (page - 1) * pageSize
|
||||||
|
if start >= len(items) {
|
||||||
|
return []*userclient.HostProfile{}
|
||||||
|
}
|
||||||
|
end := start + pageSize
|
||||||
|
if end > len(items) {
|
||||||
|
end = len(items)
|
||||||
|
}
|
||||||
|
return items[start:end]
|
||||||
|
}
|
||||||
|
|
||||||
func sqlPlaceholders(count int) string {
|
func sqlPlaceholders(count int) string {
|
||||||
if count <= 0 {
|
if count <= 0 {
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@ -9,6 +9,8 @@ type listQuery struct {
|
|||||||
AgencyID int64
|
AgencyID int64
|
||||||
ParentBDUserID int64
|
ParentBDUserID int64
|
||||||
ParentLeaderUserID int64
|
ParentLeaderUserID int64
|
||||||
|
SortBy string
|
||||||
|
SortDirection string
|
||||||
}
|
}
|
||||||
|
|
||||||
type createBDLeaderRequest struct {
|
type createBDLeaderRequest struct {
|
||||||
|
|||||||
@ -472,6 +472,16 @@ func normalizeListQuery(query listQuery) listQuery {
|
|||||||
}
|
}
|
||||||
query.Keyword = strings.TrimSpace(query.Keyword)
|
query.Keyword = strings.TrimSpace(query.Keyword)
|
||||||
query.Status = strings.ToLower(strings.TrimSpace(query.Status))
|
query.Status = strings.ToLower(strings.TrimSpace(query.Status))
|
||||||
|
query.SortBy = strings.ToLower(strings.TrimSpace(query.SortBy))
|
||||||
|
if query.SortBy != "diamond" {
|
||||||
|
query.SortBy = ""
|
||||||
|
query.SortDirection = ""
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
query.SortDirection = strings.ToLower(strings.TrimSpace(query.SortDirection))
|
||||||
|
if query.SortDirection != "asc" {
|
||||||
|
query.SortDirection = "desc"
|
||||||
|
}
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,6 @@ type resourceDTO struct {
|
|||||||
WalletAssetAmount int64 `json:"walletAssetAmount"`
|
WalletAssetAmount int64 `json:"walletAssetAmount"`
|
||||||
PriceType string `json:"priceType"`
|
PriceType string `json:"priceType"`
|
||||||
CoinPrice int64 `json:"coinPrice"`
|
CoinPrice int64 `json:"coinPrice"`
|
||||||
GiftPointAmount int64 `json:"giftPointAmount"`
|
|
||||||
BadgeForm string `json:"badgeForm,omitempty"`
|
BadgeForm string `json:"badgeForm,omitempty"`
|
||||||
BadgeKind string `json:"badgeKind,omitempty"`
|
BadgeKind string `json:"badgeKind,omitempty"`
|
||||||
LevelTrack string `json:"levelTrack,omitempty"`
|
LevelTrack string `json:"levelTrack,omitempty"`
|
||||||
@ -74,7 +73,6 @@ type giftDTO struct {
|
|||||||
GiftTypeCode string `json:"giftTypeCode"`
|
GiftTypeCode string `json:"giftTypeCode"`
|
||||||
ChargeAssetType string `json:"chargeAssetType"`
|
ChargeAssetType string `json:"chargeAssetType"`
|
||||||
CoinPrice int64 `json:"coinPrice"`
|
CoinPrice int64 `json:"coinPrice"`
|
||||||
GiftPointAmount int64 `json:"giftPointAmount"`
|
|
||||||
EffectiveFromMS int64 `json:"effectiveFromMs"`
|
EffectiveFromMS int64 `json:"effectiveFromMs"`
|
||||||
EffectiveToMS int64 `json:"effectiveToMs"`
|
EffectiveToMS int64 `json:"effectiveToMs"`
|
||||||
EffectTypes []string `json:"effectTypes"`
|
EffectTypes []string `json:"effectTypes"`
|
||||||
@ -197,7 +195,6 @@ func resourceFromProto(item *walletv1.Resource) resourceDTO {
|
|||||||
WalletAssetAmount: item.GetWalletAssetAmount(),
|
WalletAssetAmount: item.GetWalletAssetAmount(),
|
||||||
PriceType: item.GetPriceType(),
|
PriceType: item.GetPriceType(),
|
||||||
CoinPrice: item.GetCoinPrice(),
|
CoinPrice: item.GetCoinPrice(),
|
||||||
GiftPointAmount: item.GetGiftPointAmount(),
|
|
||||||
BadgeForm: badgeFormForResource(item.GetResourceType(), item.GetMetadataJson()),
|
BadgeForm: badgeFormForResource(item.GetResourceType(), item.GetMetadataJson()),
|
||||||
BadgeKind: badgeKindForResource(item.GetResourceType(), item.GetMetadataJson()),
|
BadgeKind: badgeKindForResource(item.GetResourceType(), item.GetMetadataJson()),
|
||||||
LevelTrack: badgeLevelTrackForResource(item.GetResourceType(), item.GetMetadataJson()),
|
LevelTrack: badgeLevelTrackForResource(item.GetResourceType(), item.GetMetadataJson()),
|
||||||
@ -298,7 +295,6 @@ func giftFromProto(gift *walletv1.GiftConfig) giftDTO {
|
|||||||
GiftTypeCode: gift.GetGiftTypeCode(),
|
GiftTypeCode: gift.GetGiftTypeCode(),
|
||||||
ChargeAssetType: gift.GetChargeAssetType(),
|
ChargeAssetType: gift.GetChargeAssetType(),
|
||||||
CoinPrice: gift.GetCoinPrice(),
|
CoinPrice: gift.GetCoinPrice(),
|
||||||
GiftPointAmount: gift.GetGiftPointAmount(),
|
|
||||||
EffectiveFromMS: gift.GetEffectiveFromMs(),
|
EffectiveFromMS: gift.GetEffectiveFromMs(),
|
||||||
EffectiveToMS: gift.GetEffectiveToMs(),
|
EffectiveToMS: gift.GetEffectiveToMs(),
|
||||||
EffectTypes: gift.GetEffectTypes(),
|
EffectTypes: gift.GetEffectTypes(),
|
||||||
|
|||||||
@ -294,14 +294,15 @@ func (h *Handler) ListGifts(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
resp, err := h.wallet.ListGiftConfigs(c.Request.Context(), &walletv1.ListGiftConfigsRequest{
|
resp, err := h.wallet.ListGiftConfigs(c.Request.Context(), &walletv1.ListGiftConfigsRequest{
|
||||||
RequestId: middleware.CurrentRequestID(c),
|
RequestId: middleware.CurrentRequestID(c),
|
||||||
AppCode: appctx.FromContext(c.Request.Context()),
|
AppCode: appctx.FromContext(c.Request.Context()),
|
||||||
Status: options.Status,
|
Status: options.Status,
|
||||||
Keyword: options.Keyword,
|
Keyword: options.Keyword,
|
||||||
Page: int32(options.Page),
|
Page: int32(options.Page),
|
||||||
PageSize: int32(options.PageSize),
|
PageSize: int32(options.PageSize),
|
||||||
RegionId: regionID,
|
RegionId: regionID,
|
||||||
FilterRegion: filterRegion,
|
FilterRegion: filterRegion,
|
||||||
|
GiftTypeCode: giftTypeCodeQuery(c),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.ServerError(c, "获取礼物列表失败")
|
response.ServerError(c, "获取礼物列表失败")
|
||||||
@ -719,6 +720,14 @@ func optionalInt64Query(c *gin.Context, primary string, fallback string) (int64,
|
|||||||
return value, ok
|
return value, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func giftTypeCodeQuery(c *gin.Context) string {
|
||||||
|
value := strings.TrimSpace(c.Query("giftTypeCode"))
|
||||||
|
if value == "" {
|
||||||
|
value = strings.TrimSpace(c.Query("gift_type_code"))
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
func optionalInt64QueryWithPresence(c *gin.Context, primary string, fallback string) (int64, bool, bool) {
|
func optionalInt64QueryWithPresence(c *gin.Context, primary string, fallback string) (int64, bool, bool) {
|
||||||
raw := strings.TrimSpace(c.Query(primary))
|
raw := strings.TrimSpace(c.Query(primary))
|
||||||
if raw == "" {
|
if raw == "" {
|
||||||
|
|||||||
@ -53,7 +53,6 @@ type resourceRequest struct {
|
|||||||
Amount int64 `json:"amount"`
|
Amount int64 `json:"amount"`
|
||||||
PriceType string `json:"priceType"`
|
PriceType string `json:"priceType"`
|
||||||
CoinPrice int64 `json:"coinPrice"`
|
CoinPrice int64 `json:"coinPrice"`
|
||||||
GiftPointAmount int64 `json:"giftPointAmount"`
|
|
||||||
BadgeForm string `json:"badgeForm"`
|
BadgeForm string `json:"badgeForm"`
|
||||||
BadgeKind string `json:"badgeKind"`
|
BadgeKind string `json:"badgeKind"`
|
||||||
LevelTrack string `json:"levelTrack"`
|
LevelTrack string `json:"levelTrack"`
|
||||||
@ -134,7 +133,6 @@ type giftRequest struct {
|
|||||||
GiftTypeCode string `json:"giftTypeCode"`
|
GiftTypeCode string `json:"giftTypeCode"`
|
||||||
ChargeAssetType string `json:"chargeAssetType"`
|
ChargeAssetType string `json:"chargeAssetType"`
|
||||||
CoinPrice int64 `json:"coinPrice"`
|
CoinPrice int64 `json:"coinPrice"`
|
||||||
GiftPointAmount int64 `json:"giftPointAmount"`
|
|
||||||
HeatValue int64 `json:"heatValue"` // 兼容旧后台请求;新后台不再展示或提交热度。
|
HeatValue int64 `json:"heatValue"` // 兼容旧后台请求;新后台不再展示或提交热度。
|
||||||
EffectiveAtMS int64 `json:"effectiveAtMs"`
|
EffectiveAtMS int64 `json:"effectiveAtMs"`
|
||||||
EffectiveFromMS int64 `json:"effectiveFromMs"`
|
EffectiveFromMS int64 `json:"effectiveFromMs"`
|
||||||
@ -370,7 +368,7 @@ func (r giftRequest) createProto(c *gin.Context) *walletv1.CreateGiftConfigReque
|
|||||||
GiftTypeCode: strings.TrimSpace(r.GiftTypeCode),
|
GiftTypeCode: strings.TrimSpace(r.GiftTypeCode),
|
||||||
ChargeAssetType: strings.TrimSpace(r.ChargeAssetType),
|
ChargeAssetType: strings.TrimSpace(r.ChargeAssetType),
|
||||||
CoinPrice: r.CoinPrice,
|
CoinPrice: r.CoinPrice,
|
||||||
GiftPointAmount: r.GiftPointAmount,
|
GiftPointAmount: 0,
|
||||||
HeatValue: r.CoinPrice,
|
HeatValue: r.CoinPrice,
|
||||||
EffectiveAtMs: r.EffectiveAtMS,
|
EffectiveAtMs: r.EffectiveAtMS,
|
||||||
EffectiveFromMs: r.EffectiveFromMS,
|
EffectiveFromMs: r.EffectiveFromMS,
|
||||||
@ -395,7 +393,7 @@ func (r giftRequest) updateProto(c *gin.Context, giftID string) *walletv1.Update
|
|||||||
GiftTypeCode: strings.TrimSpace(r.GiftTypeCode),
|
GiftTypeCode: strings.TrimSpace(r.GiftTypeCode),
|
||||||
ChargeAssetType: strings.TrimSpace(r.ChargeAssetType),
|
ChargeAssetType: strings.TrimSpace(r.ChargeAssetType),
|
||||||
CoinPrice: r.CoinPrice,
|
CoinPrice: r.CoinPrice,
|
||||||
GiftPointAmount: r.GiftPointAmount,
|
GiftPointAmount: 0,
|
||||||
HeatValue: r.CoinPrice,
|
HeatValue: r.CoinPrice,
|
||||||
EffectiveAtMs: r.EffectiveAtMS,
|
EffectiveAtMs: r.EffectiveAtMS,
|
||||||
EffectiveFromMs: r.EffectiveFromMS,
|
EffectiveFromMs: r.EffectiveFromMS,
|
||||||
@ -502,7 +500,7 @@ func resourcePricing(priceType string, coinPrice int64) (string, int64, int64) {
|
|||||||
if priceType == resourcePriceTypeFree {
|
if priceType == resourcePriceTypeFree {
|
||||||
return resourcePriceTypeFree, 0, 0
|
return resourcePriceTypeFree, 0, 0
|
||||||
}
|
}
|
||||||
return resourcePriceTypeCoin, coinPrice, coinPrice
|
return resourcePriceTypeCoin, coinPrice, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateResourcePricing(req resourceRequest) error {
|
func validateResourcePricing(req resourceRequest) error {
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
roomTreasureLevelCount = 7
|
roomTreasureLevelCount = 7
|
||||||
|
|
||||||
defaultEnergySource = "gift_point_added"
|
defaultEnergySource = "heat_value"
|
||||||
defaultOpenDelayMS = int64(30_000)
|
defaultOpenDelayMS = int64(30_000)
|
||||||
defaultBroadcastScope = "region"
|
defaultBroadcastScope = "region"
|
||||||
defaultRewardStackPolicy = "allow_stack"
|
defaultRewardStackPolicy = "allow_stack"
|
||||||
@ -161,7 +161,11 @@ func normalizeConfig(config RoomTreasureConfig) (RoomTreasureConfig, error) {
|
|||||||
return RoomTreasureConfig{}, fmt.Errorf("%w: 配置版本不能小于 0", ErrInvalidArgument)
|
return RoomTreasureConfig{}, fmt.Errorf("%w: 配置版本不能小于 0", ErrInvalidArgument)
|
||||||
}
|
}
|
||||||
config.EnergySource = defaultString(strings.TrimSpace(config.EnergySource), defaultEnergySource)
|
config.EnergySource = defaultString(strings.TrimSpace(config.EnergySource), defaultEnergySource)
|
||||||
if !stringIn(config.EnergySource, "gift_point_added", "heat_value") {
|
if config.EnergySource == "gift_point_added" {
|
||||||
|
// gift_point_added 是历史配置值;GIFT_POINT 已下线,后台读取旧配置时统一切到真实礼物贡献口径。
|
||||||
|
config.EnergySource = defaultEnergySource
|
||||||
|
}
|
||||||
|
if config.EnergySource != defaultEnergySource {
|
||||||
return RoomTreasureConfig{}, fmt.Errorf("%w: 能量来源不支持", ErrInvalidArgument)
|
return RoomTreasureConfig{}, fmt.Errorf("%w: 能量来源不支持", ErrInvalidArgument)
|
||||||
}
|
}
|
||||||
if config.OpenDelayMS < 0 {
|
if config.OpenDelayMS < 0 {
|
||||||
|
|||||||
@ -213,6 +213,7 @@ func periodWindow(period string, now time.Time) (time.Time, time.Time) {
|
|||||||
|
|
||||||
func (s *Service) count(ctx context.Context, appCode string, boardType string, startMS int64, endMS int64) (int64, error) {
|
func (s *Service) count(ctx context.Context, appCode string, boardType string, startMS int64, endMS int64) (int64, error) {
|
||||||
subject, predicate := leaderboardDimension(boardType)
|
subject, predicate := leaderboardDimension(boardType)
|
||||||
|
// 排行榜价值优先使用送礼时按真实扣费比例生成的 heat_value;旧交易缺字段时只退回 coin_spent/charge_amount,不再读取 GIFT_POINT。
|
||||||
query := fmt.Sprintf(`
|
query := fmt.Sprintf(`
|
||||||
WITH gift_facts AS (
|
WITH gift_facts AS (
|
||||||
SELECT
|
SELECT
|
||||||
@ -220,10 +221,9 @@ func (s *Service) count(ctx context.Context, appCode string, boardType string, s
|
|||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.target_user_id')) AS SIGNED) AS target_user_id,
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.target_user_id')) AS SIGNED) AS target_user_id,
|
||||||
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.room_id')), '') AS room_id,
|
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.room_id')), '') AS room_id,
|
||||||
COALESCE(
|
COALESCE(
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_point_added')) AS SIGNED),
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.heat_value')) AS SIGNED),
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_point_amount')) AS SIGNED),
|
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.charge_amount')) AS SIGNED),
|
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.coin_spent')) AS SIGNED),
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.coin_spent')) AS SIGNED),
|
||||||
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.charge_amount')) AS SIGNED),
|
||||||
0
|
0
|
||||||
) AS gift_value,
|
) AS gift_value,
|
||||||
COALESCE(CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_count')) AS SIGNED), 0) AS gift_count,
|
COALESCE(CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_count')) AS SIGNED), 0) AS gift_count,
|
||||||
@ -247,6 +247,7 @@ func (s *Service) count(ctx context.Context, appCode string, boardType string, s
|
|||||||
|
|
||||||
func (s *Service) listItems(ctx context.Context, appCode string, boardType string, startMS int64, endMS int64, limit int, offset int) ([]Item, error) {
|
func (s *Service) listItems(ctx context.Context, appCode string, boardType string, startMS int64, endMS int64, limit int, offset int) ([]Item, error) {
|
||||||
subject, predicate := leaderboardDimension(boardType)
|
subject, predicate := leaderboardDimension(boardType)
|
||||||
|
// 排行榜列表和值排序统一使用 heat_value 口径,避免历史 gift_point 字段重新参与运营排行。
|
||||||
query := fmt.Sprintf(`
|
query := fmt.Sprintf(`
|
||||||
WITH gift_facts AS (
|
WITH gift_facts AS (
|
||||||
SELECT
|
SELECT
|
||||||
@ -254,10 +255,9 @@ func (s *Service) listItems(ctx context.Context, appCode string, boardType strin
|
|||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.target_user_id')) AS SIGNED) AS target_user_id,
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.target_user_id')) AS SIGNED) AS target_user_id,
|
||||||
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.room_id')), '') AS room_id,
|
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.room_id')), '') AS room_id,
|
||||||
COALESCE(
|
COALESCE(
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_point_added')) AS SIGNED),
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.heat_value')) AS SIGNED),
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_point_amount')) AS SIGNED),
|
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.charge_amount')) AS SIGNED),
|
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.coin_spent')) AS SIGNED),
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.coin_spent')) AS SIGNED),
|
||||||
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.charge_amount')) AS SIGNED),
|
||||||
0
|
0
|
||||||
) AS gift_value,
|
) AS gift_value,
|
||||||
COALESCE(CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_count')) AS SIGNED), 0) AS gift_count,
|
COALESCE(CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_count')) AS SIGNED), 0) AS gift_count,
|
||||||
@ -305,6 +305,7 @@ func (s *Service) listItems(ctx context.Context, appCode string, boardType strin
|
|||||||
|
|
||||||
func (s *Service) rankForUser(ctx context.Context, appCode string, boardType string, startMS int64, endMS int64, userID int64) (Item, bool, error) {
|
func (s *Service) rankForUser(ctx context.Context, appCode string, boardType string, startMS int64, endMS int64, userID int64) (Item, bool, error) {
|
||||||
subject, predicate := leaderboardDimension(boardType)
|
subject, predicate := leaderboardDimension(boardType)
|
||||||
|
// 用户名次查询与列表共用真实贡献口径;只兼容读取老交易的扣费金额,不兼容读取积分字段。
|
||||||
query := fmt.Sprintf(`
|
query := fmt.Sprintf(`
|
||||||
WITH gift_facts AS (
|
WITH gift_facts AS (
|
||||||
SELECT
|
SELECT
|
||||||
@ -312,10 +313,9 @@ func (s *Service) rankForUser(ctx context.Context, appCode string, boardType str
|
|||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.target_user_id')) AS SIGNED) AS target_user_id,
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.target_user_id')) AS SIGNED) AS target_user_id,
|
||||||
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.room_id')), '') AS room_id,
|
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.room_id')), '') AS room_id,
|
||||||
COALESCE(
|
COALESCE(
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_point_added')) AS SIGNED),
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.heat_value')) AS SIGNED),
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_point_amount')) AS SIGNED),
|
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.charge_amount')) AS SIGNED),
|
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.coin_spent')) AS SIGNED),
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.coin_spent')) AS SIGNED),
|
||||||
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.charge_amount')) AS SIGNED),
|
||||||
0
|
0
|
||||||
) AS gift_value,
|
) AS gift_value,
|
||||||
COALESCE(CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_count')) AS SIGNED), 0) AS gift_count,
|
COALESCE(CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_count')) AS SIGNED), 0) AS gift_count,
|
||||||
|
|||||||
@ -223,6 +223,7 @@ func userLeaderboardPeriodWindow(period string, now time.Time) (time.Time, time.
|
|||||||
|
|
||||||
func countUserLeaderboard(ctx context.Context, db *sql.DB, appCode string, boardType string, startMS int64, endMS int64) (int64, error) {
|
func countUserLeaderboard(ctx context.Context, db *sql.DB, appCode string, boardType string, startMS int64, endMS int64) (int64, error) {
|
||||||
subject, predicate := userLeaderboardDimension(boardType)
|
subject, predicate := userLeaderboardDimension(boardType)
|
||||||
|
// App 排行榜价值优先使用真实送礼贡献 heat_value;旧交易只兜底扣费金额,不再读取历史 GIFT_POINT 字段。
|
||||||
query := fmt.Sprintf(`
|
query := fmt.Sprintf(`
|
||||||
WITH gift_facts AS (
|
WITH gift_facts AS (
|
||||||
SELECT
|
SELECT
|
||||||
@ -230,10 +231,9 @@ func countUserLeaderboard(ctx context.Context, db *sql.DB, appCode string, board
|
|||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.target_user_id')) AS SIGNED) AS target_user_id,
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.target_user_id')) AS SIGNED) AS target_user_id,
|
||||||
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.room_id')), '') AS room_id,
|
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.room_id')), '') AS room_id,
|
||||||
COALESCE(
|
COALESCE(
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_point_added')) AS SIGNED),
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.heat_value')) AS SIGNED),
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_point_amount')) AS SIGNED),
|
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.charge_amount')) AS SIGNED),
|
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.coin_spent')) AS SIGNED),
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.coin_spent')) AS SIGNED),
|
||||||
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.charge_amount')) AS SIGNED),
|
||||||
0
|
0
|
||||||
) AS gift_value,
|
) AS gift_value,
|
||||||
COALESCE(CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_count')) AS SIGNED), 0) AS gift_count,
|
COALESCE(CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_count')) AS SIGNED), 0) AS gift_count,
|
||||||
@ -257,6 +257,7 @@ func countUserLeaderboard(ctx context.Context, db *sql.DB, appCode string, board
|
|||||||
|
|
||||||
func listUserLeaderboardItems(ctx context.Context, db *sql.DB, appCode string, boardType string, startMS int64, endMS int64, limit int, offset int) ([]userLeaderboardItem, error) {
|
func listUserLeaderboardItems(ctx context.Context, db *sql.DB, appCode string, boardType string, startMS int64, endMS int64, limit int, offset int) ([]userLeaderboardItem, error) {
|
||||||
subject, predicate := userLeaderboardDimension(boardType)
|
subject, predicate := userLeaderboardDimension(boardType)
|
||||||
|
// 列表排序和值统计统一走 heat_value,保证 App 和后台排行榜不被历史积分字段污染。
|
||||||
query := fmt.Sprintf(`
|
query := fmt.Sprintf(`
|
||||||
WITH gift_facts AS (
|
WITH gift_facts AS (
|
||||||
SELECT
|
SELECT
|
||||||
@ -264,10 +265,9 @@ func listUserLeaderboardItems(ctx context.Context, db *sql.DB, appCode string, b
|
|||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.target_user_id')) AS SIGNED) AS target_user_id,
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.target_user_id')) AS SIGNED) AS target_user_id,
|
||||||
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.room_id')), '') AS room_id,
|
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.room_id')), '') AS room_id,
|
||||||
COALESCE(
|
COALESCE(
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_point_added')) AS SIGNED),
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.heat_value')) AS SIGNED),
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_point_amount')) AS SIGNED),
|
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.charge_amount')) AS SIGNED),
|
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.coin_spent')) AS SIGNED),
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.coin_spent')) AS SIGNED),
|
||||||
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.charge_amount')) AS SIGNED),
|
||||||
0
|
0
|
||||||
) AS gift_value,
|
) AS gift_value,
|
||||||
COALESCE(CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_count')) AS SIGNED), 0) AS gift_count,
|
COALESCE(CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_count')) AS SIGNED), 0) AS gift_count,
|
||||||
@ -315,6 +315,7 @@ func listUserLeaderboardItems(ctx context.Context, db *sql.DB, appCode string, b
|
|||||||
|
|
||||||
func userLeaderboardRankForUser(ctx context.Context, db *sql.DB, appCode string, boardType string, startMS int64, endMS int64, userID int64) (userLeaderboardItem, bool, error) {
|
func userLeaderboardRankForUser(ctx context.Context, db *sql.DB, appCode string, boardType string, startMS int64, endMS int64, userID int64) (userLeaderboardItem, bool, error) {
|
||||||
subject, predicate := userLeaderboardDimension(boardType)
|
subject, predicate := userLeaderboardDimension(boardType)
|
||||||
|
// 当前用户名次查询只按真实贡献或扣费兜底统计,不再兼容 gift_point_added/gift_point_amount。
|
||||||
query := fmt.Sprintf(`
|
query := fmt.Sprintf(`
|
||||||
WITH gift_facts AS (
|
WITH gift_facts AS (
|
||||||
SELECT
|
SELECT
|
||||||
@ -322,10 +323,9 @@ func userLeaderboardRankForUser(ctx context.Context, db *sql.DB, appCode string,
|
|||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.target_user_id')) AS SIGNED) AS target_user_id,
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.target_user_id')) AS SIGNED) AS target_user_id,
|
||||||
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.room_id')), '') AS room_id,
|
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.room_id')), '') AS room_id,
|
||||||
COALESCE(
|
COALESCE(
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_point_added')) AS SIGNED),
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.heat_value')) AS SIGNED),
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_point_amount')) AS SIGNED),
|
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.charge_amount')) AS SIGNED),
|
|
||||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.coin_spent')) AS SIGNED),
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.coin_spent')) AS SIGNED),
|
||||||
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.charge_amount')) AS SIGNED),
|
||||||
0
|
0
|
||||||
) AS gift_value,
|
) AS gift_value,
|
||||||
COALESCE(CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_count')) AS SIGNED), 0) AS gift_count,
|
COALESCE(CAST(JSON_UNQUOTE(JSON_EXTRACT(metadata_json, '$.gift_count')) AS SIGNED), 0) AS gift_count,
|
||||||
|
|||||||
@ -254,7 +254,7 @@ INSERT IGNORE INTO room_treasure_configs (
|
|||||||
'lalu',
|
'lalu',
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
'gift_point_added',
|
'heat_value',
|
||||||
30000,
|
30000,
|
||||||
1,
|
1,
|
||||||
'region',
|
'region',
|
||||||
|
|||||||
@ -406,7 +406,7 @@ type SendGift struct {
|
|||||||
BillingReceiptID string `json:"billing_receipt_id,omitempty"`
|
BillingReceiptID string `json:"billing_receipt_id,omitempty"`
|
||||||
// CoinSpent 是 sender COIN 实际扣减值,来自 wallet-service 服务端价格。
|
// CoinSpent 是 sender COIN 实际扣减值,来自 wallet-service 服务端价格。
|
||||||
CoinSpent int64 `json:"coin_spent,omitempty"`
|
CoinSpent int64 `json:"coin_spent,omitempty"`
|
||||||
// GiftPointAdded 是 target GIFT_POINT 实际入账值,来自 wallet-service。
|
// GiftPointAdded 是历史命令字段;GIFT_POINT 已下线,新送礼不再赋值,恢复逻辑只依赖 HeatValue。
|
||||||
GiftPointAdded int64 `json:"gift_point_added,omitempty"`
|
GiftPointAdded int64 `json:"gift_point_added,omitempty"`
|
||||||
// HeatValue 是 Room Cell 恢复时唯一可信的热度增量,不能重新用客户端价格推导。
|
// HeatValue 是 Room Cell 恢复时唯一可信的热度增量,不能重新用客户端价格推导。
|
||||||
HeatValue int64 `json:"heat_value,omitempty"`
|
HeatValue int64 `json:"heat_value,omitempty"`
|
||||||
|
|||||||
@ -123,7 +123,6 @@ func (s *Service) SendGift(ctx context.Context, req *roomv1.SendGiftRequest) (*r
|
|||||||
}
|
}
|
||||||
settledCommand.BillingReceiptID = billing.GetBillingReceiptId()
|
settledCommand.BillingReceiptID = billing.GetBillingReceiptId()
|
||||||
settledCommand.CoinSpent = billing.GetCoinSpent()
|
settledCommand.CoinSpent = billing.GetCoinSpent()
|
||||||
settledCommand.GiftPointAdded = billing.GetGiftPointAdded()
|
|
||||||
settledCommand.HeatValue = billing.GetHeatValue()
|
settledCommand.HeatValue = billing.GetHeatValue()
|
||||||
settledCommand.PriceVersion = billing.GetPriceVersion()
|
settledCommand.PriceVersion = billing.GetPriceVersion()
|
||||||
settledCommand.GiftTypeCode = billing.GetGiftTypeCode()
|
settledCommand.GiftTypeCode = billing.GetGiftTypeCode()
|
||||||
@ -259,7 +258,6 @@ func (s *Service) SendGift(ctx context.Context, req *roomv1.SendGiftRequest) (*r
|
|||||||
"gift_count": fmt.Sprintf("%d", cmd.GiftCount),
|
"gift_count": fmt.Sprintf("%d", cmd.GiftCount),
|
||||||
"billing_receipt_id": billing.GetBillingReceiptId(),
|
"billing_receipt_id": billing.GetBillingReceiptId(),
|
||||||
"coin_spent": fmt.Sprintf("%d", settledCommand.CoinSpent),
|
"coin_spent": fmt.Sprintf("%d", settledCommand.CoinSpent),
|
||||||
"gift_point_added": fmt.Sprintf("%d", settledCommand.GiftPointAdded),
|
|
||||||
"target_count": fmt.Sprintf("%d", len(cmd.TargetUserIDs)),
|
"target_count": fmt.Sprintf("%d", len(cmd.TargetUserIDs)),
|
||||||
"target_user_ids": giftTargetUserIDsAttribute(cmd.TargetUserIDs),
|
"target_user_ids": giftTargetUserIDsAttribute(cmd.TargetUserIDs),
|
||||||
"price_version": settledCommand.PriceVersion,
|
"price_version": settledCommand.PriceVersion,
|
||||||
|
|||||||
@ -25,12 +25,11 @@ import (
|
|||||||
const (
|
const (
|
||||||
roomTreasureLevelCount = 7
|
roomTreasureLevelCount = 7
|
||||||
|
|
||||||
roomTreasureDefaultEnergySource = "gift_point_added"
|
roomTreasureDefaultEnergySource = "heat_value"
|
||||||
roomTreasureDefaultOpenDelayMS = int64(30_000)
|
roomTreasureDefaultOpenDelayMS = int64(30_000)
|
||||||
roomTreasureDefaultBroadcastScope = "region"
|
roomTreasureDefaultBroadcastScope = "region"
|
||||||
roomTreasureDefaultRewardStackPolicy = "allow_stack"
|
roomTreasureDefaultRewardStackPolicy = "allow_stack"
|
||||||
|
|
||||||
roomTreasureEnergyGiftPoint = "gift_point_added"
|
|
||||||
roomTreasureEnergyHeatValue = "heat_value"
|
roomTreasureEnergyHeatValue = "heat_value"
|
||||||
|
|
||||||
roomTreasureBroadcastNone = "none"
|
roomTreasureBroadcastNone = "none"
|
||||||
@ -127,7 +126,11 @@ func normalizeRoomTreasureConfig(config RoomTreasureConfig) (RoomTreasureConfig,
|
|||||||
return RoomTreasureConfig{}, xerr.New(xerr.InvalidArgument, "room treasure config_version is invalid")
|
return RoomTreasureConfig{}, xerr.New(xerr.InvalidArgument, "room treasure config_version is invalid")
|
||||||
}
|
}
|
||||||
config.EnergySource = defaultRoomTreasureString(config.EnergySource, roomTreasureDefaultEnergySource)
|
config.EnergySource = defaultRoomTreasureString(config.EnergySource, roomTreasureDefaultEnergySource)
|
||||||
if config.EnergySource != roomTreasureEnergyGiftPoint && config.EnergySource != roomTreasureEnergyHeatValue {
|
if config.EnergySource == "gift_point_added" {
|
||||||
|
// 旧配置可能还存着 gift_point_added;GIFT_POINT 已下线,运行时统一按真实送礼贡献 heat_value 计算宝箱能量。
|
||||||
|
config.EnergySource = roomTreasureEnergyHeatValue
|
||||||
|
}
|
||||||
|
if config.EnergySource != roomTreasureEnergyHeatValue {
|
||||||
return RoomTreasureConfig{}, xerr.New(xerr.InvalidArgument, "room treasure energy_source is invalid")
|
return RoomTreasureConfig{}, xerr.New(xerr.InvalidArgument, "room treasure energy_source is invalid")
|
||||||
}
|
}
|
||||||
if config.OpenDelayMS < 0 || config.BroadcastDelayMS < 0 {
|
if config.OpenDelayMS < 0 || config.BroadcastDelayMS < 0 {
|
||||||
@ -327,7 +330,7 @@ func (s *Service) applyRoomTreasureGift(now time.Time, current *state.RoomState,
|
|||||||
treasure := treasureStateForNow(current.Treasure, cfg, now, true)
|
treasure := treasureStateForNow(current.Treasure, cfg, now, true)
|
||||||
result := treasureGiftApplyResult{touched: current.Treasure == nil || treasure.ResetAtMS != current.Treasure.ResetAtMS}
|
result := treasureGiftApplyResult{touched: current.Treasure == nil || treasure.ResetAtMS != current.Treasure.ResetAtMS}
|
||||||
|
|
||||||
addedEnergy := roomTreasureEnergy(cfg, cmd.GiftID, billing.GetGiftTypeCode(), billing.GetGiftPointAdded(), billing.GetHeatValue())
|
addedEnergy := roomTreasureEnergy(cfg, cmd.GiftID, billing.GetGiftTypeCode(), billing.GetHeatValue())
|
||||||
effectiveEnergy := int64(0)
|
effectiveEnergy := int64(0)
|
||||||
overflowEnergy := int64(0)
|
overflowEnergy := int64(0)
|
||||||
|
|
||||||
@ -483,11 +486,9 @@ func roomTreasureLevelByNumber(cfg RoomTreasureConfig, level int32) RoomTreasure
|
|||||||
return defaultRoomTreasureConfig(cfg.AppCode).Levels[0]
|
return defaultRoomTreasureConfig(cfg.AppCode).Levels[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
func roomTreasureEnergy(cfg RoomTreasureConfig, giftID string, giftTypeCode string, giftPointAdded int64, heatValue int64) int64 {
|
func roomTreasureEnergy(cfg RoomTreasureConfig, giftID string, giftTypeCode string, heatValue int64) int64 {
|
||||||
base := giftPointAdded
|
// 宝箱能量只读取 wallet-service 按真实扣费和贡献比例计算出的 heat_value,避免历史 GIFT_POINT 字段污染活动进度。
|
||||||
if cfg.EnergySource == roomTreasureEnergyHeatValue {
|
base := heatValue
|
||||||
base = heatValue
|
|
||||||
}
|
|
||||||
if base < 0 {
|
if base < 0 {
|
||||||
base = 0
|
base = 0
|
||||||
}
|
}
|
||||||
|
|||||||
@ -291,7 +291,7 @@ func (r *Repository) Migrate(ctx context.Context) error {
|
|||||||
'lalu',
|
'lalu',
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
'gift_point_added',
|
'heat_value',
|
||||||
30000,
|
30000,
|
||||||
1,
|
1,
|
||||||
'region',
|
'region',
|
||||||
|
|||||||
@ -402,7 +402,7 @@ CREATE TABLE IF NOT EXISTS wallet_gift_prices (
|
|||||||
status VARCHAR(32) NOT NULL COMMENT '业务状态',
|
status VARCHAR(32) NOT NULL COMMENT '业务状态',
|
||||||
charge_asset_type VARCHAR(32) NOT NULL DEFAULT 'COIN' COMMENT '扣费资产类型',
|
charge_asset_type VARCHAR(32) NOT NULL DEFAULT 'COIN' COMMENT '扣费资产类型',
|
||||||
coin_price BIGINT NOT NULL COMMENT '金币价格',
|
coin_price BIGINT NOT NULL COMMENT '金币价格',
|
||||||
gift_point_amount BIGINT NOT NULL COMMENT '礼物积分数量',
|
gift_point_amount BIGINT NOT NULL COMMENT '历史字段:GIFT_POINT 已下线,新写入固定 0',
|
||||||
heat_value BIGINT NOT NULL COMMENT '热度值',
|
heat_value BIGINT NOT NULL COMMENT '热度值',
|
||||||
effective_at_ms BIGINT NOT NULL COMMENT '生效时间,UTC epoch ms',
|
effective_at_ms BIGINT NOT NULL COMMENT '生效时间,UTC epoch ms',
|
||||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||||
@ -590,7 +590,7 @@ CREATE TABLE IF NOT EXISTS resources (
|
|||||||
wallet_asset_amount BIGINT NOT NULL DEFAULT 0 COMMENT '钱包资产数量',
|
wallet_asset_amount BIGINT NOT NULL DEFAULT 0 COMMENT '钱包资产数量',
|
||||||
price_type VARCHAR(32) NOT NULL DEFAULT '' COMMENT '资源价格类型:coin/free',
|
price_type VARCHAR(32) NOT NULL DEFAULT '' COMMENT '资源价格类型:coin/free',
|
||||||
coin_price BIGINT NOT NULL DEFAULT 0 COMMENT '资源金币价格',
|
coin_price BIGINT NOT NULL DEFAULT 0 COMMENT '资源金币价格',
|
||||||
gift_point_amount BIGINT NOT NULL DEFAULT 0 COMMENT '资源礼物积分',
|
gift_point_amount BIGINT NOT NULL DEFAULT 0 COMMENT '历史字段:GIFT_POINT 已下线,新写入固定 0',
|
||||||
usage_scope_json JSON NOT NULL COMMENT '使用范围 JSON 配置或快照',
|
usage_scope_json JSON NOT NULL COMMENT '使用范围 JSON 配置或快照',
|
||||||
asset_url VARCHAR(512) NOT NULL DEFAULT '' COMMENT '资产 URL',
|
asset_url VARCHAR(512) NOT NULL DEFAULT '' COMMENT '资产 URL',
|
||||||
preview_url VARCHAR(512) NOT NULL DEFAULT '' COMMENT '预览 URL',
|
preview_url VARCHAR(512) NOT NULL DEFAULT '' COMMENT '预览 URL',
|
||||||
@ -635,7 +635,7 @@ DEALLOCATE PREPARE stmt;
|
|||||||
|
|
||||||
SET @ddl := IF(
|
SET @ddl := IF(
|
||||||
(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'resources' AND COLUMN_NAME = 'gift_point_amount') = 0,
|
(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'resources' AND COLUMN_NAME = 'gift_point_amount') = 0,
|
||||||
'ALTER TABLE resources ADD COLUMN gift_point_amount BIGINT NOT NULL DEFAULT 0 COMMENT ''资源礼物积分'' AFTER coin_price',
|
'ALTER TABLE resources ADD COLUMN gift_point_amount BIGINT NOT NULL DEFAULT 0 COMMENT ''历史字段:GIFT_POINT 已下线,新写入固定 0'' AFTER coin_price',
|
||||||
'SELECT 1'
|
'SELECT 1'
|
||||||
);
|
);
|
||||||
PREPARE stmt FROM @ddl;
|
PREPARE stmt FROM @ddl;
|
||||||
@ -831,7 +831,7 @@ CREATE TABLE IF NOT EXISTS user_gift_wall (
|
|||||||
total_value BIGINT NOT NULL DEFAULT 0 COMMENT '总值',
|
total_value BIGINT NOT NULL DEFAULT 0 COMMENT '总值',
|
||||||
total_coin_value BIGINT NOT NULL DEFAULT 0 COMMENT '总金币值',
|
total_coin_value BIGINT NOT NULL DEFAULT 0 COMMENT '总金币值',
|
||||||
total_diamond_value BIGINT NOT NULL DEFAULT 0 COMMENT '总钻石值',
|
total_diamond_value BIGINT NOT NULL DEFAULT 0 COMMENT '总钻石值',
|
||||||
total_gift_point BIGINT NOT NULL DEFAULT 0 COMMENT '总礼物积分',
|
total_gift_point BIGINT NOT NULL DEFAULT 0 COMMENT '历史字段:GIFT_POINT 已下线,新送礼不再累加',
|
||||||
total_heat_value BIGINT NOT NULL DEFAULT 0 COMMENT '总热度值',
|
total_heat_value BIGINT NOT NULL DEFAULT 0 COMMENT '总热度值',
|
||||||
charge_asset_type VARCHAR(32) NOT NULL DEFAULT 'COIN' COMMENT '扣费资产类型',
|
charge_asset_type VARCHAR(32) NOT NULL DEFAULT 'COIN' COMMENT '扣费资产类型',
|
||||||
last_price_version VARCHAR(64) NOT NULL DEFAULT '' COMMENT '最后价格版本',
|
last_price_version VARCHAR(64) NOT NULL DEFAULT '' COMMENT '最后价格版本',
|
||||||
|
|||||||
@ -7,7 +7,7 @@ const (
|
|||||||
AssetCoin = "COIN"
|
AssetCoin = "COIN"
|
||||||
// AssetCoinSellerCoin 是币商专用金币资产,只能通过币商转账兑换成玩家普通 COIN。
|
// AssetCoinSellerCoin 是币商专用金币资产,只能通过币商转账兑换成玩家普通 COIN。
|
||||||
AssetCoinSellerCoin = "COIN_SELLER_COIN"
|
AssetCoinSellerCoin = "COIN_SELLER_COIN"
|
||||||
// AssetGiftPoint 是主播礼物积分,不能直接消费或提现。
|
// AssetGiftPoint 是历史礼物积分资产,只保留历史账本查询兼容;新送礼不会再给它入账。
|
||||||
AssetGiftPoint = "GIFT_POINT"
|
AssetGiftPoint = "GIFT_POINT"
|
||||||
// AssetHostPeriodDiamond 是主播工资周期钻石投影,只参与工资等级结算,不进入通用钱包余额。
|
// AssetHostPeriodDiamond 是主播工资周期钻石投影,只参与工资等级结算,不进入通用钱包余额。
|
||||||
AssetHostPeriodDiamond = "HOST_PERIOD_DIAMOND"
|
AssetHostPeriodDiamond = "HOST_PERIOD_DIAMOND"
|
||||||
@ -126,7 +126,7 @@ type DebitGiftCommand struct {
|
|||||||
type DebitGiftTargetCommand struct {
|
type DebitGiftTargetCommand struct {
|
||||||
// CommandID 是单个目标交易的幂等键;同一批量送礼必须为每个目标派生独立值。
|
// CommandID 是单个目标交易的幂等键;同一批量送礼必须为每个目标派生独立值。
|
||||||
CommandID string
|
CommandID string
|
||||||
// TargetUserID 是本次入账 GIFT_POINT 的用户。
|
// TargetUserID 是本次收礼用户;GIFT_POINT 已下线,新送礼不会再给目标用户积分入账。
|
||||||
TargetUserID int64
|
TargetUserID int64
|
||||||
// TargetIsHost 只能由 gateway 注入,批量目标之间不能共享该身份快照。
|
// TargetIsHost 只能由 gateway 注入,批量目标之间不能共享该身份快照。
|
||||||
TargetIsHost bool
|
TargetIsHost bool
|
||||||
@ -157,11 +157,12 @@ type Receipt struct {
|
|||||||
CoinSpent int64
|
CoinSpent int64
|
||||||
ChargeAssetType string
|
ChargeAssetType string
|
||||||
ChargeAmount int64
|
ChargeAmount int64
|
||||||
GiftPointAdded int64
|
// GiftPointAdded 是历史回执字段,新送礼固定为 0;房间贡献和主播周期钻石只按真实扣费金额计算。
|
||||||
HeatValue int64
|
GiftPointAdded int64
|
||||||
GiftTypeCode string
|
HeatValue int64
|
||||||
PriceVersion string
|
GiftTypeCode string
|
||||||
BalanceAfter int64
|
PriceVersion string
|
||||||
|
BalanceAfter int64
|
||||||
// HostPeriodDiamondAdded 是本次送礼写入主播工资周期账户的钻石数;非主播恒为 0。
|
// HostPeriodDiamondAdded 是本次送礼写入主播工资周期账户的钻石数;非主播恒为 0。
|
||||||
HostPeriodDiamondAdded int64
|
HostPeriodDiamondAdded int64
|
||||||
// HostPeriodCycleKey 是工资周期键,当前按 UTC 月生成,后续结算按此键定位周期账户。
|
// HostPeriodCycleKey 是工资周期键,当前按 UTC 月生成,后续结算按此键定位周期账户。
|
||||||
@ -447,13 +448,14 @@ type GiftWallItem struct {
|
|||||||
TotalValue int64
|
TotalValue int64
|
||||||
TotalCoinValue int64
|
TotalCoinValue int64
|
||||||
TotalDiamondValue int64
|
TotalDiamondValue int64
|
||||||
TotalGiftPoint int64
|
// TotalGiftPoint 是历史礼物墙字段,新送礼不再累加,保留用于旧数据展示兼容。
|
||||||
TotalHeatValue int64
|
TotalGiftPoint int64
|
||||||
ChargeAssetType string
|
TotalHeatValue int64
|
||||||
LastPriceVersion string
|
ChargeAssetType string
|
||||||
FirstReceivedAtMS int64
|
LastPriceVersion string
|
||||||
LastReceivedAtMS int64
|
FirstReceivedAtMS int64
|
||||||
SortOrder int32
|
LastReceivedAtMS int64
|
||||||
|
SortOrder int32
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserGiftWall 汇总礼物墙首屏需要的全量统计和按礼物类型聚合列表。
|
// UserGiftWall 汇总礼物墙首屏需要的全量统计和按礼物类型聚合列表。
|
||||||
@ -464,8 +466,9 @@ type UserGiftWall struct {
|
|||||||
TotalValue int64
|
TotalValue int64
|
||||||
TotalCoinValue int64
|
TotalCoinValue int64
|
||||||
TotalDiamondValue int64
|
TotalDiamondValue int64
|
||||||
TotalGiftPoint int64
|
// TotalGiftPoint 是历史礼物墙汇总字段,新送礼不再累加,保留用于旧数据展示兼容。
|
||||||
TotalHeatValue int64
|
TotalGiftPoint int64
|
||||||
|
TotalHeatValue int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// RechargeProduct 是用户充值页读取的充值档位。
|
// RechargeProduct 是用户充值页读取的充值档位。
|
||||||
|
|||||||
@ -79,18 +79,19 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Resource struct {
|
type Resource struct {
|
||||||
AppCode string
|
AppCode string
|
||||||
ResourceID int64
|
ResourceID int64
|
||||||
ResourceCode string
|
ResourceCode string
|
||||||
ResourceType string
|
ResourceType string
|
||||||
Name string
|
Name string
|
||||||
Status string
|
Status string
|
||||||
Grantable bool
|
Grantable bool
|
||||||
GrantStrategy string
|
GrantStrategy string
|
||||||
WalletAssetType string
|
WalletAssetType string
|
||||||
WalletAssetAmount int64
|
WalletAssetAmount int64
|
||||||
PriceType string
|
PriceType string
|
||||||
CoinPrice int64
|
CoinPrice int64
|
||||||
|
// GiftPointAmount 是历史字段;GIFT_POINT 已下线,新资源和礼物配置只保留真实 COIN 价格。
|
||||||
GiftPointAmount int64
|
GiftPointAmount int64
|
||||||
UsageScopes []string
|
UsageScopes []string
|
||||||
AssetURL string
|
AssetURL string
|
||||||
@ -146,18 +147,19 @@ type GiftConfig struct {
|
|||||||
PresentationJSON string
|
PresentationJSON string
|
||||||
PriceVersion string
|
PriceVersion string
|
||||||
CoinPrice int64
|
CoinPrice int64
|
||||||
GiftPointAmount int64
|
// GiftPointAmount 是历史字段;送礼结算不再读取,新配置固定为 0。
|
||||||
HeatValue int64
|
GiftPointAmount int64
|
||||||
GiftTypeCode string
|
HeatValue int64
|
||||||
ChargeAssetType string
|
GiftTypeCode string
|
||||||
EffectiveFromMS int64
|
ChargeAssetType string
|
||||||
EffectiveToMS int64
|
EffectiveFromMS int64
|
||||||
EffectTypes []string
|
EffectiveToMS int64
|
||||||
CreatedByUserID int64
|
EffectTypes []string
|
||||||
UpdatedByUserID int64
|
CreatedByUserID int64
|
||||||
CreatedAtMS int64
|
UpdatedByUserID int64
|
||||||
UpdatedAtMS int64
|
CreatedAtMS int64
|
||||||
RegionIDs []int64
|
UpdatedAtMS int64
|
||||||
|
RegionIDs []int64
|
||||||
}
|
}
|
||||||
|
|
||||||
type GiftTypeConfig struct {
|
type GiftTypeConfig struct {
|
||||||
@ -249,18 +251,19 @@ type ListResourcesQuery struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ResourceCommand struct {
|
type ResourceCommand struct {
|
||||||
AppCode string
|
AppCode string
|
||||||
ResourceID int64
|
ResourceID int64
|
||||||
ResourceCode string
|
ResourceCode string
|
||||||
ResourceType string
|
ResourceType string
|
||||||
Name string
|
Name string
|
||||||
Status string
|
Status string
|
||||||
Grantable bool
|
Grantable bool
|
||||||
GrantStrategy string
|
GrantStrategy string
|
||||||
WalletAssetType string
|
WalletAssetType string
|
||||||
WalletAssetAmount int64
|
WalletAssetAmount int64
|
||||||
PriceType string
|
PriceType string
|
||||||
CoinPrice int64
|
CoinPrice int64
|
||||||
|
// GiftPointAmount 是历史字段;保存资源时固定为 0,保留字段只兼容旧表结构。
|
||||||
GiftPointAmount int64
|
GiftPointAmount int64
|
||||||
UsageScopes []string
|
UsageScopes []string
|
||||||
AssetURL string
|
AssetURL string
|
||||||
@ -315,6 +318,7 @@ type ListGiftConfigsQuery struct {
|
|||||||
AppCode string
|
AppCode string
|
||||||
Status string
|
Status string
|
||||||
Keyword string
|
Keyword string
|
||||||
|
GiftTypeCode string
|
||||||
Page int32
|
Page int32
|
||||||
PageSize int32
|
PageSize int32
|
||||||
ActiveOnly bool
|
ActiveOnly bool
|
||||||
@ -338,16 +342,17 @@ type GiftConfigCommand struct {
|
|||||||
PresentationJSON string
|
PresentationJSON string
|
||||||
PriceVersion string
|
PriceVersion string
|
||||||
CoinPrice int64
|
CoinPrice int64
|
||||||
GiftPointAmount int64
|
// GiftPointAmount 是历史字段;保存礼物价格时固定为 0,业务计算只读 CoinPrice。
|
||||||
HeatValue int64
|
GiftPointAmount int64
|
||||||
EffectiveAtMS int64
|
HeatValue int64
|
||||||
GiftTypeCode string
|
EffectiveAtMS int64
|
||||||
ChargeAssetType string
|
GiftTypeCode string
|
||||||
EffectiveFromMS int64
|
ChargeAssetType string
|
||||||
EffectiveToMS int64
|
EffectiveFromMS int64
|
||||||
EffectTypes []string
|
EffectiveToMS int64
|
||||||
OperatorUserID int64
|
EffectTypes []string
|
||||||
RegionIDs []int64
|
OperatorUserID int64
|
||||||
|
RegionIDs []int64
|
||||||
}
|
}
|
||||||
|
|
||||||
type GiftTypeConfigCommand struct {
|
type GiftTypeConfigCommand struct {
|
||||||
|
|||||||
@ -34,8 +34,8 @@ func walletOutboxMessageFromRecord(record mysqlstorage.WalletOutboxRecord) walle
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestDebitGiftUsesServerPriceAndCreditsGiftPoint 验证送礼只信服务端价格并产生双边分录。
|
// TestDebitGiftUsesServerPriceWithoutGiftPointCredit 验证送礼只信服务端价格,且礼物积分下线后不再给收礼人入账。
|
||||||
func TestDebitGiftUsesServerPriceAndCreditsGiftPoint(t *testing.T) {
|
func TestDebitGiftUsesServerPriceWithoutGiftPointCredit(t *testing.T) {
|
||||||
repository := mysqltest.NewRepository(t)
|
repository := mysqltest.NewRepository(t)
|
||||||
repository.SetBalance(10001, 100)
|
repository.SetBalance(10001, 100)
|
||||||
repository.SetGiftPrice("rose", "v9", 7, 3, 11)
|
repository.SetGiftPrice("rose", "v9", 7, 3, 11)
|
||||||
@ -54,7 +54,7 @@ func TestDebitGiftUsesServerPriceAndCreditsGiftPoint(t *testing.T) {
|
|||||||
t.Fatalf("DebitGift failed: %v", err)
|
t.Fatalf("DebitGift failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if receipt.CoinSpent != 14 || receipt.GiftPointAdded != 6 || receipt.HeatValue != 14 || receipt.BalanceAfter != 86 || receipt.PriceVersion != "v9" {
|
if receipt.CoinSpent != 14 || receipt.GiftPointAdded != 0 || receipt.HeatValue != 14 || receipt.BalanceAfter != 86 || receipt.PriceVersion != "v9" {
|
||||||
t.Fatalf("server price settlement mismatch: %+v", receipt)
|
t.Fatalf("server price settlement mismatch: %+v", receipt)
|
||||||
}
|
}
|
||||||
if receipt.TransactionID == "" || receipt.BillingReceiptID == "" {
|
if receipt.TransactionID == "" || receipt.BillingReceiptID == "" {
|
||||||
@ -72,14 +72,14 @@ func TestDebitGiftUsesServerPriceAndCreditsGiftPoint(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("GetBalances target failed: %v", err)
|
t.Fatalf("GetBalances target failed: %v", err)
|
||||||
}
|
}
|
||||||
if balanceAmount(balances, ledger.AssetGiftPoint) != 6 {
|
if balanceAmount(balances, ledger.AssetGiftPoint) != 0 {
|
||||||
t.Fatalf("target GIFT_POINT balance mismatch: %+v", balances)
|
t.Fatalf("target GIFT_POINT must stay zero after point removal: %+v", balances)
|
||||||
}
|
}
|
||||||
if got := repository.CountRows("wallet_entries", "transaction_id = ?", receipt.TransactionID); got != 2 {
|
if got := repository.CountRows("wallet_entries", "transaction_id = ?", receipt.TransactionID); got != 1 {
|
||||||
t.Fatalf("gift debit should write two entries, got %d", got)
|
t.Fatalf("gift debit should only write sender COIN entry, got %d", got)
|
||||||
}
|
}
|
||||||
if got := repository.CountRows("wallet_outbox", "transaction_id = ?", receipt.TransactionID); got != 3 {
|
if got := repository.CountRows("wallet_outbox", "transaction_id = ?", receipt.TransactionID); got != 2 {
|
||||||
t.Fatalf("gift debit should write balance and gift outbox events, got %d", got)
|
t.Fatalf("gift debit should write sender balance and gift outbox events, got %d", got)
|
||||||
}
|
}
|
||||||
if receipt.HostPeriodDiamondAdded != 0 || receipt.HostPeriodCycleKey != "" {
|
if receipt.HostPeriodDiamondAdded != 0 || receipt.HostPeriodCycleKey != "" {
|
||||||
t.Fatalf("non-host gift must not credit host period diamonds: %+v", receipt)
|
t.Fatalf("non-host gift must not credit host period diamonds: %+v", receipt)
|
||||||
@ -113,7 +113,7 @@ func TestBatchDebitGiftSettlesAllTargetsAtomically(t *testing.T) {
|
|||||||
t.Fatalf("BatchDebitGift failed: %v", err)
|
t.Fatalf("BatchDebitGift failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if receipt.Aggregate.CoinSpent != 28 || receipt.Aggregate.GiftPointAdded != 12 || receipt.Aggregate.HeatValue != 28 || receipt.Aggregate.BalanceAfter != 72 || len(receipt.Targets) != 2 {
|
if receipt.Aggregate.CoinSpent != 28 || receipt.Aggregate.GiftPointAdded != 0 || receipt.Aggregate.HeatValue != 28 || receipt.Aggregate.BalanceAfter != 72 || len(receipt.Targets) != 2 {
|
||||||
t.Fatalf("batch aggregate mismatch: %+v", receipt)
|
t.Fatalf("batch aggregate mismatch: %+v", receipt)
|
||||||
}
|
}
|
||||||
for _, userID := range []int64{10002, 10003} {
|
for _, userID := range []int64{10002, 10003} {
|
||||||
@ -121,8 +121,8 @@ func TestBatchDebitGiftSettlesAllTargetsAtomically(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("GetBalances target %d failed: %v", userID, err)
|
t.Fatalf("GetBalances target %d failed: %v", userID, err)
|
||||||
}
|
}
|
||||||
if balanceAmount(balances, ledger.AssetGiftPoint) != 6 {
|
if balanceAmount(balances, ledger.AssetGiftPoint) != 0 {
|
||||||
t.Fatalf("target %d GIFT_POINT mismatch: %+v", userID, balances)
|
t.Fatalf("target %d GIFT_POINT must stay zero after point removal: %+v", userID, balances)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
balances, err := svc.GetBalances(context.Background(), 10001, []string{ledger.AssetCoin})
|
balances, err := svc.GetBalances(context.Background(), 10001, []string{ledger.AssetCoin})
|
||||||
@ -135,8 +135,8 @@ func TestBatchDebitGiftSettlesAllTargetsAtomically(t *testing.T) {
|
|||||||
if got := repository.CountRows("wallet_transactions", "command_id IN (?, ?)", "cmd-gift-batch:target:10002", "cmd-gift-batch:target:10003"); got != 2 {
|
if got := repository.CountRows("wallet_transactions", "command_id IN (?, ?)", "cmd-gift-batch:target:10002", "cmd-gift-batch:target:10003"); got != 2 {
|
||||||
t.Fatalf("batch gift should write one transaction per target, got %d", got)
|
t.Fatalf("batch gift should write one transaction per target, got %d", got)
|
||||||
}
|
}
|
||||||
if got := repository.CountRows("wallet_entries", "transaction_id IN (?, ?)", receipt.Targets[0].Receipt.TransactionID, receipt.Targets[1].Receipt.TransactionID); got != 4 {
|
if got := repository.CountRows("wallet_entries", "transaction_id IN (?, ?)", receipt.Targets[0].Receipt.TransactionID, receipt.Targets[1].Receipt.TransactionID); got != 2 {
|
||||||
t.Fatalf("batch gift should write sender and target entries per target, got %d", got)
|
t.Fatalf("batch gift should only write sender COIN entries per target, got %d", got)
|
||||||
}
|
}
|
||||||
|
|
||||||
again, err := svc.BatchDebitGift(context.Background(), ledger.BatchDebitGiftCommand{
|
again, err := svc.BatchDebitGift(context.Background(), ledger.BatchDebitGiftCommand{
|
||||||
@ -303,7 +303,7 @@ func TestDebitGiftAppliesGlobalDefaultGiftDiamondRatioToHeatValueByGiftType(t *t
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("DebitGift %s failed: %v", tc.giftType, err)
|
t.Fatalf("DebitGift %s failed: %v", tc.giftType, err)
|
||||||
}
|
}
|
||||||
if receipt.HeatValue != tc.wantHeat || receipt.GiftPointAdded != 100 || receipt.CoinSpent != tc.coinPrice {
|
if receipt.HeatValue != tc.wantHeat || receipt.GiftPointAdded != 0 || receipt.CoinSpent != tc.coinPrice {
|
||||||
t.Fatalf("%s room contribution ratio mismatch: %+v want heat %d", tc.giftType, receipt, tc.wantHeat)
|
t.Fatalf("%s room contribution ratio mismatch: %+v want heat %d", tc.giftType, receipt, tc.wantHeat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -338,7 +338,7 @@ func TestBatchDebitGiftAppliesGlobalDefaultGiftDiamondRatioToEachTargetHeatValue
|
|||||||
t.Fatalf("batch aggregate room contribution ratio mismatch: %+v", receipt)
|
t.Fatalf("batch aggregate room contribution ratio mismatch: %+v", receipt)
|
||||||
}
|
}
|
||||||
for _, target := range receipt.Targets {
|
for _, target := range receipt.Targets {
|
||||||
if target.Receipt.HeatValue != 10 || target.Receipt.GiftPointAdded != 100 {
|
if target.Receipt.HeatValue != 10 || target.Receipt.GiftPointAdded != 0 {
|
||||||
t.Fatalf("target room contribution ratio mismatch: %+v", target)
|
t.Fatalf("target room contribution ratio mismatch: %+v", target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1003,7 +1003,7 @@ func TestHostSalaryHalfMonthSettlementUsesPolicyMode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestDebitGiftAllowsSelfGift 验证用户可以给自己送礼,扣 COIN 和收 GIFT_POINT 仍然分别落账。
|
// TestDebitGiftAllowsSelfGift 验证用户可以给自己送礼;礼物积分下线后只扣 COIN,不再回加 GIFT_POINT。
|
||||||
func TestDebitGiftAllowsSelfGift(t *testing.T) {
|
func TestDebitGiftAllowsSelfGift(t *testing.T) {
|
||||||
repository := mysqltest.NewRepository(t)
|
repository := mysqltest.NewRepository(t)
|
||||||
repository.SetBalance(10001, 100)
|
repository.SetBalance(10001, 100)
|
||||||
@ -1023,18 +1023,18 @@ func TestDebitGiftAllowsSelfGift(t *testing.T) {
|
|||||||
t.Fatalf("self DebitGift failed: %v", err)
|
t.Fatalf("self DebitGift failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if receipt.CoinSpent != 14 || receipt.GiftPointAdded != 6 || receipt.BalanceAfter != 86 {
|
if receipt.CoinSpent != 14 || receipt.GiftPointAdded != 0 || receipt.BalanceAfter != 86 {
|
||||||
t.Fatalf("self gift settlement mismatch: %+v", receipt)
|
t.Fatalf("self gift settlement mismatch: %+v", receipt)
|
||||||
}
|
}
|
||||||
balances, err := svc.GetBalances(context.Background(), 10001, []string{ledger.AssetCoin, ledger.AssetGiftPoint})
|
balances, err := svc.GetBalances(context.Background(), 10001, []string{ledger.AssetCoin, ledger.AssetGiftPoint})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("GetBalances failed: %v", err)
|
t.Fatalf("GetBalances failed: %v", err)
|
||||||
}
|
}
|
||||||
if balanceAmount(balances, ledger.AssetCoin) != 86 || balanceAmount(balances, ledger.AssetGiftPoint) != 6 {
|
if balanceAmount(balances, ledger.AssetCoin) != 86 || balanceAmount(balances, ledger.AssetGiftPoint) != 0 {
|
||||||
t.Fatalf("self gift balances mismatch: %+v", balances)
|
t.Fatalf("self gift balances mismatch: %+v", balances)
|
||||||
}
|
}
|
||||||
if got := repository.CountRows("wallet_entries", "transaction_id = ?", receipt.TransactionID); got != 2 {
|
if got := repository.CountRows("wallet_entries", "transaction_id = ?", receipt.TransactionID); got != 1 {
|
||||||
t.Fatalf("self gift should still write debit and credit entries, got %d", got)
|
t.Fatalf("self gift should only write debit entry, got %d", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2159,7 +2159,7 @@ func TestGiftConfigMustSelectGiftResource(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("DebitGift with resource-backed gift failed: %v", err)
|
t.Fatalf("DebitGift with resource-backed gift failed: %v", err)
|
||||||
}
|
}
|
||||||
if receipt.CoinSpent != 10 || receipt.GiftPointAdded != 4 || receipt.HeatValue != 10 {
|
if receipt.CoinSpent != 10 || receipt.GiftPointAdded != 0 || receipt.HeatValue != 10 {
|
||||||
t.Fatalf("resource-backed gift settlement mismatch: %+v", receipt)
|
t.Fatalf("resource-backed gift settlement mismatch: %+v", receipt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2244,8 +2244,8 @@ func TestGiftConfigSyncsConfiguredResourcePrice(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("create priced gift resource failed: %v", err)
|
t.Fatalf("create priced gift resource failed: %v", err)
|
||||||
}
|
}
|
||||||
if paidResource.GiftPointAmount != 77 {
|
if paidResource.GiftPointAmount != 0 {
|
||||||
t.Fatalf("resource gift point should sync coin price: %+v", paidResource)
|
t.Fatalf("resource gift point should stay disabled: %+v", paidResource)
|
||||||
}
|
}
|
||||||
gift, err := svc.CreateGiftConfig(ctx, resourcedomain.GiftConfigCommand{
|
gift, err := svc.CreateGiftConfig(ctx, resourcedomain.GiftConfigCommand{
|
||||||
GiftID: "price-sync",
|
GiftID: "price-sync",
|
||||||
@ -2264,7 +2264,7 @@ func TestGiftConfigSyncsConfiguredResourcePrice(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("create synced gift config failed: %v", err)
|
t.Fatalf("create synced gift config failed: %v", err)
|
||||||
}
|
}
|
||||||
if gift.ChargeAssetType != ledger.AssetCoin || gift.CoinPrice != 77 || gift.GiftPointAmount != 77 {
|
if gift.ChargeAssetType != ledger.AssetCoin || gift.CoinPrice != 77 || gift.GiftPointAmount != 0 {
|
||||||
t.Fatalf("gift price should be overridden by resource price: %+v", gift)
|
t.Fatalf("gift price should be overridden by resource price: %+v", gift)
|
||||||
}
|
}
|
||||||
updatedResource, err := svc.UpdateResource(ctx, resourcedomain.ResourceCommand{
|
updatedResource, err := svc.UpdateResource(ctx, resourcedomain.ResourceCommand{
|
||||||
@ -2283,7 +2283,7 @@ func TestGiftConfigSyncsConfiguredResourcePrice(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("update priced gift resource failed: %v", err)
|
t.Fatalf("update priced gift resource failed: %v", err)
|
||||||
}
|
}
|
||||||
if updatedResource.CoinPrice != 88 || updatedResource.GiftPointAmount != 88 {
|
if updatedResource.CoinPrice != 88 || updatedResource.GiftPointAmount != 0 {
|
||||||
t.Fatalf("updated resource price mismatch: %+v", updatedResource)
|
t.Fatalf("updated resource price mismatch: %+v", updatedResource)
|
||||||
}
|
}
|
||||||
repository.SetBalance(54001, 100)
|
repository.SetBalance(54001, 100)
|
||||||
@ -2298,7 +2298,7 @@ func TestGiftConfigSyncsConfiguredResourcePrice(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("debit gift after resource price update failed: %v", err)
|
t.Fatalf("debit gift after resource price update failed: %v", err)
|
||||||
}
|
}
|
||||||
if updatedReceipt.CoinSpent != 88 || updatedReceipt.GiftPointAdded != 88 {
|
if updatedReceipt.CoinSpent != 88 || updatedReceipt.GiftPointAdded != 0 {
|
||||||
t.Fatalf("gift price should follow updated resource price: %+v", updatedReceipt)
|
t.Fatalf("gift price should follow updated resource price: %+v", updatedReceipt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -173,7 +173,7 @@ func (r *Repository) Ping(ctx context.Context) error {
|
|||||||
return r.db.PingContext(ctx)
|
return r.db.PingContext(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DebitGift 在一个 MySQL 事务内完成 sender 扣费、target GIFT_POINT 入账、主播周期钻石入账、分录和 outbox。
|
// DebitGift 在一个 MySQL 事务内完成 sender 扣费、主播周期钻石入账、分录和 outbox;历史 GIFT_POINT 回执字段保留但不再入账。
|
||||||
func (r *Repository) DebitGift(ctx context.Context, command ledger.DebitGiftCommand) (ledger.Receipt, error) {
|
func (r *Repository) DebitGift(ctx context.Context, command ledger.DebitGiftCommand) (ledger.Receipt, error) {
|
||||||
if r == nil || r.db == nil {
|
if r == nil || r.db == nil {
|
||||||
return ledger.Receipt{}, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
return ledger.Receipt{}, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
||||||
@ -218,10 +218,7 @@ func (r *Repository) DebitGift(ctx context.Context, command ledger.DebitGiftComm
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return ledger.Receipt{}, err
|
return ledger.Receipt{}, err
|
||||||
}
|
}
|
||||||
giftPointAdded, err := checkedMul(price.GiftPointAmount, int64(command.GiftCount))
|
// 礼物积分已经从运营配置和收礼收益里下线;回执字段只保留 0,兼容旧客户端和历史事件解析。
|
||||||
if err != nil {
|
|
||||||
return ledger.Receipt{}, err
|
|
||||||
}
|
|
||||||
roomContributionRatio, roomContributionRatioRegionID, err := r.resolveGlobalGiftDiamondRatio(ctx, tx, command.AppCode, giftConfig.GiftTypeCode)
|
roomContributionRatio, roomContributionRatioRegionID, err := r.resolveGlobalGiftDiamondRatio(ctx, tx, command.AppCode, giftConfig.GiftTypeCode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ledger.Receipt{}, err
|
return ledger.Receipt{}, err
|
||||||
@ -240,7 +237,7 @@ func (r *Repository) DebitGift(ctx context.Context, command ledger.DebitGiftComm
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return ledger.Receipt{}, err
|
return ledger.Receipt{}, err
|
||||||
}
|
}
|
||||||
// 非主播不会进入该分支,因此普通用户收礼只增加 GIFT_POINT,不产生工资周期积分。
|
// 非主播不会进入该分支,因此普通用户收礼不会产生工资周期钻石。
|
||||||
hostPeriodDiamondAdded, err = giftDiamondAmount(chargeAmount, ratio.PPM)
|
hostPeriodDiamondAdded, err = giftDiamondAmount(chargeAmount, ratio.PPM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ledger.Receipt{}, err
|
return ledger.Receipt{}, err
|
||||||
@ -257,11 +254,6 @@ func (r *Repository) DebitGift(ctx context.Context, command ledger.DebitGiftComm
|
|||||||
if sender.AvailableAmount < chargeAmount {
|
if sender.AvailableAmount < chargeAmount {
|
||||||
return ledger.Receipt{}, xerr.New(xerr.InsufficientBalance, "insufficient balance")
|
return ledger.Receipt{}, xerr.New(xerr.InsufficientBalance, "insufficient balance")
|
||||||
}
|
}
|
||||||
target, err := r.lockAccount(ctx, tx, command.TargetUserID, ledger.AssetGiftPoint, true, nowMs)
|
|
||||||
if err != nil {
|
|
||||||
return ledger.Receipt{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
transactionID := transactionID(command.AppCode, command.CommandID)
|
transactionID := transactionID(command.AppCode, command.CommandID)
|
||||||
metadata := giftMetadata{
|
metadata := giftMetadata{
|
||||||
AppCode: command.AppCode,
|
AppCode: command.AppCode,
|
||||||
@ -277,7 +269,7 @@ func (r *Repository) DebitGift(ctx context.Context, command ledger.DebitGiftComm
|
|||||||
ChargeAssetType: price.ChargeAssetType,
|
ChargeAssetType: price.ChargeAssetType,
|
||||||
ChargeAmount: chargeAmount,
|
ChargeAmount: chargeAmount,
|
||||||
CoinSpent: chargeAmount,
|
CoinSpent: chargeAmount,
|
||||||
GiftPointAdded: giftPointAdded,
|
GiftPointAdded: 0,
|
||||||
HeatValue: heatValue,
|
HeatValue: heatValue,
|
||||||
BalanceAfter: sender.AvailableAmount - chargeAmount,
|
BalanceAfter: sender.AvailableAmount - chargeAmount,
|
||||||
BillingReceipt: billingReceiptID(command.AppCode, command.CommandID),
|
BillingReceipt: billingReceiptID(command.AppCode, command.CommandID),
|
||||||
@ -296,7 +288,7 @@ func (r *Repository) DebitGift(ctx context.Context, command ledger.DebitGiftComm
|
|||||||
RoomContributionRatioPercent: roomContributionRatio.Percent,
|
RoomContributionRatioPercent: roomContributionRatio.Percent,
|
||||||
RoomContributionRatioRegionID: roomContributionRatioRegionID,
|
RoomContributionRatioRegionID: roomContributionRatioRegionID,
|
||||||
CoinPrice: price.CoinPrice,
|
CoinPrice: price.CoinPrice,
|
||||||
GiftPointAmount: price.GiftPointAmount,
|
GiftPointAmount: 0,
|
||||||
HeatUnitValue: price.CoinPrice,
|
HeatUnitValue: price.CoinPrice,
|
||||||
}
|
}
|
||||||
if err := r.insertTransaction(ctx, tx, transactionID, command.CommandID, bizTypeGiftDebit, requestHash, fmt.Sprintf("%s:%s", command.GiftID, price.PriceVersion), metadata, nowMs); err != nil {
|
if err := r.insertTransaction(ctx, tx, transactionID, command.CommandID, bizTypeGiftDebit, requestHash, fmt.Sprintf("%s:%s", command.GiftID, price.PriceVersion), metadata, nowMs); err != nil {
|
||||||
@ -320,26 +312,8 @@ func (r *Repository) DebitGift(ctx context.Context, command ledger.DebitGiftComm
|
|||||||
}); err != nil {
|
}); err != nil {
|
||||||
return ledger.Receipt{}, err
|
return ledger.Receipt{}, err
|
||||||
}
|
}
|
||||||
if err := r.applyAccountDelta(ctx, tx, target, giftPointAdded, 0, nowMs); err != nil {
|
|
||||||
return ledger.Receipt{}, err
|
|
||||||
}
|
|
||||||
targetAfter := target.AvailableAmount + giftPointAdded
|
|
||||||
if err := r.insertEntry(ctx, tx, walletEntry{
|
|
||||||
TransactionID: transactionID,
|
|
||||||
UserID: command.TargetUserID,
|
|
||||||
AssetType: ledger.AssetGiftPoint,
|
|
||||||
AvailableDelta: giftPointAdded,
|
|
||||||
FrozenDelta: 0,
|
|
||||||
AvailableAfter: targetAfter,
|
|
||||||
FrozenAfter: target.FrozenAmount,
|
|
||||||
CounterpartyUserID: command.SenderUserID,
|
|
||||||
RoomID: command.RoomID,
|
|
||||||
CreatedAtMS: nowMs,
|
|
||||||
}); err != nil {
|
|
||||||
return ledger.Receipt{}, err
|
|
||||||
}
|
|
||||||
if hostPeriodDiamondAdded > 0 {
|
if hostPeriodDiamondAdded > 0 {
|
||||||
// 周期钻石与送礼扣费同事务提交;如果后续任一步失败,用户扣币、主播礼物积分和工资积分会一起回滚。
|
// 周期钻石与送礼扣费同事务提交;如果后续任一步失败,用户扣币和主播工资周期钻石会一起回滚。
|
||||||
hostPeriodDiamondAfter, hostPeriodDiamondVersion, err := r.creditHostPeriodDiamonds(ctx, tx, transactionID, command.CommandID, metadata, nowMs)
|
hostPeriodDiamondAfter, hostPeriodDiamondVersion, err := r.creditHostPeriodDiamonds(ctx, tx, transactionID, command.CommandID, metadata, nowMs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ledger.Receipt{}, err
|
return ledger.Receipt{}, err
|
||||||
@ -350,7 +324,6 @@ func (r *Repository) DebitGift(ctx context.Context, command ledger.DebitGiftComm
|
|||||||
|
|
||||||
events := []walletOutboxEvent{
|
events := []walletOutboxEvent{
|
||||||
balanceChangedEvent(transactionID, command.CommandID, command.SenderUserID, price.ChargeAssetType, -chargeAmount, 0, senderAfter, sender.FrozenAmount, sender.Version+1, metadata, nowMs),
|
balanceChangedEvent(transactionID, command.CommandID, command.SenderUserID, price.ChargeAssetType, -chargeAmount, 0, senderAfter, sender.FrozenAmount, sender.Version+1, metadata, nowMs),
|
||||||
balanceChangedEvent(transactionID, command.CommandID, command.TargetUserID, ledger.AssetGiftPoint, giftPointAdded, 0, targetAfter, target.FrozenAmount, target.Version+1, metadata, nowMs),
|
|
||||||
giftDebitedEvent(transactionID, command.CommandID, command.SenderUserID, price.ChargeAssetType, -chargeAmount, 0, metadata, nowMs),
|
giftDebitedEvent(transactionID, command.CommandID, command.SenderUserID, price.ChargeAssetType, -chargeAmount, 0, metadata, nowMs),
|
||||||
}
|
}
|
||||||
if hostPeriodDiamondAdded > 0 {
|
if hostPeriodDiamondAdded > 0 {
|
||||||
@ -402,10 +375,7 @@ func (r *Repository) BatchDebitGift(ctx context.Context, command ledger.BatchDeb
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return ledger.BatchGiftReceipt{}, err
|
return ledger.BatchGiftReceipt{}, err
|
||||||
}
|
}
|
||||||
giftPointAdded, err := checkedMul(price.GiftPointAmount, int64(command.GiftCount))
|
// 礼物积分已经下线;批量回执仍保留字段,但每个目标都固定返回 0。
|
||||||
if err != nil {
|
|
||||||
return ledger.BatchGiftReceipt{}, err
|
|
||||||
}
|
|
||||||
roomContributionRatio, roomContributionRatioRegionID, err := r.resolveGlobalGiftDiamondRatio(ctx, tx, command.AppCode, giftConfig.GiftTypeCode)
|
roomContributionRatio, roomContributionRatioRegionID, err := r.resolveGlobalGiftDiamondRatio(ctx, tx, command.AppCode, giftConfig.GiftTypeCode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ledger.BatchGiftReceipt{}, err
|
return ledger.BatchGiftReceipt{}, err
|
||||||
@ -466,11 +436,6 @@ func (r *Repository) BatchDebitGift(ctx context.Context, command ledger.BatchDeb
|
|||||||
return ledger.BatchGiftReceipt{}, xerr.New(xerr.InsufficientBalance, "insufficient balance")
|
return ledger.BatchGiftReceipt{}, xerr.New(xerr.InsufficientBalance, "insufficient balance")
|
||||||
}
|
}
|
||||||
|
|
||||||
targetAccounts, err := r.lockGiftTargetAccounts(ctx, tx, command.Targets, nowMs)
|
|
||||||
if err != nil {
|
|
||||||
return ledger.BatchGiftReceipt{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
anyHostTarget := false
|
anyHostTarget := false
|
||||||
for _, target := range command.Targets {
|
for _, target := range command.Targets {
|
||||||
if target.TargetIsHost {
|
if target.TargetIsHost {
|
||||||
@ -487,7 +452,7 @@ func (r *Repository) BatchDebitGift(ctx context.Context, command ledger.BatchDeb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
events := make([]walletOutboxEvent, 0, len(command.Targets)*4)
|
events := make([]walletOutboxEvent, 0, len(command.Targets)*3)
|
||||||
targetReceipts := make([]ledger.BatchGiftTargetReceipt, 0, len(command.Targets))
|
targetReceipts := make([]ledger.BatchGiftTargetReceipt, 0, len(command.Targets))
|
||||||
for _, target := range command.Targets {
|
for _, target := range command.Targets {
|
||||||
hostPeriodDiamondAdded := int64(0)
|
hostPeriodDiamondAdded := int64(0)
|
||||||
@ -521,7 +486,7 @@ func (r *Repository) BatchDebitGift(ctx context.Context, command ledger.BatchDeb
|
|||||||
ChargeAssetType: price.ChargeAssetType,
|
ChargeAssetType: price.ChargeAssetType,
|
||||||
ChargeAmount: chargeAmount,
|
ChargeAmount: chargeAmount,
|
||||||
CoinSpent: chargeAmount,
|
CoinSpent: chargeAmount,
|
||||||
GiftPointAdded: giftPointAdded,
|
GiftPointAdded: 0,
|
||||||
HeatValue: heatValue,
|
HeatValue: heatValue,
|
||||||
BalanceAfter: senderAfter,
|
BalanceAfter: senderAfter,
|
||||||
BillingReceipt: billingReceiptID(command.AppCode, target.CommandID),
|
BillingReceipt: billingReceiptID(command.AppCode, target.CommandID),
|
||||||
@ -540,7 +505,7 @@ func (r *Repository) BatchDebitGift(ctx context.Context, command ledger.BatchDeb
|
|||||||
RoomContributionRatioPercent: roomContributionRatio.Percent,
|
RoomContributionRatioPercent: roomContributionRatio.Percent,
|
||||||
RoomContributionRatioRegionID: roomContributionRatioRegionID,
|
RoomContributionRatioRegionID: roomContributionRatioRegionID,
|
||||||
CoinPrice: price.CoinPrice,
|
CoinPrice: price.CoinPrice,
|
||||||
GiftPointAmount: price.GiftPointAmount,
|
GiftPointAmount: 0,
|
||||||
HeatUnitValue: price.CoinPrice,
|
HeatUnitValue: price.CoinPrice,
|
||||||
}
|
}
|
||||||
if err := r.insertTransaction(ctx, tx, transactionID, target.CommandID, bizTypeGiftDebit, debitRequestHash(debitGiftCommandFromBatchTarget(command, target)), fmt.Sprintf("%s:%s", command.GiftID, price.PriceVersion), metadata, nowMs); err != nil {
|
if err := r.insertTransaction(ctx, tx, transactionID, target.CommandID, bizTypeGiftDebit, debitRequestHash(debitGiftCommandFromBatchTarget(command, target)), fmt.Sprintf("%s:%s", command.GiftID, price.PriceVersion), metadata, nowMs); err != nil {
|
||||||
@ -553,16 +518,6 @@ func (r *Repository) BatchDebitGift(ctx context.Context, command ledger.BatchDeb
|
|||||||
sender.AvailableAmount = senderAfter
|
sender.AvailableAmount = senderAfter
|
||||||
sender.Version++
|
sender.Version++
|
||||||
|
|
||||||
targetAccount := targetAccounts[target.TargetUserID]
|
|
||||||
if err := r.applyAccountDelta(ctx, tx, targetAccount, giftPointAdded, 0, nowMs); err != nil {
|
|
||||||
return ledger.BatchGiftReceipt{}, err
|
|
||||||
}
|
|
||||||
targetAfter := targetAccount.AvailableAmount + giftPointAdded
|
|
||||||
events = append(events, balanceChangedEvent(transactionID, target.CommandID, target.TargetUserID, ledger.AssetGiftPoint, giftPointAdded, 0, targetAfter, targetAccount.FrozenAmount, targetAccount.Version+1, metadata, nowMs))
|
|
||||||
targetAccount.AvailableAmount = targetAfter
|
|
||||||
targetAccount.Version++
|
|
||||||
targetAccounts[target.TargetUserID] = targetAccount
|
|
||||||
|
|
||||||
if err := r.insertEntry(ctx, tx, walletEntry{
|
if err := r.insertEntry(ctx, tx, walletEntry{
|
||||||
TransactionID: transactionID,
|
TransactionID: transactionID,
|
||||||
UserID: command.SenderUserID,
|
UserID: command.SenderUserID,
|
||||||
@ -577,20 +532,6 @@ func (r *Repository) BatchDebitGift(ctx context.Context, command ledger.BatchDeb
|
|||||||
}); err != nil {
|
}); err != nil {
|
||||||
return ledger.BatchGiftReceipt{}, err
|
return ledger.BatchGiftReceipt{}, err
|
||||||
}
|
}
|
||||||
if err := r.insertEntry(ctx, tx, walletEntry{
|
|
||||||
TransactionID: transactionID,
|
|
||||||
UserID: target.TargetUserID,
|
|
||||||
AssetType: ledger.AssetGiftPoint,
|
|
||||||
AvailableDelta: giftPointAdded,
|
|
||||||
FrozenDelta: 0,
|
|
||||||
AvailableAfter: targetAfter,
|
|
||||||
FrozenAfter: targetAccount.FrozenAmount,
|
|
||||||
CounterpartyUserID: command.SenderUserID,
|
|
||||||
RoomID: command.RoomID,
|
|
||||||
CreatedAtMS: nowMs,
|
|
||||||
}); err != nil {
|
|
||||||
return ledger.BatchGiftReceipt{}, err
|
|
||||||
}
|
|
||||||
if hostPeriodDiamondAdded > 0 {
|
if hostPeriodDiamondAdded > 0 {
|
||||||
hostPeriodDiamondAfter, hostPeriodDiamondVersion, err := r.creditHostPeriodDiamonds(ctx, tx, transactionID, target.CommandID, metadata, nowMs)
|
hostPeriodDiamondAfter, hostPeriodDiamondVersion, err := r.creditHostPeriodDiamonds(ctx, tx, transactionID, target.CommandID, metadata, nowMs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -742,7 +683,7 @@ func (r *Repository) upsertUserGiftWall(ctx context.Context, tx *sql.Tx, metadat
|
|||||||
chargeAmount,
|
chargeAmount,
|
||||||
totalCoinValue,
|
totalCoinValue,
|
||||||
totalDiamondValue,
|
totalDiamondValue,
|
||||||
metadata.GiftPointAdded,
|
0,
|
||||||
metadata.HeatValue,
|
metadata.HeatValue,
|
||||||
chargeAssetType,
|
chargeAssetType,
|
||||||
metadata.PriceVersion,
|
metadata.PriceVersion,
|
||||||
@ -1675,6 +1616,7 @@ type giftPrice struct {
|
|||||||
PriceVersion string
|
PriceVersion string
|
||||||
ChargeAssetType string
|
ChargeAssetType string
|
||||||
CoinPrice int64
|
CoinPrice int64
|
||||||
|
// GiftPointAmount 是 wallet_gift_prices 的历史列;送礼结算只按 CoinPrice 和比例计算,不再读取它做收益。
|
||||||
GiftPointAmount int64
|
GiftPointAmount int64
|
||||||
HeatValue int64
|
HeatValue int64
|
||||||
}
|
}
|
||||||
@ -1716,7 +1658,7 @@ func (r *Repository) resolveGiftPrice(ctx context.Context, tx *sql.Tx, giftID st
|
|||||||
}
|
}
|
||||||
return giftPrice{}, err
|
return giftPrice{}, err
|
||||||
}
|
}
|
||||||
if price.CoinPrice < 0 || price.GiftPointAmount < 0 || price.HeatValue < 0 {
|
if price.CoinPrice < 0 || price.HeatValue < 0 {
|
||||||
return giftPrice{}, xerr.New(xerr.Internal, "gift price is invalid")
|
return giftPrice{}, xerr.New(xerr.Internal, "gift price is invalid")
|
||||||
}
|
}
|
||||||
price.ChargeAssetType = ledger.NormalizeGiftChargeAssetType(price.ChargeAssetType)
|
price.ChargeAssetType = ledger.NormalizeGiftChargeAssetType(price.ChargeAssetType)
|
||||||
@ -2537,22 +2479,24 @@ func (r *Repository) balanceAfterTransaction(ctx context.Context, tx *sql.Tx, tr
|
|||||||
}
|
}
|
||||||
|
|
||||||
type giftMetadata struct {
|
type giftMetadata struct {
|
||||||
AppCode string `json:"app_code"`
|
AppCode string `json:"app_code"`
|
||||||
GiftID string `json:"gift_id"`
|
GiftID string `json:"gift_id"`
|
||||||
GiftName string `json:"gift_name"`
|
GiftName string `json:"gift_name"`
|
||||||
ResourceID int64 `json:"resource_id"`
|
ResourceID int64 `json:"resource_id"`
|
||||||
ResourceSnapshot string `json:"resource_snapshot_json"`
|
ResourceSnapshot string `json:"resource_snapshot_json"`
|
||||||
GiftTypeCode string `json:"gift_type_code"`
|
GiftTypeCode string `json:"gift_type_code"`
|
||||||
PresentationJSON string `json:"presentation_json"`
|
PresentationJSON string `json:"presentation_json"`
|
||||||
SortOrder int32 `json:"sort_order"`
|
SortOrder int32 `json:"sort_order"`
|
||||||
GiftCount int32 `json:"gift_count"`
|
GiftCount int32 `json:"gift_count"`
|
||||||
PriceVersion string `json:"price_version"`
|
PriceVersion string `json:"price_version"`
|
||||||
CoinPrice int64 `json:"coin_price"`
|
CoinPrice int64 `json:"coin_price"`
|
||||||
GiftPointAmount int64 `json:"gift_point_amount"`
|
// GiftPointAmount 是历史礼物积分单价字段,新送礼固定写 0,业务统计只读真实扣费和热度。
|
||||||
HeatUnitValue int64 `json:"heat_unit_value"`
|
GiftPointAmount int64 `json:"gift_point_amount"`
|
||||||
ChargeAssetType string `json:"charge_asset_type"`
|
HeatUnitValue int64 `json:"heat_unit_value"`
|
||||||
ChargeAmount int64 `json:"charge_amount"`
|
ChargeAssetType string `json:"charge_asset_type"`
|
||||||
CoinSpent int64 `json:"coin_spent"`
|
ChargeAmount int64 `json:"charge_amount"`
|
||||||
|
CoinSpent int64 `json:"coin_spent"`
|
||||||
|
// GiftPointAdded 是历史收礼积分字段,新送礼固定写 0,保留 JSON 字段只为旧事件解析兼容。
|
||||||
GiftPointAdded int64 `json:"gift_point_added"`
|
GiftPointAdded int64 `json:"gift_point_added"`
|
||||||
HeatValue int64 `json:"heat_value"`
|
HeatValue int64 `json:"heat_value"`
|
||||||
BalanceAfter int64 `json:"balance_after"`
|
BalanceAfter int64 `json:"balance_after"`
|
||||||
@ -2711,7 +2655,7 @@ func receiptFromGiftMetadata(transactionID string, metadata giftMetadata) ledger
|
|||||||
CoinSpent: metadata.CoinSpent,
|
CoinSpent: metadata.CoinSpent,
|
||||||
ChargeAssetType: chargeAssetType,
|
ChargeAssetType: chargeAssetType,
|
||||||
ChargeAmount: chargeAmount,
|
ChargeAmount: chargeAmount,
|
||||||
GiftPointAdded: metadata.GiftPointAdded,
|
GiftPointAdded: 0,
|
||||||
HeatValue: metadata.HeatValue,
|
HeatValue: metadata.HeatValue,
|
||||||
GiftTypeCode: metadata.GiftTypeCode,
|
GiftTypeCode: metadata.GiftTypeCode,
|
||||||
PriceVersion: metadata.PriceVersion,
|
PriceVersion: metadata.PriceVersion,
|
||||||
@ -3207,31 +3151,6 @@ func debitGiftCommandFromBatchTarget(command ledger.BatchDebitGiftCommand, targe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Repository) lockGiftTargetAccounts(ctx context.Context, tx *sql.Tx, targets []ledger.DebitGiftTargetCommand, nowMs int64) (map[int64]walletAccount, error) {
|
|
||||||
ids := make([]int64, 0, len(targets))
|
|
||||||
seen := make(map[int64]struct{}, len(targets))
|
|
||||||
for _, target := range targets {
|
|
||||||
if _, exists := seen[target.TargetUserID]; exists {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
seen[target.TargetUserID] = struct{}{}
|
|
||||||
ids = append(ids, target.TargetUserID)
|
|
||||||
}
|
|
||||||
sort.Slice(ids, func(left, right int) bool {
|
|
||||||
return ids[left] < ids[right]
|
|
||||||
})
|
|
||||||
accounts := make(map[int64]walletAccount, len(ids))
|
|
||||||
for _, targetUserID := range ids {
|
|
||||||
// 固定按 user_id 升序锁目标账户,降低并发批量送礼互相等待时形成死锁的概率。
|
|
||||||
account, err := r.lockAccount(ctx, tx, targetUserID, ledger.AssetGiftPoint, true, nowMs)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
accounts[targetUserID] = account
|
|
||||||
}
|
|
||||||
return accounts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func aggregateGiftReceipts(targets []ledger.BatchGiftTargetReceipt) (ledger.Receipt, error) {
|
func aggregateGiftReceipts(targets []ledger.BatchGiftTargetReceipt) (ledger.Receipt, error) {
|
||||||
aggregate := ledger.Receipt{}
|
aggregate := ledger.Receipt{}
|
||||||
billingReceiptIDs := make([]string, 0, len(targets))
|
billingReceiptIDs := make([]string, 0, len(targets))
|
||||||
@ -3251,9 +3170,6 @@ func aggregateGiftReceipts(targets []ledger.BatchGiftTargetReceipt) (ledger.Rece
|
|||||||
if aggregate.ChargeAmount, err = checkedAdd(aggregate.ChargeAmount, receipt.ChargeAmount); err != nil {
|
if aggregate.ChargeAmount, err = checkedAdd(aggregate.ChargeAmount, receipt.ChargeAmount); err != nil {
|
||||||
return ledger.Receipt{}, err
|
return ledger.Receipt{}, err
|
||||||
}
|
}
|
||||||
if aggregate.GiftPointAdded, err = checkedAdd(aggregate.GiftPointAdded, receipt.GiftPointAdded); err != nil {
|
|
||||||
return ledger.Receipt{}, err
|
|
||||||
}
|
|
||||||
if aggregate.HeatValue, err = checkedAdd(aggregate.HeatValue, receipt.HeatValue); err != nil {
|
if aggregate.HeatValue, err = checkedAdd(aggregate.HeatValue, receipt.HeatValue); err != nil {
|
||||||
return ledger.Receipt{}, err
|
return ledger.Receipt{}, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -292,11 +292,11 @@ func (r *Repository) syncGiftPricesForResourceTx(ctx context.Context, tx *sql.Tx
|
|||||||
}
|
}
|
||||||
|
|
||||||
coinPrice := int64(0)
|
coinPrice := int64(0)
|
||||||
giftPointAmount := int64(0)
|
|
||||||
if priceType == resourcedomain.PriceTypeCoin {
|
if priceType == resourcedomain.PriceTypeCoin {
|
||||||
coinPrice = resource.CoinPrice
|
coinPrice = resource.CoinPrice
|
||||||
giftPointAmount = resource.GiftPointAmount
|
|
||||||
}
|
}
|
||||||
|
// gift_point_amount 是历史字段,资源改价同步礼物价格时固定写 0,避免旧积分口径重新进入送礼链路。
|
||||||
|
giftPointAmount := int64(0)
|
||||||
if _, err := tx.ExecContext(ctx, `
|
if _, err := tx.ExecContext(ctx, `
|
||||||
UPDATE wallet_gift_prices gp
|
UPDATE wallet_gift_prices gp
|
||||||
JOIN gift_configs gc ON gc.app_code = gp.app_code AND gc.gift_id = gp.gift_id
|
JOIN gift_configs gc ON gc.app_code = gp.app_code AND gc.gift_id = gp.gift_id
|
||||||
@ -627,7 +627,7 @@ func (r *Repository) ListGiftConfigs(ctx context.Context, query resourcedomain.L
|
|||||||
item.PriceVersion = price.PriceVersion
|
item.PriceVersion = price.PriceVersion
|
||||||
item.ChargeAssetType = price.ChargeAssetType
|
item.ChargeAssetType = price.ChargeAssetType
|
||||||
item.CoinPrice = price.CoinPrice
|
item.CoinPrice = price.CoinPrice
|
||||||
item.GiftPointAmount = price.GiftPointAmount
|
item.GiftPointAmount = 0
|
||||||
item.HeatValue = price.CoinPrice
|
item.HeatValue = price.CoinPrice
|
||||||
}
|
}
|
||||||
items = append(items, item)
|
items = append(items, item)
|
||||||
@ -2006,7 +2006,8 @@ func (r *Repository) getGiftConfig(ctx context.Context, giftID string) (resource
|
|||||||
gift.PriceVersion = price.PriceVersion
|
gift.PriceVersion = price.PriceVersion
|
||||||
gift.ChargeAssetType = price.ChargeAssetType
|
gift.ChargeAssetType = price.ChargeAssetType
|
||||||
gift.CoinPrice = price.CoinPrice
|
gift.CoinPrice = price.CoinPrice
|
||||||
gift.GiftPointAmount = price.GiftPointAmount
|
// gift_point_amount 是历史列,后台和送礼主链路只暴露真实 COIN 价格。
|
||||||
|
gift.GiftPointAmount = 0
|
||||||
// heat_value 是旧配置列,后台不再读写;对兼容调用方只回真实价格,房间贡献也按真实扣费结算。
|
// heat_value 是旧配置列,后台不再读写;对兼容调用方只回真实价格,房间贡献也按真实扣费结算。
|
||||||
gift.HeatValue = price.CoinPrice
|
gift.HeatValue = price.CoinPrice
|
||||||
}
|
}
|
||||||
@ -2837,6 +2838,10 @@ func giftConfigWhereSQL(query resourcedomain.ListGiftConfigsQuery) (string, []an
|
|||||||
where += ` AND (gc.gift_id LIKE ? OR gc.name LIKE ? OR r.resource_code LIKE ?)`
|
where += ` AND (gc.gift_id LIKE ? OR gc.name LIKE ? OR r.resource_code LIKE ?)`
|
||||||
args = append(args, like, like, like)
|
args = append(args, like, like, like)
|
||||||
}
|
}
|
||||||
|
if query.GiftTypeCode != "" {
|
||||||
|
where += ` AND gc.gift_type_code = ?`
|
||||||
|
args = append(args, query.GiftTypeCode)
|
||||||
|
}
|
||||||
if query.FilterRegion {
|
if query.FilterRegion {
|
||||||
where += ` AND EXISTS (
|
where += ` AND EXISTS (
|
||||||
SELECT 1
|
SELECT 1
|
||||||
@ -2908,6 +2913,7 @@ func normalizeResourceGroupListQuery(query resourcedomain.ListResourceGroupsQuer
|
|||||||
func normalizeGiftConfigListQuery(query resourcedomain.ListGiftConfigsQuery) resourcedomain.ListGiftConfigsQuery {
|
func normalizeGiftConfigListQuery(query resourcedomain.ListGiftConfigsQuery) resourcedomain.ListGiftConfigsQuery {
|
||||||
query.Status = strings.ToLower(strings.TrimSpace(query.Status))
|
query.Status = strings.ToLower(strings.TrimSpace(query.Status))
|
||||||
query.Keyword = strings.TrimSpace(query.Keyword)
|
query.Keyword = strings.TrimSpace(query.Keyword)
|
||||||
|
query.GiftTypeCode = resourcedomain.NormalizeGiftTypeCode(query.GiftTypeCode)
|
||||||
query.Page, query.PageSize = normalizePage(query.Page, query.PageSize)
|
query.Page, query.PageSize = normalizePage(query.Page, query.PageSize)
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
@ -3082,7 +3088,8 @@ func normalizeResourceCommand(command resourcedomain.ResourceCommand) resourcedo
|
|||||||
command.CoinPrice = 0
|
command.CoinPrice = 0
|
||||||
command.GiftPointAmount = 0
|
command.GiftPointAmount = 0
|
||||||
} else if command.PriceType == resourcedomain.PriceTypeCoin {
|
} else if command.PriceType == resourcedomain.PriceTypeCoin {
|
||||||
command.GiftPointAmount = command.CoinPrice
|
// 资源价格只保留真实 COIN 价格;gift_point_amount 是历史字段,写入 0 兼容旧表结构。
|
||||||
|
command.GiftPointAmount = 0
|
||||||
}
|
}
|
||||||
command.AssetURL = strings.TrimSpace(command.AssetURL)
|
command.AssetURL = strings.TrimSpace(command.AssetURL)
|
||||||
command.PreviewURL = strings.TrimSpace(command.PreviewURL)
|
command.PreviewURL = strings.TrimSpace(command.PreviewURL)
|
||||||
@ -3124,7 +3131,7 @@ func validateResourceCommand(command resourcedomain.ResourceCommand) error {
|
|||||||
if command.PriceType == resourcedomain.PriceTypeCoin && command.CoinPrice <= 0 {
|
if command.PriceType == resourcedomain.PriceTypeCoin && command.CoinPrice <= 0 {
|
||||||
return xerr.New(xerr.InvalidArgument, "resource coin price is invalid")
|
return xerr.New(xerr.InvalidArgument, "resource coin price is invalid")
|
||||||
}
|
}
|
||||||
if command.CoinPrice < 0 || command.GiftPointAmount < 0 || command.GiftPointAmount != command.CoinPrice {
|
if command.CoinPrice < 0 {
|
||||||
return xerr.New(xerr.InvalidArgument, "resource price is invalid")
|
return xerr.New(xerr.InvalidArgument, "resource price is invalid")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -3265,7 +3272,7 @@ func applyResourcePricingToGiftCommand(command resourcedomain.GiftConfigCommand,
|
|||||||
case resourcedomain.PriceTypeCoin:
|
case resourcedomain.PriceTypeCoin:
|
||||||
command.ChargeAssetType = ledger.AssetCoin
|
command.ChargeAssetType = ledger.AssetCoin
|
||||||
command.CoinPrice = resource.CoinPrice
|
command.CoinPrice = resource.CoinPrice
|
||||||
command.GiftPointAmount = resource.GiftPointAmount
|
command.GiftPointAmount = 0
|
||||||
case resourcedomain.PriceTypeFree:
|
case resourcedomain.PriceTypeFree:
|
||||||
command.ChargeAssetType = ledger.AssetCoin
|
command.ChargeAssetType = ledger.AssetCoin
|
||||||
command.CoinPrice = 0
|
command.CoinPrice = 0
|
||||||
@ -3289,6 +3296,8 @@ func normalizeGiftConfigCommand(command resourcedomain.GiftConfigCommand) resour
|
|||||||
command.ChargeAssetType = ledger.AssetCoin
|
command.ChargeAssetType = ledger.AssetCoin
|
||||||
}
|
}
|
||||||
command.EffectTypes = normalizeGiftEffectTypes(command.EffectTypes)
|
command.EffectTypes = normalizeGiftEffectTypes(command.EffectTypes)
|
||||||
|
// gift_point_amount 是历史字段,礼物配置保存时固定归零,送礼结算只读取真实 COIN 价格。
|
||||||
|
command.GiftPointAmount = 0
|
||||||
if command.EffectiveAtMS < 0 {
|
if command.EffectiveAtMS < 0 {
|
||||||
command.EffectiveAtMS = 0
|
command.EffectiveAtMS = 0
|
||||||
}
|
}
|
||||||
@ -3316,7 +3325,7 @@ func validateGiftConfigCommand(command resourcedomain.GiftConfigCommand) error {
|
|||||||
if !resourcedomain.ValidStatus(command.Status) {
|
if !resourcedomain.ValidStatus(command.Status) {
|
||||||
return xerr.New(xerr.InvalidArgument, "status is invalid")
|
return xerr.New(xerr.InvalidArgument, "status is invalid")
|
||||||
}
|
}
|
||||||
if command.CoinPrice < 0 || command.GiftPointAmount < 0 || command.HeatValue < 0 {
|
if command.CoinPrice < 0 || command.HeatValue < 0 {
|
||||||
return xerr.New(xerr.InvalidArgument, "gift price is invalid")
|
return xerr.New(xerr.InvalidArgument, "gift price is invalid")
|
||||||
}
|
}
|
||||||
if !resourcedomain.ValidGiftTypeCode(command.GiftTypeCode) {
|
if !resourcedomain.ValidGiftTypeCode(command.GiftTypeCode) {
|
||||||
|
|||||||
@ -128,6 +128,7 @@ func (s *Server) ListGiftConfigs(ctx context.Context, req *walletv1.ListGiftConf
|
|||||||
AppCode: req.GetAppCode(),
|
AppCode: req.GetAppCode(),
|
||||||
Status: req.GetStatus(),
|
Status: req.GetStatus(),
|
||||||
Keyword: req.GetKeyword(),
|
Keyword: req.GetKeyword(),
|
||||||
|
GiftTypeCode: req.GetGiftTypeCode(),
|
||||||
Page: req.GetPage(),
|
Page: req.GetPage(),
|
||||||
PageSize: req.GetPageSize(),
|
PageSize: req.GetPageSize(),
|
||||||
ActiveOnly: req.GetActiveOnly(),
|
ActiveOnly: req.GetActiveOnly(),
|
||||||
@ -401,7 +402,7 @@ func resourceCommandFromCreate(req *walletv1.CreateResourceRequest) resourcedoma
|
|||||||
WalletAssetAmount: req.GetWalletAssetAmount(),
|
WalletAssetAmount: req.GetWalletAssetAmount(),
|
||||||
PriceType: req.GetPriceType(),
|
PriceType: req.GetPriceType(),
|
||||||
CoinPrice: req.GetCoinPrice(),
|
CoinPrice: req.GetCoinPrice(),
|
||||||
GiftPointAmount: req.GetGiftPointAmount(),
|
GiftPointAmount: 0,
|
||||||
UsageScopes: req.GetUsageScopes(),
|
UsageScopes: req.GetUsageScopes(),
|
||||||
AssetURL: req.GetAssetUrl(),
|
AssetURL: req.GetAssetUrl(),
|
||||||
PreviewURL: req.GetPreviewUrl(),
|
PreviewURL: req.GetPreviewUrl(),
|
||||||
@ -426,7 +427,7 @@ func resourceCommandFromUpdate(req *walletv1.UpdateResourceRequest) resourcedoma
|
|||||||
WalletAssetAmount: req.GetWalletAssetAmount(),
|
WalletAssetAmount: req.GetWalletAssetAmount(),
|
||||||
PriceType: req.GetPriceType(),
|
PriceType: req.GetPriceType(),
|
||||||
CoinPrice: req.GetCoinPrice(),
|
CoinPrice: req.GetCoinPrice(),
|
||||||
GiftPointAmount: req.GetGiftPointAmount(),
|
GiftPointAmount: 0,
|
||||||
UsageScopes: req.GetUsageScopes(),
|
UsageScopes: req.GetUsageScopes(),
|
||||||
AssetUrl: req.GetAssetUrl(),
|
AssetUrl: req.GetAssetUrl(),
|
||||||
PreviewUrl: req.GetPreviewUrl(),
|
PreviewUrl: req.GetPreviewUrl(),
|
||||||
@ -478,7 +479,7 @@ func giftConfigCommandFromCreate(req *walletv1.CreateGiftConfigRequest) resource
|
|||||||
PresentationJSON: req.GetPresentationJson(),
|
PresentationJSON: req.GetPresentationJson(),
|
||||||
PriceVersion: req.GetPriceVersion(),
|
PriceVersion: req.GetPriceVersion(),
|
||||||
CoinPrice: req.GetCoinPrice(),
|
CoinPrice: req.GetCoinPrice(),
|
||||||
GiftPointAmount: req.GetGiftPointAmount(),
|
GiftPointAmount: 0,
|
||||||
HeatValue: req.GetHeatValue(),
|
HeatValue: req.GetHeatValue(),
|
||||||
EffectiveAtMS: req.GetEffectiveAtMs(),
|
EffectiveAtMS: req.GetEffectiveAtMs(),
|
||||||
GiftTypeCode: req.GetGiftTypeCode(),
|
GiftTypeCode: req.GetGiftTypeCode(),
|
||||||
@ -502,7 +503,7 @@ func giftConfigCommandFromUpdate(req *walletv1.UpdateGiftConfigRequest) resource
|
|||||||
PresentationJSON: req.GetPresentationJson(),
|
PresentationJSON: req.GetPresentationJson(),
|
||||||
PriceVersion: req.GetPriceVersion(),
|
PriceVersion: req.GetPriceVersion(),
|
||||||
CoinPrice: req.GetCoinPrice(),
|
CoinPrice: req.GetCoinPrice(),
|
||||||
GiftPointAmount: req.GetGiftPointAmount(),
|
GiftPointAmount: 0,
|
||||||
HeatValue: req.GetHeatValue(),
|
HeatValue: req.GetHeatValue(),
|
||||||
EffectiveAtMS: req.GetEffectiveAtMs(),
|
EffectiveAtMS: req.GetEffectiveAtMs(),
|
||||||
GiftTypeCode: req.GetGiftTypeCode(),
|
GiftTypeCode: req.GetGiftTypeCode(),
|
||||||
@ -567,7 +568,7 @@ func resourceToProto(resource resourcedomain.Resource) *walletv1.Resource {
|
|||||||
WalletAssetAmount: resource.WalletAssetAmount,
|
WalletAssetAmount: resource.WalletAssetAmount,
|
||||||
PriceType: resource.PriceType,
|
PriceType: resource.PriceType,
|
||||||
CoinPrice: resource.CoinPrice,
|
CoinPrice: resource.CoinPrice,
|
||||||
GiftPointAmount: resource.GiftPointAmount,
|
GiftPointAmount: 0,
|
||||||
UsageScopes: resource.UsageScopes,
|
UsageScopes: resource.UsageScopes,
|
||||||
AssetUrl: resource.AssetURL,
|
AssetUrl: resource.AssetURL,
|
||||||
PreviewUrl: resource.PreviewURL,
|
PreviewUrl: resource.PreviewURL,
|
||||||
@ -669,7 +670,7 @@ func giftConfigToProto(gift resourcedomain.GiftConfig) *walletv1.GiftConfig {
|
|||||||
PriceVersion: gift.PriceVersion,
|
PriceVersion: gift.PriceVersion,
|
||||||
ChargeAssetType: gift.ChargeAssetType,
|
ChargeAssetType: gift.ChargeAssetType,
|
||||||
CoinPrice: gift.CoinPrice,
|
CoinPrice: gift.CoinPrice,
|
||||||
GiftPointAmount: gift.GiftPointAmount,
|
GiftPointAmount: 0,
|
||||||
HeatValue: gift.HeatValue,
|
HeatValue: gift.HeatValue,
|
||||||
GiftTypeCode: gift.GiftTypeCode,
|
GiftTypeCode: gift.GiftTypeCode,
|
||||||
EffectiveFromMs: gift.EffectiveFromMS,
|
EffectiveFromMs: gift.EffectiveFromMS,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user