2026-06-23 11:53:00 +08:00

151 lines
4.2 KiB
Go

package mysql
import (
"context"
"database/sql"
"errors"
"time"
"hyapp/pkg/appcode"
"hyapp/pkg/xerr"
)
func (r *Repository) creditHostPeriodDiamonds(ctx context.Context, tx *sql.Tx, transactionID string, commandID string, metadata giftMetadata, nowMs int64) (int64, int64, error) {
if metadata.HostPeriodDiamondAdded <= 0 {
return 0, 0, nil
}
after, version, exists, err := r.lockHostPeriodDiamondAccount(ctx, tx, metadata.TargetUserID, metadata.HostPeriodCycleKey)
if err != nil {
return 0, 0, err
}
if !exists {
after = metadata.HostPeriodDiamondAdded
version = 1
_, err = tx.ExecContext(ctx,
`INSERT INTO host_period_diamond_accounts (
app_code, user_id, cycle_key, region_id, agency_owner_user_id, total_diamonds, gift_diamond_total,
version, first_gift_at_ms, last_gift_at_ms, created_at_ms, updated_at_ms
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
appcode.FromContext(ctx),
metadata.TargetUserID,
metadata.HostPeriodCycleKey,
metadata.TargetHostRegionID,
metadata.TargetAgencyOwnerUserID,
after,
metadata.HostPeriodDiamondAdded,
version,
nowMs,
nowMs,
nowMs,
nowMs,
)
if err != nil && isMySQLDuplicateError(err) {
after, version, exists, err = r.lockHostPeriodDiamondAccount(ctx, tx, metadata.TargetUserID, metadata.HostPeriodCycleKey)
}
if err != nil {
return 0, 0, err
}
if !exists {
if err := r.insertHostPeriodDiamondEntry(ctx, tx, transactionID, commandID, metadata, after, nowMs); err != nil {
return 0, 0, err
}
return after, version, nil
}
}
after, err = checkedAdd(after, metadata.HostPeriodDiamondAdded)
if err != nil {
return 0, 0, err
}
version++
result, err := tx.ExecContext(ctx,
`UPDATE host_period_diamond_accounts
SET region_id = ?, agency_owner_user_id = ?, total_diamonds = ?, gift_diamond_total = gift_diamond_total + ?,
version = ?, last_gift_at_ms = ?, updated_at_ms = ?
WHERE app_code = ? AND user_id = ? AND cycle_key = ?`,
metadata.TargetHostRegionID,
metadata.TargetAgencyOwnerUserID,
after,
metadata.HostPeriodDiamondAdded,
version,
nowMs,
nowMs,
appcode.FromContext(ctx),
metadata.TargetUserID,
metadata.HostPeriodCycleKey,
)
if err != nil {
return 0, 0, err
}
rows, err := result.RowsAffected()
if err != nil {
return 0, 0, err
}
if rows != 1 {
return 0, 0, xerr.New(xerr.LedgerConflict, "host period diamond account version conflict")
}
if err := r.insertHostPeriodDiamondEntry(ctx, tx, transactionID, commandID, metadata, after, nowMs); err != nil {
return 0, 0, err
}
return after, version, nil
}
func (r *Repository) lockHostPeriodDiamondAccount(ctx context.Context, tx *sql.Tx, userID int64, cycleKey string) (int64, int64, bool, error) {
row := tx.QueryRowContext(ctx,
`SELECT total_diamonds, version
FROM host_period_diamond_accounts
WHERE app_code = ? AND user_id = ? AND cycle_key = ?
FOR UPDATE`,
appcode.FromContext(ctx),
userID,
cycleKey,
)
var totalDiamonds int64
var version int64
if err := row.Scan(&totalDiamonds, &version); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return 0, 0, false, nil
}
return 0, 0, false, err
}
return totalDiamonds, version, true, nil
}
func (r *Repository) insertHostPeriodDiamondEntry(ctx context.Context, tx *sql.Tx, transactionID string, commandID string, metadata giftMetadata, diamondAfter int64, nowMs int64) error {
_, err := tx.ExecContext(ctx,
`INSERT INTO host_period_diamond_entries (
app_code, transaction_id, command_id, user_id, cycle_key, region_id, agency_owner_user_id,
diamond_delta, diamond_after, gift_charge_asset_type, gift_charge_amount,
gift_id, gift_count, sender_user_id, room_id, created_at_ms
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
appcode.FromContext(ctx),
transactionID,
commandID,
metadata.TargetUserID,
metadata.HostPeriodCycleKey,
metadata.TargetHostRegionID,
metadata.TargetAgencyOwnerUserID,
metadata.HostPeriodDiamondAdded,
diamondAfter,
metadata.ChargeAssetType,
metadata.ChargeAmount,
metadata.GiftID,
metadata.GiftCount,
metadata.SenderUserID,
metadata.RoomID,
nowMs,
)
return err
}
func hostPeriodCycleKeyFromMS(ms int64) string {
return time.UnixMilli(ms).UTC().Format("2006-01")
}