实现多商户履约平台基础
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"embed"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"affiliate_dash/internal/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
//go:embed migrations/*.sql
|
||||
var migrationFiles embed.FS
|
||||
|
||||
type schemaMigration struct {
|
||||
Version string `gorm:"primaryKey;size:64"`
|
||||
Name string `gorm:"size:255;not null"`
|
||||
Checksum string `gorm:"size:64;not null"`
|
||||
AppliedAt time.Time `gorm:"not null"`
|
||||
}
|
||||
|
||||
func (schemaMigration) TableName() string {
|
||||
return "schema_migrations"
|
||||
}
|
||||
|
||||
// Migrate 使用显式 SQL 迁移管理表结构,避免大型项目依赖隐式结构同步。
|
||||
func Migrate(db *gorm.DB) error {
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Exec("SELECT pg_advisory_xact_lock(hashtext(?))", "affiliate_dash_schema_migrations").Error; err != nil {
|
||||
return fmt.Errorf("lock schema migrations: %w", err)
|
||||
}
|
||||
if err := ensureMigrationTable(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := applySQLMigrations(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
merchant, err := ensureSelfMerchant(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ensureWallet(tx, merchant.ID)
|
||||
})
|
||||
}
|
||||
|
||||
func ensureMigrationTable(tx *gorm.DB) error {
|
||||
statements := []string{
|
||||
`
|
||||
CREATE TABLE IF NOT EXISTS schema_migrations (
|
||||
version varchar(64) PRIMARY KEY,
|
||||
name varchar(255) NOT NULL,
|
||||
checksum varchar(64) NOT NULL,
|
||||
applied_at timestamptz NOT NULL
|
||||
)`,
|
||||
`ALTER TABLE schema_migrations ADD COLUMN IF NOT EXISTS name varchar(255) NOT NULL DEFAULT ''`,
|
||||
`ALTER TABLE schema_migrations ADD COLUMN IF NOT EXISTS checksum varchar(64) NOT NULL DEFAULT ''`,
|
||||
`ALTER TABLE schema_migrations ADD COLUMN IF NOT EXISTS applied_at timestamptz NOT NULL DEFAULT now()`,
|
||||
}
|
||||
for _, statement := range statements {
|
||||
if err := tx.Exec(statement).Error; err != nil {
|
||||
return fmt.Errorf("ensure schema_migrations: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func applySQLMigrations(tx *gorm.DB) error {
|
||||
paths, err := fs.Glob(migrationFiles, "migrations/*.sql")
|
||||
if err != nil {
|
||||
return fmt.Errorf("list migrations: %w", err)
|
||||
}
|
||||
sort.Strings(paths)
|
||||
for _, path := range paths {
|
||||
content, err := migrationFiles.ReadFile(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read migration %s: %w", path, err)
|
||||
}
|
||||
version, name := parseMigrationName(path)
|
||||
checksum := migrationChecksum(content)
|
||||
|
||||
var existing schemaMigration
|
||||
err = tx.Where("version = ?", version).First(&existing).Error
|
||||
if err == nil {
|
||||
if existing.Checksum == "" {
|
||||
if err := tx.Model(&schemaMigration{}).Where("version = ?", version).Updates(map[string]interface{}{
|
||||
"name": name,
|
||||
"checksum": checksum,
|
||||
}).Error; err != nil {
|
||||
return fmt.Errorf("adopt migration %s: %w", version, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if existing.Checksum != checksum {
|
||||
return fmt.Errorf("migration %s checksum mismatch: 已执行版本不能被修改", version)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != gorm.ErrRecordNotFound {
|
||||
return fmt.Errorf("query migration %s: %w", version, err)
|
||||
}
|
||||
sql := strings.TrimSpace(string(content))
|
||||
if sql == "" {
|
||||
return fmt.Errorf("migration %s is empty", path)
|
||||
}
|
||||
if err := tx.Exec(sql).Error; err != nil {
|
||||
return fmt.Errorf("apply migration %s: %w", path, err)
|
||||
}
|
||||
if err := tx.Create(&schemaMigration{
|
||||
Version: version,
|
||||
Name: name,
|
||||
Checksum: checksum,
|
||||
AppliedAt: time.Now(),
|
||||
}).Error; err != nil {
|
||||
return fmt.Errorf("record migration %s: %w", version, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseMigrationName(path string) (string, string) {
|
||||
base := filepath.Base(path)
|
||||
name := strings.TrimSuffix(base, filepath.Ext(base))
|
||||
parts := strings.SplitN(name, "_", 2)
|
||||
if len(parts) == 1 {
|
||||
return parts[0], name
|
||||
}
|
||||
return parts[0], parts[1]
|
||||
}
|
||||
|
||||
func migrationChecksum(content []byte) string {
|
||||
sum := sha256.Sum256(content)
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
func ensureSelfMerchant(tx *gorm.DB) (*model.Merchant, error) {
|
||||
var merchant model.Merchant
|
||||
err := tx.Where("code = ?", "self-operated").First(&merchant).Error
|
||||
if err == nil {
|
||||
return &merchant, nil
|
||||
}
|
||||
if err != gorm.ErrRecordNotFound {
|
||||
return nil, err
|
||||
}
|
||||
merchant = model.Merchant{
|
||||
Code: "self-operated",
|
||||
Name: "自营商户",
|
||||
Status: model.MerchantStatusActive,
|
||||
}
|
||||
if err := tx.Create(&merchant).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &merchant, nil
|
||||
}
|
||||
|
||||
func ensureWallet(tx *gorm.DB, merchantID uint) error {
|
||||
return tx.Clauses(clause.OnConflict{DoNothing: true}).Create(&model.WalletAccount{
|
||||
MerchantID: merchantID,
|
||||
Currency: "CNY",
|
||||
}).Error
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ,
|
||||
deleted_at TIMESTAMPTZ,
|
||||
username VARCHAR(64) NOT NULL,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
nickname VARCHAR(64),
|
||||
role VARCHAR(32) NOT NULL DEFAULT 'distributor',
|
||||
status BIGINT DEFAULT 1,
|
||||
invite_code VARCHAR(32),
|
||||
parent_id BIGINT
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_users_username ON users (username);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_users_invite_code ON users (invite_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_deleted_at ON users (deleted_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_parent_id ON users (parent_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS merchants (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ,
|
||||
deleted_at TIMESTAMPTZ,
|
||||
code VARCHAR(64) NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'active',
|
||||
contact_name VARCHAR(64),
|
||||
contact_info VARCHAR(128)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_merchants_code ON merchants (code);
|
||||
CREATE INDEX IF NOT EXISTS idx_merchants_deleted_at ON merchants (deleted_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_merchants_status ON merchants (status);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS merchant_members (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ,
|
||||
merchant_id BIGINT NOT NULL,
|
||||
user_id BIGINT NOT NULL,
|
||||
role VARCHAR(32) NOT NULL DEFAULT 'operator',
|
||||
status BIGINT NOT NULL DEFAULT 1,
|
||||
is_default BOOLEAN NOT NULL DEFAULT FALSE
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_merchant_member ON merchant_members (merchant_id, user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_merchant_members_merchant_id ON merchant_members (merchant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_merchant_members_user_id ON merchant_members (user_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS skins (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ,
|
||||
deleted_at TIMESTAMPTZ,
|
||||
merchant_id BIGINT NOT NULL DEFAULT 0,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
sku VARCHAR(64) NOT NULL,
|
||||
game VARCHAR(64),
|
||||
category VARCHAR(64),
|
||||
cover_url VARCHAR(512),
|
||||
price DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
cost_price DOUBLE PRECISION DEFAULT 0,
|
||||
commission DOUBLE PRECISION DEFAULT 0,
|
||||
stock BIGINT DEFAULT 0,
|
||||
status BIGINT DEFAULT 1,
|
||||
description TEXT
|
||||
);
|
||||
|
||||
DROP INDEX IF EXISTS idx_skins_sku;
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_skins_merchant_sku ON skins (merchant_id, sku);
|
||||
CREATE INDEX IF NOT EXISTS idx_skins_deleted_at ON skins (deleted_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_skins_merchant_id ON skins (merchant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_skins_game ON skins (game);
|
||||
CREATE INDEX IF NOT EXISTS idx_skins_category ON skins (category);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS orders (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ,
|
||||
deleted_at TIMESTAMPTZ,
|
||||
merchant_id BIGINT NOT NULL DEFAULT 0,
|
||||
order_no VARCHAR(64) NOT NULL,
|
||||
skin_id BIGINT NOT NULL,
|
||||
distributor_id BIGINT NOT NULL,
|
||||
buyer_name VARCHAR(64),
|
||||
amount DOUBLE PRECISION NOT NULL,
|
||||
commission_amt DOUBLE PRECISION DEFAULT 0,
|
||||
status VARCHAR(32) DEFAULT 'pending',
|
||||
remark VARCHAR(255),
|
||||
provider_order_no VARCHAR(64),
|
||||
shipped_at TIMESTAMPTZ,
|
||||
ship_fail_reason VARCHAR(512),
|
||||
game_channel VARCHAR(64),
|
||||
game_uid VARCHAR(128),
|
||||
role_name VARCHAR(64),
|
||||
pay_score BIGINT DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_orders_order_no ON orders (order_no);
|
||||
CREATE INDEX IF NOT EXISTS idx_orders_deleted_at ON orders (deleted_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_orders_merchant_id ON orders (merchant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_orders_skin_id ON orders (skin_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_orders_distributor_id ON orders (distributor_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_orders_status ON orders (status);
|
||||
CREATE INDEX IF NOT EXISTS idx_orders_provider_order_no ON orders (provider_order_no);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ship_logs (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMPTZ,
|
||||
merchant_id BIGINT NOT NULL DEFAULT 0,
|
||||
order_no VARCHAR(64) NOT NULL,
|
||||
order_id BIGINT,
|
||||
ship_status VARCHAR(32) NOT NULL,
|
||||
provider_order_no VARCHAR(64),
|
||||
fail_reason VARCHAR(512),
|
||||
payload TEXT,
|
||||
result_status VARCHAR(32),
|
||||
message VARCHAR(255)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ship_logs_merchant_id ON ship_logs (merchant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_ship_logs_order_no ON ship_logs (order_no);
|
||||
CREATE INDEX IF NOT EXISTS idx_ship_logs_order_id ON ship_logs (order_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS api_clients (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ,
|
||||
deleted_at TIMESTAMPTZ,
|
||||
merchant_id BIGINT NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
app_key VARCHAR(96) NOT NULL,
|
||||
secret_ciphertext TEXT NOT NULL,
|
||||
signature_version VARCHAR(16) NOT NULL DEFAULT 'v1',
|
||||
scopes TEXT NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'active',
|
||||
expires_at TIMESTAMPTZ,
|
||||
last_used_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_api_clients_app_key ON api_clients (app_key);
|
||||
CREATE INDEX IF NOT EXISTS idx_api_clients_deleted_at ON api_clients (deleted_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_api_clients_merchant_id ON api_clients (merchant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_api_clients_status ON api_clients (status);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS products (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ,
|
||||
deleted_at TIMESTAMPTZ,
|
||||
code VARCHAR(96) NOT NULL,
|
||||
name VARCHAR(160) NOT NULL,
|
||||
category VARCHAR(64),
|
||||
description TEXT,
|
||||
attributes TEXT,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'active'
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_products_code ON products (code);
|
||||
CREATE INDEX IF NOT EXISTS idx_products_deleted_at ON products (deleted_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_products_category ON products (category);
|
||||
CREATE INDEX IF NOT EXISTS idx_products_status ON products (status);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS merchant_products (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ,
|
||||
deleted_at TIMESTAMPTZ,
|
||||
merchant_id BIGINT NOT NULL,
|
||||
product_id BIGINT NOT NULL,
|
||||
sku VARCHAR(96) NOT NULL,
|
||||
display_name VARCHAR(160),
|
||||
price_amount BIGINT NOT NULL DEFAULT 0,
|
||||
cost_amount BIGINT NOT NULL DEFAULT 0,
|
||||
currency VARCHAR(12) NOT NULL DEFAULT 'CNY',
|
||||
stock BIGINT NOT NULL DEFAULT -1,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'active',
|
||||
fulfillment_config TEXT
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_merchant_product_sku ON merchant_products (merchant_id, sku);
|
||||
CREATE INDEX IF NOT EXISTS idx_merchant_products_deleted_at ON merchant_products (deleted_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_merchant_products_merchant_id ON merchant_products (merchant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_merchant_products_product_id ON merchant_products (product_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_merchant_products_status ON merchant_products (status);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS wallet_accounts (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ,
|
||||
merchant_id BIGINT NOT NULL,
|
||||
currency VARCHAR(12) NOT NULL DEFAULT 'CNY',
|
||||
available_balance BIGINT NOT NULL DEFAULT 0,
|
||||
frozen_balance BIGINT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_wallet_accounts_merchant_id ON wallet_accounts (merchant_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS wallet_ledger_entries (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMPTZ,
|
||||
merchant_id BIGINT NOT NULL,
|
||||
wallet_account_id BIGINT NOT NULL,
|
||||
entry_no VARCHAR(64) NOT NULL,
|
||||
type VARCHAR(24) NOT NULL,
|
||||
amount BIGINT NOT NULL,
|
||||
balance_after BIGINT NOT NULL,
|
||||
reference_type VARCHAR(32) NOT NULL,
|
||||
reference_no VARCHAR(96) NOT NULL,
|
||||
idempotency_key VARCHAR(128),
|
||||
note VARCHAR(255)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_wallet_ledger_entries_entry_no ON wallet_ledger_entries (entry_no);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_wallet_ledger_idempotency ON wallet_ledger_entries (merchant_id, idempotency_key);
|
||||
CREATE INDEX IF NOT EXISTS idx_wallet_ledger_entries_merchant_id ON wallet_ledger_entries (merchant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_wallet_ledger_entries_wallet_account_id ON wallet_ledger_entries (wallet_account_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_wallet_ledger_entries_type ON wallet_ledger_entries (type);
|
||||
CREATE INDEX IF NOT EXISTS idx_wallet_ledger_entries_reference_no ON wallet_ledger_entries (reference_no);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fulfillment_orders (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ,
|
||||
deleted_at TIMESTAMPTZ,
|
||||
merchant_id BIGINT NOT NULL,
|
||||
order_no VARCHAR(64) NOT NULL,
|
||||
client_order_no VARCHAR(96) NOT NULL,
|
||||
merchant_product_id BIGINT NOT NULL,
|
||||
product_sku VARCHAR(96) NOT NULL,
|
||||
product_name VARCHAR(160) NOT NULL,
|
||||
quantity BIGINT NOT NULL DEFAULT 1,
|
||||
amount BIGINT NOT NULL,
|
||||
currency VARCHAR(12) NOT NULL DEFAULT 'CNY',
|
||||
payment_status VARCHAR(16) NOT NULL DEFAULT 'pending',
|
||||
fulfillment_status VARCHAR(16) NOT NULL DEFAULT 'pending',
|
||||
buyer_reference VARCHAR(128),
|
||||
request_data TEXT,
|
||||
result_data TEXT,
|
||||
provider_order_no VARCHAR(96),
|
||||
failure_reason VARCHAR(512),
|
||||
cancelled_at TIMESTAMPTZ,
|
||||
delivered_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_fulfillment_orders_order_no ON fulfillment_orders (order_no);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_order_client_no ON fulfillment_orders (merchant_id, client_order_no);
|
||||
CREATE INDEX IF NOT EXISTS idx_fulfillment_orders_deleted_at ON fulfillment_orders (deleted_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_fulfillment_orders_merchant_id ON fulfillment_orders (merchant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_fulfillment_orders_merchant_product_id ON fulfillment_orders (merchant_product_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_fulfillment_orders_payment_status ON fulfillment_orders (payment_status);
|
||||
CREATE INDEX IF NOT EXISTS idx_fulfillment_orders_fulfillment_status ON fulfillment_orders (fulfillment_status);
|
||||
CREATE INDEX IF NOT EXISTS idx_fulfillment_orders_provider_order_no ON fulfillment_orders (provider_order_no);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fulfillment_jobs (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ,
|
||||
merchant_id BIGINT NOT NULL,
|
||||
order_id BIGINT NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'pending',
|
||||
attempts BIGINT NOT NULL DEFAULT 0,
|
||||
next_run_at TIMESTAMPTZ NOT NULL,
|
||||
provider_order_no VARCHAR(96),
|
||||
request_payload TEXT,
|
||||
result_payload TEXT,
|
||||
last_error VARCHAR(512)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_fulfillment_jobs_order_id ON fulfillment_jobs (order_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_fulfillment_jobs_merchant_id ON fulfillment_jobs (merchant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_fulfillment_jobs_status ON fulfillment_jobs (status);
|
||||
CREATE INDEX IF NOT EXISTS idx_fulfillment_jobs_next_run_at ON fulfillment_jobs (next_run_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_fulfillment_jobs_provider_order_no ON fulfillment_jobs (provider_order_no);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS callback_subscriptions (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ,
|
||||
deleted_at TIMESTAMPTZ,
|
||||
merchant_id BIGINT NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
url VARCHAR(1024) NOT NULL,
|
||||
events TEXT NOT NULL,
|
||||
secret_ciphertext TEXT NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'active'
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_callback_subscriptions_deleted_at ON callback_subscriptions (deleted_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_callback_subscriptions_merchant_id ON callback_subscriptions (merchant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_callback_subscriptions_status ON callback_subscriptions (status);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS callback_deliveries (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ,
|
||||
merchant_id BIGINT NOT NULL,
|
||||
callback_subscription_id BIGINT NOT NULL,
|
||||
event_id VARCHAR(64) NOT NULL,
|
||||
event VARCHAR(64) NOT NULL,
|
||||
payload TEXT NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'pending',
|
||||
attempts BIGINT NOT NULL DEFAULT 0,
|
||||
next_attempt_at TIMESTAMPTZ NOT NULL,
|
||||
last_status_code BIGINT NOT NULL DEFAULT 0,
|
||||
last_response TEXT,
|
||||
delivered_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_callback_deliveries_event_id ON callback_deliveries (event_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_callback_deliveries_merchant_id ON callback_deliveries (merchant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_callback_deliveries_callback_subscription_id ON callback_deliveries (callback_subscription_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_callback_deliveries_event ON callback_deliveries (event);
|
||||
CREATE INDEX IF NOT EXISTS idx_callback_deliveries_status ON callback_deliveries (status);
|
||||
CREATE INDEX IF NOT EXISTS idx_callback_deliveries_next_attempt_at ON callback_deliveries (next_attempt_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS api_request_nonces (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMPTZ,
|
||||
api_client_id BIGINT NOT NULL,
|
||||
nonce VARCHAR(96) NOT NULL,
|
||||
expires_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_api_nonce ON api_request_nonces (api_client_id, nonce);
|
||||
CREATE INDEX IF NOT EXISTS idx_api_request_nonces_api_client_id ON api_request_nonces (api_client_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_api_request_nonces_expires_at ON api_request_nonces (expires_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS audit_logs (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMPTZ,
|
||||
merchant_id BIGINT,
|
||||
actor_user_id BIGINT,
|
||||
api_client_id BIGINT,
|
||||
request_id VARCHAR(64),
|
||||
action VARCHAR(96) NOT NULL,
|
||||
entity_type VARCHAR(64) NOT NULL,
|
||||
entity_id VARCHAR(96) NOT NULL,
|
||||
metadata TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_merchant_id ON audit_logs (merchant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_actor_user_id ON audit_logs (actor_user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_api_client_id ON audit_logs (api_client_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_request_id ON audit_logs (request_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_action ON audit_logs (action);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_entity_type ON audit_logs (entity_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_entity_id ON audit_logs (entity_id);
|
||||
Reference in New Issue
Block a user