352 lines
18 KiB
Go
352 lines
18 KiB
Go
package mysql
|
||
|
||
import (
|
||
"context"
|
||
"database/sql"
|
||
"errors"
|
||
"strings"
|
||
|
||
mysqlDriver "github.com/go-sql-driver/mysql"
|
||
)
|
||
|
||
func ensureResourceShopPurchaseOrderIndexes(ctx context.Context, db *sql.DB) error {
|
||
|
||
if _, err := db.ExecContext(ctx, `
|
||
ALTER TABLE resource_shop_purchase_orders
|
||
ADD INDEX idx_resource_shop_purchase_time (app_code, created_at_ms, order_id)`); err != nil && !isDuplicateKeyNameError(err) {
|
||
return err
|
||
}
|
||
|
||
if _, err := db.ExecContext(ctx, `
|
||
ALTER TABLE resource_shop_purchase_orders
|
||
ADD INDEX idx_resource_shop_purchase_resource_time (app_code, resource_id, created_at_ms, order_id)`); err != nil && !isDuplicateKeyNameError(err) {
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func ensureResourceGrantRevokeSchema(ctx context.Context, db *sql.DB) error {
|
||
if _, err := db.ExecContext(ctx, `
|
||
ALTER TABLE resource_grants
|
||
ADD COLUMN revoked_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '撤销时间,UTC epoch ms' AFTER operator_user_id`); err != nil && !isDuplicateColumnError(err) {
|
||
return err
|
||
}
|
||
if _, err := db.ExecContext(ctx, `
|
||
ALTER TABLE resource_grants
|
||
ADD COLUMN revoked_by_user_id BIGINT NOT NULL DEFAULT 0 COMMENT '撤销操作人用户 ID' AFTER revoked_at_ms`); err != nil && !isDuplicateColumnError(err) {
|
||
return err
|
||
}
|
||
if _, err := db.ExecContext(ctx, `
|
||
ALTER TABLE resource_grants
|
||
ADD COLUMN revoke_reason VARCHAR(256) NOT NULL DEFAULT '' COMMENT '撤销原因' AFTER revoked_by_user_id`); err != nil && !isDuplicateColumnError(err) {
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func isDuplicateKeyNameError(err error) bool {
|
||
var mysqlErr *mysqlDriver.MySQLError
|
||
return errors.As(err, &mysqlErr) && mysqlErr.Number == 1061
|
||
}
|
||
|
||
func ensureHostSalarySettlementSchema(ctx context.Context, db *sql.DB) error {
|
||
|
||
if _, err := db.ExecContext(ctx, `
|
||
ALTER TABLE host_salary_settlement_records
|
||
ADD COLUMN trigger_mode VARCHAR(24) NOT NULL DEFAULT 'automatic' COMMENT '触发方式:automatic/manual' AFTER settlement_type`); err != nil && !isDuplicateColumnError(err) {
|
||
return err
|
||
}
|
||
|
||
if _, err := db.ExecContext(ctx, `
|
||
ALTER TABLE host_salary_settlement_records
|
||
ADD COLUMN settlement_role VARCHAR(24) NOT NULL DEFAULT 'all' COMMENT '结算身份:all/host/agency' AFTER trigger_mode`); err != nil && !isDuplicateColumnError(err) {
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func ensureExternalRechargeSchema(ctx context.Context, db *sql.DB) error {
|
||
if _, err := db.ExecContext(ctx, `
|
||
ALTER TABLE wallet_recharge_products
|
||
ADD COLUMN audience_type VARCHAR(32) NOT NULL DEFAULT 'normal' COMMENT '充值档位适用用户类型:normal/coin_seller' AFTER app_code`); err != nil && !isDuplicateColumnError(err) {
|
||
return err
|
||
}
|
||
if _, err := db.ExecContext(ctx, `
|
||
ALTER TABLE wallet_recharge_products
|
||
ADD COLUMN app_visible BOOLEAN NOT NULL DEFAULT TRUE COMMENT '是否在 App 普通充值列表展示;不影响 Google Play 验单' AFTER status`); err != nil && !isDuplicateColumnError(err) {
|
||
return err
|
||
}
|
||
if _, err := db.ExecContext(ctx, `
|
||
CREATE TABLE IF NOT EXISTS third_party_payment_channels (
|
||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离',
|
||
provider_code VARCHAR(32) NOT NULL COMMENT '三方支付渠道编码',
|
||
provider_name VARCHAR(64) NOT NULL COMMENT '三方支付渠道名称',
|
||
status VARCHAR(32) NOT NULL DEFAULT 'active' COMMENT '状态:active/disabled',
|
||
sort_order INT NOT NULL DEFAULT 0 COMMENT '排序权重',
|
||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||
PRIMARY KEY (app_code, provider_code),
|
||
KEY idx_third_party_payment_channels_status (app_code, status, sort_order)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='三方支付渠道配置表'`); err != nil {
|
||
return err
|
||
}
|
||
if _, err := db.ExecContext(ctx, `
|
||
CREATE TABLE IF NOT EXISTS third_party_payment_methods (
|
||
method_id BIGINT NOT NULL AUTO_INCREMENT COMMENT '支付方式 ID',
|
||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离',
|
||
provider_code VARCHAR(32) NOT NULL COMMENT '三方支付渠道编码',
|
||
country_code VARCHAR(16) NOT NULL COMMENT 'MiFaPay countryCode,GLOBAL 表示全球方式',
|
||
country_name VARCHAR(64) NOT NULL DEFAULT '' COMMENT '国家/地区名称',
|
||
currency_code VARCHAR(8) NOT NULL COMMENT '下单币种',
|
||
pay_way VARCHAR(64) NOT NULL COMMENT 'MiFaPay payWay',
|
||
pay_type VARCHAR(64) NOT NULL COMMENT 'MiFaPay payType',
|
||
method_name VARCHAR(128) NOT NULL COMMENT '支付方式展示名',
|
||
logo_url VARCHAR(512) NOT NULL DEFAULT '' COMMENT '支付方式 logo',
|
||
status VARCHAR(32) NOT NULL DEFAULT 'active' COMMENT '状态:active/disabled',
|
||
usd_to_currency_rate DECIMAL(24,8) NOT NULL DEFAULT 0 COMMENT '1 USD 可兑换本币金额;0 表示未配置,不在 H5 展示',
|
||
sort_order INT NOT NULL DEFAULT 0 COMMENT '排序权重',
|
||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||
PRIMARY KEY (method_id),
|
||
UNIQUE KEY uk_third_party_payment_method (app_code, provider_code, country_code, pay_way, pay_type),
|
||
KEY idx_third_party_payment_methods_country (app_code, provider_code, country_code, status, sort_order)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='三方支付国家和方式配置表'`); err != nil {
|
||
return err
|
||
}
|
||
if _, err := db.ExecContext(ctx, `
|
||
CREATE TABLE IF NOT EXISTS external_recharge_orders (
|
||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离',
|
||
order_id VARCHAR(96) NOT NULL COMMENT '平台外部充值订单 ID',
|
||
command_id VARCHAR(128) NOT NULL COMMENT '客户端命令幂等 ID',
|
||
target_user_id BIGINT NOT NULL COMMENT '充值目标用户 ID',
|
||
target_region_id BIGINT NOT NULL COMMENT '充值目标区域 ID',
|
||
target_country_id BIGINT NOT NULL DEFAULT 0 COMMENT '充值目标国家 ID',
|
||
target_country_code VARCHAR(16) NOT NULL DEFAULT '' COMMENT '充值目标国家码',
|
||
audience_type VARCHAR(32) NOT NULL DEFAULT 'normal' COMMENT '商品适用用户类型',
|
||
product_id BIGINT NOT NULL COMMENT '充值商品 ID',
|
||
product_code VARCHAR(128) NOT NULL DEFAULT '' COMMENT '充值商品编码快照',
|
||
product_name VARCHAR(128) NOT NULL DEFAULT '' COMMENT '充值商品名称快照',
|
||
coin_amount BIGINT NOT NULL COMMENT '到账金币数量',
|
||
usd_minor_amount BIGINT NOT NULL COMMENT '美元最小单位金额',
|
||
provider_code VARCHAR(32) NOT NULL COMMENT '支付渠道:mifapay/usdt_trc20',
|
||
payment_method_id BIGINT NOT NULL DEFAULT 0 COMMENT '三方支付方式 ID',
|
||
country_code VARCHAR(16) NOT NULL DEFAULT '' COMMENT '支付国家码',
|
||
currency_code VARCHAR(8) NOT NULL DEFAULT '' COMMENT '支付币种',
|
||
provider_amount_minor BIGINT NOT NULL DEFAULT 0 COMMENT '三方下单金额最小单位',
|
||
pay_way VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'MiFaPay payWay',
|
||
pay_type VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'MiFaPay payType',
|
||
pay_url TEXT NULL COMMENT '三方收银台地址',
|
||
provider_order_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT '三方订单号',
|
||
tx_hash VARCHAR(128) NOT NULL DEFAULT '' COMMENT 'USDT-TRC20 交易哈希',
|
||
receive_address VARCHAR(128) NOT NULL DEFAULT '' COMMENT 'USDT 收款地址',
|
||
status VARCHAR(32) NOT NULL DEFAULT 'pending' COMMENT 'pending/redirected/credited/failed',
|
||
failure_reason VARCHAR(512) NOT NULL DEFAULT '' COMMENT '失败原因',
|
||
wallet_transaction_id VARCHAR(96) NOT NULL DEFAULT '' COMMENT '入账钱包交易 ID',
|
||
provider_payload JSON NULL COMMENT '三方原始响应或链上交易快照',
|
||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||
PRIMARY KEY (app_code, order_id),
|
||
UNIQUE KEY uk_external_recharge_command (app_code, command_id),
|
||
KEY idx_external_recharge_user_time (app_code, target_user_id, created_at_ms),
|
||
KEY idx_external_recharge_provider_order (app_code, provider_code, provider_order_id),
|
||
KEY idx_external_recharge_wallet_tx (app_code, wallet_transaction_id),
|
||
KEY idx_external_recharge_reconcile (app_code, status, provider_code, updated_at_ms, created_at_ms),
|
||
KEY idx_external_recharge_tx (app_code, provider_code, tx_hash)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='H5 外部充值订单表'`); err != nil {
|
||
return err
|
||
}
|
||
if _, err := db.ExecContext(ctx, `
|
||
ALTER TABLE external_recharge_orders
|
||
ADD COLUMN target_country_id BIGINT NOT NULL DEFAULT 0 COMMENT '充值目标国家 ID' AFTER target_region_id`); err != nil && !isDuplicateColumnError(err) {
|
||
return err
|
||
}
|
||
return seedThirdPartyPaymentDefaults(ctx, db)
|
||
}
|
||
|
||
// ensureGooglePaymentPaidDetailsSchema 给 payment_orders 补 Google Orders API 实付明细列;
|
||
// 财务列表按 wallet_transaction_id 反查 Google 支付订单,需要独立索引避免回表扫描。
|
||
func ensureGooglePaymentPaidDetailsSchema(ctx context.Context, db *sql.DB) error {
|
||
alters := []string{
|
||
`ALTER TABLE payment_orders ADD COLUMN paid_currency_code VARCHAR(8) NOT NULL DEFAULT '' COMMENT '用户实付币种,来自 Google Orders API' AFTER amount_micro`,
|
||
`ALTER TABLE payment_orders ADD COLUMN paid_amount_micro BIGINT NOT NULL DEFAULT 0 COMMENT '用户实付总额微单位(实付币种)' AFTER paid_currency_code`,
|
||
`ALTER TABLE payment_orders ADD COLUMN paid_tax_micro BIGINT NOT NULL DEFAULT 0 COMMENT '订单税额微单位(实付币种)' AFTER paid_amount_micro`,
|
||
`ALTER TABLE payment_orders ADD COLUMN paid_net_micro BIGINT NOT NULL DEFAULT 0 COMMENT 'Google 扣费后开发者净收入微单位(实付币种)' AFTER paid_tax_micro`,
|
||
`ALTER TABLE payment_orders ADD COLUMN paid_synced_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '实付明细同步时间,UTC epoch ms;0 表示未同步' AFTER paid_net_micro`,
|
||
}
|
||
for _, stmt := range alters {
|
||
if _, err := db.ExecContext(ctx, stmt); err != nil && !isDuplicateColumnError(err) {
|
||
return err
|
||
}
|
||
}
|
||
if _, err := db.ExecContext(ctx, `
|
||
ALTER TABLE payment_orders
|
||
ADD INDEX idx_payment_orders_wallet_tx (app_code, wallet_transaction_id)`); err != nil && !isDuplicateKeyNameError(err) {
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func ensureWalletPolicyInstanceSchema(ctx context.Context, db *sql.DB) error {
|
||
_, err := db.ExecContext(ctx, `
|
||
CREATE TABLE IF NOT EXISTS wallet_policy_instances (
|
||
app_code VARCHAR(32) NOT NULL COMMENT '应用编码',
|
||
instance_code VARCHAR(96) NOT NULL COMMENT '后台策略实例编码',
|
||
template_code VARCHAR(96) NOT NULL COMMENT '模板编码快照',
|
||
template_version VARCHAR(64) NOT NULL COMMENT '模板版本快照',
|
||
region_id BIGINT NOT NULL DEFAULT 0 COMMENT '适用地区,0 表示 App 兜底',
|
||
status VARCHAR(24) NOT NULL DEFAULT 'active' COMMENT 'active/disabled',
|
||
effective_from_ms BIGINT NOT NULL DEFAULT 0 COMMENT 'UTC epoch ms',
|
||
effective_to_ms BIGINT NOT NULL DEFAULT 0 COMMENT '0 表示长期有效',
|
||
host_point_asset_type VARCHAR(32) NOT NULL DEFAULT 'POINT' COMMENT '主播收益积分资产',
|
||
host_point_ratio_ppm BIGINT NOT NULL DEFAULT 700000 COMMENT '有效付费礼物转 POINT 比例,ppm',
|
||
points_per_usd BIGINT NOT NULL DEFAULT 100000 COMMENT 'POINT/USD 展示换算比例',
|
||
withdraw_fee_bps INT NOT NULL DEFAULT 500 COMMENT '提现手续费 bps',
|
||
rule_json JSON NOT NULL COMMENT '完整政策快照',
|
||
published_by_admin_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
|
||
published_at_ms BIGINT NOT NULL DEFAULT 0,
|
||
created_at_ms BIGINT NOT NULL,
|
||
updated_at_ms BIGINT NOT NULL,
|
||
PRIMARY KEY (app_code, instance_code, region_id),
|
||
KEY idx_wallet_policy_active (app_code, region_id, status, effective_from_ms, effective_to_ms)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='钱包运行侧收益政策实例表'`)
|
||
return err
|
||
}
|
||
|
||
func ensureGiftDiamondRatioSchema(ctx context.Context, db *sql.DB) error {
|
||
if _, err := db.ExecContext(ctx, `
|
||
CREATE TABLE IF NOT EXISTS gift_diamond_ratio_configs (
|
||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离',
|
||
region_id BIGINT NOT NULL DEFAULT 0 COMMENT '房间可见区域,0 表示全局默认',
|
||
gift_type_code VARCHAR(32) NOT NULL COMMENT '礼物类型编码',
|
||
status VARCHAR(32) NOT NULL DEFAULT 'active' COMMENT '业务状态',
|
||
ratio_percent DECIMAL(5,2) NOT NULL DEFAULT 100.00 COMMENT '房间贡献热度和主播周期钻石入账比例,百分比',
|
||
coin_return_ratio_percent DECIMAL(5,2) NOT NULL DEFAULT 0.00 COMMENT '收礼人金币返还比例,百分比',
|
||
created_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '创建后台用户 ID',
|
||
updated_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '更新后台用户 ID',
|
||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||
PRIMARY KEY (app_code, region_id, gift_type_code),
|
||
KEY idx_gift_diamond_ratio_region (app_code, region_id, status)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='礼物钻石和房间贡献比例配置表'`); err != nil {
|
||
return err
|
||
}
|
||
if added, err := ensureGiftReturnCoinRatioColumn(ctx, db); err != nil {
|
||
return err
|
||
} else if added {
|
||
|
||
if err := seedGiftReturnCoinRatioDefaults(ctx, db); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
_, err := db.ExecContext(ctx, `
|
||
INSERT IGNORE INTO gift_diamond_ratio_configs (
|
||
app_code, region_id, gift_type_code, status, ratio_percent, coin_return_ratio_percent,
|
||
created_by_admin_id, updated_by_admin_id, created_at_ms, updated_at_ms
|
||
) VALUES
|
||
('lalu', 0, 'normal', 'active', 100.00, 30.00, 0, 0, 0, 0),
|
||
('lalu', 0, 'lucky', 'active', 10.00, 10.00, 0, 0, 0, 0),
|
||
('lalu', 0, 'super_lucky', 'active', 1.00, 1.00, 0, 0, 0, 0)`)
|
||
return err
|
||
}
|
||
|
||
func ensureGiftReturnCoinRatioColumn(ctx context.Context, db *sql.DB) (bool, error) {
|
||
|
||
if _, err := db.ExecContext(ctx, `
|
||
ALTER TABLE gift_diamond_ratio_configs
|
||
ADD COLUMN coin_return_ratio_percent DECIMAL(5,2) NOT NULL DEFAULT 0.00 COMMENT '收礼人金币返还比例,百分比' AFTER ratio_percent`); err != nil {
|
||
if isDuplicateColumnError(err) {
|
||
return false, nil
|
||
}
|
||
return false, err
|
||
}
|
||
return true, nil
|
||
}
|
||
|
||
func seedGiftReturnCoinRatioDefaults(ctx context.Context, db *sql.DB) error {
|
||
|
||
_, err := db.ExecContext(ctx, `
|
||
UPDATE gift_diamond_ratio_configs
|
||
SET coin_return_ratio_percent = CASE gift_type_code
|
||
WHEN 'lucky' THEN 10.00
|
||
WHEN 'super_lucky' THEN 1.00
|
||
ELSE 30.00
|
||
END`)
|
||
return err
|
||
}
|
||
|
||
func ensureCoinSellerSalaryExchangeRateSchema(ctx context.Context, db *sql.DB) error {
|
||
_, err := db.ExecContext(ctx, `
|
||
CREATE TABLE IF NOT EXISTS coin_seller_salary_exchange_rate_tiers (
|
||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离',
|
||
region_id BIGINT NOT NULL COMMENT '币商所属区域 ID',
|
||
tier_id BIGINT NOT NULL AUTO_INCREMENT COMMENT '区间 ID',
|
||
min_usd_minor BIGINT NOT NULL COMMENT '起始美元金额,单位美分,包含',
|
||
max_usd_minor BIGINT NOT NULL COMMENT '结束美元金额,单位美分,包含',
|
||
coin_per_usd BIGINT NOT NULL COMMENT '每 1 USD 工资可兑换的币商专用金币数量',
|
||
status VARCHAR(24) NOT NULL DEFAULT 'active' COMMENT '状态:active/disabled',
|
||
sort_order INT NOT NULL DEFAULT 0 COMMENT '排序权重',
|
||
created_by_user_id BIGINT NOT NULL DEFAULT 0 COMMENT '创建人用户 ID',
|
||
updated_by_user_id BIGINT NOT NULL DEFAULT 0 COMMENT '更新人用户 ID',
|
||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||
PRIMARY KEY (tier_id),
|
||
KEY idx_coin_seller_salary_rates_region (app_code, region_id, status, min_usd_minor, max_usd_minor),
|
||
KEY idx_coin_seller_salary_rates_sort (app_code, region_id, status, sort_order)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='币商工资兑换比例区间表'`)
|
||
return err
|
||
}
|
||
|
||
func ensureUserResourceEquipmentSchema(ctx context.Context, db *sql.DB) error {
|
||
|
||
if _, err := db.ExecContext(ctx, `
|
||
CREATE TABLE IF NOT EXISTS user_resource_equipment (
|
||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离',
|
||
user_id BIGINT NOT NULL COMMENT '用户 ID',
|
||
resource_type VARCHAR(32) NOT NULL COMMENT '资源类型',
|
||
entitlement_id VARCHAR(96) NOT NULL COMMENT '权益 ID',
|
||
resource_id BIGINT NOT NULL COMMENT '资源 ID',
|
||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||
PRIMARY KEY (app_code, user_id, resource_type, entitlement_id),
|
||
KEY idx_user_resource_equipment_user (app_code, user_id, updated_at_ms)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户资源装备表'`); err != nil {
|
||
return err
|
||
}
|
||
|
||
primaryColumns, err := tablePrimaryKeyColumns(ctx, db, "user_resource_equipment")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if strings.Join(primaryColumns, ",") == "app_code,user_id,resource_type" {
|
||
_, err = db.ExecContext(ctx, `
|
||
ALTER TABLE user_resource_equipment
|
||
DROP PRIMARY KEY,
|
||
ADD PRIMARY KEY (app_code, user_id, resource_type, entitlement_id)`)
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func tablePrimaryKeyColumns(ctx context.Context, db *sql.DB, tableName string) ([]string, error) {
|
||
rows, err := db.QueryContext(ctx, `
|
||
SELECT COLUMN_NAME
|
||
FROM information_schema.KEY_COLUMN_USAGE
|
||
WHERE TABLE_SCHEMA = DATABASE()
|
||
AND TABLE_NAME = ?
|
||
AND CONSTRAINT_NAME = 'PRIMARY'
|
||
ORDER BY ORDINAL_POSITION ASC`, tableName)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
|
||
columns := make([]string, 0, 4)
|
||
for rows.Next() {
|
||
var column string
|
||
if err := rows.Scan(&column); err != nil {
|
||
return nil, err
|
||
}
|
||
columns = append(columns, column)
|
||
}
|
||
return columns, rows.Err()
|
||
}
|