feat(server/pay): 订单列表端点 + 分币种展示价 + 富字段详情
- GET /v1/pay/orders 订单列表(Store.ListByUser,本地台账倒序) - catalog 增 PriceUsdtMicro(USDT 一口价展示) + SettlementCurrency/DisplayAmountMinor - 下单按支付方式落展示金额/结算币种(crypto→USDT / 法币→CNY),webhook 覆盖实际 - GetOrder 详情回带 sku/plan/method/amount/currency/created_at/paid_at - 新增 List 越权/倒序/富字段 + 分币种展示金额 + 详情富字段测试 - 修 3 个 pre-existing 迁移测试(renumber 后 21→23 遗留) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
This commit is contained in:
@@ -23,6 +23,7 @@ type PurchaseRow struct {
|
||||
Channel string
|
||||
SubID sql.NullInt64
|
||||
PaidAt sql.NullTime
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
type Store struct {
|
||||
@@ -38,13 +39,15 @@ func (s *Store) BeginTx(ctx context.Context) (*sql.Tx, error) {
|
||||
return s.db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelReadCommitted})
|
||||
}
|
||||
|
||||
// Insert 下单成功后落台账(status=created)。
|
||||
func (s *Store) Insert(ctx context.Context, userID int64, bizRef, sku, outTradeNo, method string) error {
|
||||
// Insert 下单成功后落台账(status=created)。amountMinor/currency 是下单时的
|
||||
// **展示预估**(按支付方式的结算币种,见 DisplayAmountMinor),webhook 到账后由
|
||||
// MarkPaidTx 用实际结算金额覆盖——保证订单列表/详情在支付前也有金额可显。
|
||||
func (s *Store) Insert(ctx context.Context, userID int64, bizRef, sku, outTradeNo, method string, amountMinor int64, currency string) error {
|
||||
now := time.Now().UTC()
|
||||
_, err := s.db.ExecContext(ctx,
|
||||
`INSERT INTO pay_purchases (user_id, biz_ref, sku, out_trade_no, method, status, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, 'created', ?, ?)`,
|
||||
userID, bizRef, sku, outTradeNo, method, now, now)
|
||||
`INSERT INTO pay_purchases (user_id, biz_ref, sku, out_trade_no, method, status, amount_minor, currency, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, 'created', ?, ?, ?, ?)`,
|
||||
userID, bizRef, sku, outTradeNo, method, amountMinor, currency, now, now)
|
||||
if err != nil {
|
||||
return fmt.Errorf("pay.Store.Insert: %w", err)
|
||||
}
|
||||
@@ -52,17 +55,42 @@ func (s *Store) Insert(ctx context.Context, userID int64, bizRef, sku, outTradeN
|
||||
}
|
||||
|
||||
const purchaseCols = `id, user_id, biz_ref, sku, out_trade_no, method, status,
|
||||
amount_minor, currency, channel, sub_id, paid_at`
|
||||
amount_minor, currency, channel, sub_id, paid_at, created_at`
|
||||
|
||||
func scanPurchase(row *sql.Row) (*PurchaseRow, error) {
|
||||
var p PurchaseRow
|
||||
if err := row.Scan(&p.ID, &p.UserID, &p.BizRef, &p.SKU, &p.OutTradeNo, &p.Method,
|
||||
&p.Status, &p.AmountMinor, &p.Currency, &p.Channel, &p.SubID, &p.PaidAt); err != nil {
|
||||
&p.Status, &p.AmountMinor, &p.Currency, &p.Channel, &p.SubID, &p.PaidAt, &p.CreatedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
// ListByUser 按用户列历史订单(created_at 倒序,复用 idx_pay_user 索引)。
|
||||
// 供「订单列表」页;limit 上限保护。
|
||||
func (s *Store) ListByUser(ctx context.Context, userID int64, limit int) ([]PurchaseRow, error) {
|
||||
if limit <= 0 || limit > 200 {
|
||||
limit = 100
|
||||
}
|
||||
rows, err := s.db.QueryContext(ctx,
|
||||
`SELECT `+purchaseCols+` FROM pay_purchases WHERE user_id = ? ORDER BY created_at DESC, id DESC LIMIT ?`,
|
||||
userID, limit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pay.Store.ListByUser: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
var out []PurchaseRow
|
||||
for rows.Next() {
|
||||
var p PurchaseRow
|
||||
if err := rows.Scan(&p.ID, &p.UserID, &p.BizRef, &p.SKU, &p.OutTradeNo, &p.Method,
|
||||
&p.Status, &p.AmountMinor, &p.Currency, &p.Channel, &p.SubID, &p.PaidAt, &p.CreatedAt); err != nil {
|
||||
return nil, fmt.Errorf("pay.Store.ListByUser scan: %w", err)
|
||||
}
|
||||
out = append(out, p)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// GetForUser 按 (userID, outTradeNo) 取行——所有权校验由查询本身完成。
|
||||
func (s *Store) GetForUser(ctx context.Context, userID int64, outTradeNo string) (*PurchaseRow, error) {
|
||||
return scanPurchase(s.db.QueryRowContext(ctx,
|
||||
|
||||
Reference in New Issue
Block a user