168 lines
6.5 KiB
Go
168 lines
6.5 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"encoding/json"
|
|
"errors"
|
|
)
|
|
|
|
type RechargeChannel struct {
|
|
ID int64 `db:"id"`
|
|
Code string `db:"channel_code"`
|
|
Name string `db:"name"`
|
|
ChannelType int `db:"channel_type"`
|
|
MinAmount int64 `db:"min_amount"`
|
|
MaxAmount int64 `db:"max_amount"`
|
|
FeeType int `db:"fee_type"`
|
|
FeeFixedAmount int64 `db:"fee_fixed_amount"`
|
|
FeeRate int `db:"fee_rate"`
|
|
}
|
|
|
|
type RechargeCreation struct {
|
|
TenantID int64
|
|
MemberID int64
|
|
RechargeNo string
|
|
ChannelID int64
|
|
RequestAmount int64
|
|
Currency string
|
|
VoucherAttachmentIDs []int64
|
|
Remark string
|
|
}
|
|
|
|
type RechargeOrder struct {
|
|
ID int64 `db:"id"`
|
|
RechargeNo string `db:"recharge_no"`
|
|
ChannelID int64 `db:"channel_id"`
|
|
ChannelName string `db:"channel_name"`
|
|
RequestAmount int64 `db:"request_amount"`
|
|
PaidAmount int64 `db:"paid_amount"`
|
|
Currency string `db:"currency"`
|
|
VoucherAttachmentIDs json.RawMessage `db:"voucher_attachment_ids"`
|
|
Status int `db:"recharge_status"`
|
|
Remark string `db:"remark"`
|
|
ReviewReasonCode string `db:"review_reason_code"`
|
|
ReviewReason string `db:"review_reason"`
|
|
ExternalNo string `db:"external_no"`
|
|
PaidAt string `db:"paid_at"`
|
|
ReviewedAt string `db:"reviewed_at"`
|
|
CreditedAt string `db:"credited_at"`
|
|
CreatedAt string `db:"created_at"`
|
|
UpdatedAt string `db:"updated_at"`
|
|
}
|
|
|
|
type RechargePage struct {
|
|
Items []RechargeOrder
|
|
Total int64
|
|
}
|
|
|
|
func (s *Store) RechargeChannels(ctx context.Context, tenantID int64) ([]RechargeChannel, error) {
|
|
items := make([]RechargeChannel, 0)
|
|
err := s.db.SelectContext(ctx, &items, `SELECT id,channel_code,name,channel_type,min_amount,max_amount,
|
|
fee_type,fee_fixed_amount,fee_rate FROM hg_payment_channel
|
|
WHERE tenant_id=? AND direction IN (1,3) AND status=1 AND deleted_at IS NULL ORDER BY sort,id`, tenantID)
|
|
return items, err
|
|
}
|
|
|
|
func (s *Store) CreateRecharge(ctx context.Context, input RechargeCreation) (*RechargeOrder, error) {
|
|
tx, err := s.db.BeginTxx(ctx, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer tx.Rollback()
|
|
var memberID int64
|
|
err = tx.GetContext(ctx, &memberID, `SELECT id FROM hg_app_member WHERE tenant_id=? AND id=?
|
|
AND member_status=1 AND deleted_at IS NULL LIMIT 1 FOR UPDATE`, input.TenantID, input.MemberID)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, ErrAccountRestricted
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var channel RechargeChannel
|
|
err = tx.GetContext(ctx, &channel, `SELECT id,channel_code,name,channel_type,min_amount,max_amount,
|
|
fee_type,fee_fixed_amount,fee_rate FROM hg_payment_channel WHERE tenant_id=? AND id=?
|
|
AND direction IN (1,3) AND status=1 AND deleted_at IS NULL LIMIT 1 FOR UPDATE`, input.TenantID, input.ChannelID)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, ErrRechargeChannel
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if input.RequestAmount < maxInt64(1, channel.MinAmount) || (channel.MaxAmount > 0 && input.RequestAmount > channel.MaxAmount) {
|
|
return nil, ErrRechargeAmount
|
|
}
|
|
for _, attachmentID := range input.VoucherAttachmentIDs {
|
|
var count int
|
|
if err = tx.GetContext(ctx, &count, `SELECT COUNT(*) FROM hg_sys_attachment WHERE tenant_id=? AND id=?
|
|
AND app_id='workbench' AND member_id=? AND status=1`, input.TenantID, attachmentID, input.MemberID); err != nil {
|
|
return nil, err
|
|
}
|
|
if count != 1 {
|
|
return nil, ErrRechargeVoucher
|
|
}
|
|
}
|
|
vouchers, err := json.Marshal(input.VoucherAttachmentIDs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result, err := tx.ExecContext(ctx, `INSERT INTO hg_recharge_order
|
|
(tenant_id,recharge_no,member_id,channel_id,request_amount,paid_amount,currency,
|
|
voucher_attachment_ids,recharge_type,recharge_status,remark,created_by,updated_by)
|
|
VALUES(?,?,?,?,?,0,?,?,1,1,?,?,?)`, input.TenantID, input.RechargeNo, input.MemberID,
|
|
input.ChannelID, input.RequestAmount, input.Currency, vouchers, input.Remark, input.MemberID, input.MemberID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
id, err := result.LastInsertId()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err = tx.Commit(); err != nil {
|
|
return nil, err
|
|
}
|
|
return &RechargeOrder{ID: id, RechargeNo: input.RechargeNo, ChannelID: channel.ID,
|
|
ChannelName: channel.Name, RequestAmount: input.RequestAmount, Currency: input.Currency,
|
|
VoucherAttachmentIDs: vouchers, Status: 1, Remark: input.Remark}, nil
|
|
}
|
|
|
|
func (s *Store) RechargeOrders(ctx context.Context, tenantID, memberID int64, status, offset, limit int) (*RechargePage, error) {
|
|
where := ` WHERE o.tenant_id=? AND o.member_id=?`
|
|
args := []any{tenantID, memberID}
|
|
if status > 0 {
|
|
where += ` AND o.recharge_status=?`
|
|
args = append(args, status)
|
|
}
|
|
var total int64
|
|
if err := s.db.GetContext(ctx, &total, `SELECT COUNT(*) FROM hg_recharge_order o`+where, args...); err != nil {
|
|
return nil, err
|
|
}
|
|
args = append(args, limit, offset)
|
|
items := make([]RechargeOrder, 0)
|
|
err := s.db.SelectContext(ctx, &items, rechargeSelectSQL()+where+
|
|
` ORDER BY o.created_at DESC,o.id DESC LIMIT ? OFFSET ?`, args...)
|
|
return &RechargePage{Items: items, Total: total}, err
|
|
}
|
|
|
|
func (s *Store) RechargeOrder(ctx context.Context, tenantID, memberID, orderID int64) (*RechargeOrder, error) {
|
|
var row RechargeOrder
|
|
err := s.db.GetContext(ctx, &row, rechargeSelectSQL()+
|
|
` WHERE o.tenant_id=? AND o.member_id=? AND o.id=? LIMIT 1`, tenantID, memberID, orderID)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, ErrRechargeNotFound
|
|
}
|
|
return &row, err
|
|
}
|
|
|
|
func rechargeSelectSQL() string {
|
|
return `SELECT o.id,o.recharge_no,o.channel_id,COALESCE(c.name,'') AS channel_name,o.request_amount,
|
|
o.paid_amount,o.currency,COALESCE(o.voucher_attachment_ids,JSON_ARRAY()) AS voucher_attachment_ids,
|
|
o.recharge_status,o.remark,o.review_reason_code,o.review_reason,o.external_no,
|
|
COALESCE(DATE_FORMAT(o.paid_at,'%Y-%m-%dT%H:%i:%sZ'),'') AS paid_at,
|
|
COALESCE(DATE_FORMAT(o.reviewed_at,'%Y-%m-%dT%H:%i:%sZ'),'') AS reviewed_at,
|
|
COALESCE(DATE_FORMAT(o.credited_at,'%Y-%m-%dT%H:%i:%sZ'),'') AS credited_at,
|
|
DATE_FORMAT(o.created_at,'%Y-%m-%dT%H:%i:%sZ') AS created_at,
|
|
DATE_FORMAT(o.updated_at,'%Y-%m-%dT%H:%i:%sZ') AS updated_at
|
|
FROM hg_recharge_order o LEFT JOIN hg_payment_channel c ON c.tenant_id=o.tenant_id AND c.id=o.channel_id`
|
|
}
|