城市合伙人端的修改(后台)
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
/** 清空开城城市及关联数据(保留 HQ 账户、商品目录等) */
|
||||
async function main() {
|
||||
await prisma.logPartnerAnalytics.deleteMany();
|
||||
await prisma.redeemRecord.deleteMany();
|
||||
await prisma.benefitCoupon.deleteMany();
|
||||
await prisma.orderDelivery.deleteMany();
|
||||
await prisma.order.deleteMany();
|
||||
await prisma.commonEvent.deleteMany({ where: { refType: { in: ['CITY', 'WAREHOUSE', 'PARTNER', 'PARTNER_ACCOUNT', 'STORE'] } } });
|
||||
await prisma.storeAccount.deleteMany();
|
||||
await prisma.store.deleteMany();
|
||||
await prisma.partnerBill.deleteMany();
|
||||
await prisma.cityWarehouse.deleteMany();
|
||||
await prisma.partnerAccount.deleteMany();
|
||||
const result = await prisma.commonCity.deleteMany();
|
||||
console.log(`Cleared ${result.count} cities and related open-city data.`);
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
@@ -148,25 +148,7 @@ CREATE TABLE common_promo_code (
|
||||
UNIQUE KEY uk_common_promo_code_code (code)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='推广码';
|
||||
|
||||
-- ===================== PARTNER(先于 city/store) =============================
|
||||
|
||||
DROP TABLE IF EXISTS partner_partner;
|
||||
CREATE TABLE partner_partner (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
company_name VARCHAR(128) NOT NULL,
|
||||
address VARCHAR(256) NOT NULL,
|
||||
contact_phone VARCHAR(20) NOT NULL,
|
||||
contract_no VARCHAR(64) DEFAULT NULL COMMENT '合同编号',
|
||||
contract_signed_at DATETIME(3) DEFAULT NULL,
|
||||
contract_expire_at DATETIME(3) DEFAULT NULL,
|
||||
bank_account_name VARCHAR(64) DEFAULT NULL,
|
||||
bank_account_no VARCHAR(32) DEFAULT NULL,
|
||||
bank_branch VARCHAR(128) DEFAULT NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_partner_partner_phone (contact_phone)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='城市合伙人主体';
|
||||
-- ===================== PARTNER(城市合伙人主账号 + 子账号) =============================
|
||||
|
||||
DROP TABLE IF EXISTS common_city;
|
||||
CREATE TABLE common_city (
|
||||
@@ -175,71 +157,99 @@ CREATE TABLE common_city (
|
||||
name VARCHAR(64) NOT NULL,
|
||||
province VARCHAR(32) NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'PENDING',
|
||||
partner_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
local_min_qty INT NOT NULL DEFAULT 2,
|
||||
cross_min_qty INT NOT NULL DEFAULT 6,
|
||||
max_partner_commission_rate DECIMAL(5,4) NOT NULL DEFAULT 0.0500 COMMENT '订单+核销佣金合计上限',
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_common_city_code (code),
|
||||
KEY idx_common_city_partner (partner_id),
|
||||
CONSTRAINT fk_common_city_partner FOREIGN KEY (partner_id) REFERENCES partner_partner(id) ON DELETE SET NULL
|
||||
UNIQUE KEY uk_common_city_code (code)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='开城配置';
|
||||
|
||||
DROP TABLE IF EXISTS common_city_commission_rule;
|
||||
CREATE TABLE common_city_commission_rule (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
city_id BIGINT UNSIGNED NOT NULL,
|
||||
order_commission_rate DECIMAL(5,4) NOT NULL DEFAULT 0.0000,
|
||||
redeem_commission_rate DECIMAL(5,4) NOT NULL DEFAULT 0.0000,
|
||||
partner_profit_rate DECIMAL(5,4) NOT NULL DEFAULT 0.3500,
|
||||
store_settlement_rate DECIMAL(5,4) NOT NULL DEFAULT 0.6000,
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_common_city_commission_city (city_id),
|
||||
CONSTRAINT fk_common_city_commission_city FOREIGN KEY (city_id) REFERENCES common_city(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='城市佣金规则';
|
||||
|
||||
DROP TABLE IF EXISTS partner_account;
|
||||
CREATE TABLE partner_account (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
partner_id BIGINT UNSIGNED NOT NULL,
|
||||
phone VARCHAR(20) NOT NULL,
|
||||
name VARCHAR(64) NOT NULL,
|
||||
wx_open_id VARCHAR(64) DEFAULT NULL,
|
||||
wx_union_id VARCHAR(64) DEFAULT NULL,
|
||||
is_primary TINYINT NOT NULL DEFAULT 0,
|
||||
parent_account_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
staff_role VARCHAR(16) DEFAULT NULL COMMENT 'PARTNER|INTERNAL|PROMOTER',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
|
||||
last_login_at DATETIME(3) DEFAULT NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
phone VARCHAR(20) NOT NULL,
|
||||
name VARCHAR(64) NOT NULL,
|
||||
wx_open_id VARCHAR(64) DEFAULT NULL,
|
||||
wx_union_id VARCHAR(64) DEFAULT NULL,
|
||||
is_primary TINYINT NOT NULL DEFAULT 0,
|
||||
parent_account_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
staff_role VARCHAR(16) DEFAULT NULL COMMENT 'PARTNER|INTERNAL|PROMOTER',
|
||||
permissions JSON DEFAULT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
|
||||
last_login_at DATETIME(3) DEFAULT NULL,
|
||||
city_id BIGINT UNSIGNED DEFAULT NULL COMMENT '主账号绑定城市',
|
||||
scope_type VARCHAR(16) DEFAULT NULL COMMENT 'CITY_WIDE|DISTRICT',
|
||||
district_codes JSON DEFAULT NULL,
|
||||
order_commission_rate DECIMAL(5,4) DEFAULT 0.0000,
|
||||
redeem_commission_rate DECIMAL(5,4) DEFAULT 0.0300,
|
||||
binding_status VARCHAR(16) DEFAULT 'ACTIVE',
|
||||
company_name VARCHAR(128) DEFAULT NULL,
|
||||
address VARCHAR(256) DEFAULT NULL,
|
||||
contact_phone VARCHAR(20) DEFAULT NULL,
|
||||
contract_no VARCHAR(64) DEFAULT NULL,
|
||||
contract_signed_at DATETIME(3) DEFAULT NULL,
|
||||
contract_expire_at DATETIME(3) DEFAULT NULL,
|
||||
bank_account_name VARCHAR(64) DEFAULT NULL,
|
||||
bank_account_no VARCHAR(32) DEFAULT NULL,
|
||||
bank_branch VARCHAR(128) DEFAULT NULL,
|
||||
weekly_store_target INT DEFAULT 20,
|
||||
managed_warehouse_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_partner_account_phone (phone),
|
||||
KEY idx_partner_account_partner (partner_id),
|
||||
CONSTRAINT fk_partner_account_partner FOREIGN KEY (partner_id) REFERENCES partner_partner(id) ON DELETE RESTRICT,
|
||||
UNIQUE KEY uk_partner_account_managed_warehouse (managed_warehouse_id),
|
||||
KEY idx_partner_account_city_scope (city_id, scope_type),
|
||||
KEY idx_partner_account_city_primary (city_id, is_primary),
|
||||
KEY idx_partner_account_parent (parent_account_id),
|
||||
KEY idx_partner_account_contact_phone (contact_phone),
|
||||
CONSTRAINT fk_partner_account_city FOREIGN KEY (city_id) REFERENCES common_city(id) ON DELETE RESTRICT,
|
||||
CONSTRAINT fk_partner_account_parent FOREIGN KEY (parent_account_id) REFERENCES partner_account(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='合伙人账号';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='城市合伙人账号(主账号=实体+城市绑定)';
|
||||
|
||||
DROP TABLE IF EXISTS common_city_warehouse;
|
||||
CREATE TABLE common_city_warehouse (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
city_id BIGINT UNSIGNED NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
address VARCHAR(256) NOT NULL,
|
||||
contact_name VARCHAR(64) NOT NULL,
|
||||
contact_phone VARCHAR(20) NOT NULL,
|
||||
manager_type VARCHAR(16) NOT NULL COMMENT 'HQ|PARTNER',
|
||||
partner_account_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_city_warehouse_city (city_id),
|
||||
KEY idx_city_warehouse_partner_account (partner_account_id),
|
||||
CONSTRAINT fk_city_warehouse_city FOREIGN KEY (city_id) REFERENCES common_city(id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_city_warehouse_partner_account FOREIGN KEY (partner_account_id) REFERENCES partner_account(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='城市仓库';
|
||||
|
||||
ALTER TABLE partner_account
|
||||
ADD CONSTRAINT fk_partner_account_managed_warehouse FOREIGN KEY (managed_warehouse_id) REFERENCES common_city_warehouse(id) ON DELETE SET NULL;
|
||||
|
||||
DROP TABLE IF EXISTS partner_bill;
|
||||
CREATE TABLE partner_bill (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
bill_no VARCHAR(32) NOT NULL,
|
||||
partner_id BIGINT UNSIGNED NOT NULL,
|
||||
period_start DATETIME(3) NOT NULL,
|
||||
period_end DATETIME(3) NOT NULL,
|
||||
order_commission DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '下单佣金汇总',
|
||||
redeem_commission DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '核销佣金汇总',
|
||||
total_amount DECIMAL(10,2) NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'DRAFT',
|
||||
confirmed_at DATETIME(3) DEFAULT NULL,
|
||||
paid_at DATETIME(3) DEFAULT NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
bill_no VARCHAR(32) NOT NULL,
|
||||
partner_account_id BIGINT UNSIGNED NOT NULL,
|
||||
period_start DATETIME(3) NOT NULL,
|
||||
period_end DATETIME(3) NOT NULL,
|
||||
order_commission DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '下单佣金汇总',
|
||||
redeem_commission DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '核销佣金汇总',
|
||||
total_amount DECIMAL(10,2) NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'DRAFT',
|
||||
confirmed_at DATETIME(3) DEFAULT NULL,
|
||||
paid_at DATETIME(3) DEFAULT NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_partner_bill_no (bill_no),
|
||||
KEY idx_partner_bill_partner_status (partner_id, status),
|
||||
CONSTRAINT fk_partner_bill_partner FOREIGN KEY (partner_id) REFERENCES partner_partner(id) ON DELETE RESTRICT
|
||||
KEY idx_partner_bill_account_status (partner_account_id, status),
|
||||
CONSTRAINT fk_partner_bill_account FOREIGN KEY (partner_account_id) REFERENCES partner_account(id) ON DELETE RESTRICT
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='合伙人T+30账单';
|
||||
|
||||
-- ===================== HQ =============================
|
||||
@@ -345,7 +355,7 @@ DROP TABLE IF EXISTS store_store;
|
||||
CREATE TABLE store_store (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
city_id BIGINT UNSIGNED NOT NULL,
|
||||
partner_id BIGINT UNSIGNED NOT NULL,
|
||||
partner_account_id BIGINT UNSIGNED NOT NULL,
|
||||
category_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
phone VARCHAR(20) NOT NULL,
|
||||
@@ -366,13 +376,14 @@ CREATE TABLE store_store (
|
||||
bank_account_name VARCHAR(64) DEFAULT NULL,
|
||||
bank_account_no VARCHAR(32) DEFAULT NULL,
|
||||
bank_branch VARCHAR(128) DEFAULT NULL,
|
||||
settlement_rate DECIMAL(5,4) NOT NULL DEFAULT 0.6000 COMMENT '门店核销结算比例',
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_store_store_city_status (city_id, status),
|
||||
KEY idx_store_store_partner (partner_id),
|
||||
KEY idx_store_store_partner_account (partner_account_id),
|
||||
CONSTRAINT fk_store_store_city FOREIGN KEY (city_id) REFERENCES common_city(id) ON DELETE RESTRICT,
|
||||
CONSTRAINT fk_store_store_partner FOREIGN KEY (partner_id) REFERENCES partner_partner(id) ON DELETE RESTRICT,
|
||||
CONSTRAINT fk_store_store_partner_account FOREIGN KEY (partner_account_id) REFERENCES partner_account(id) ON DELETE RESTRICT,
|
||||
CONSTRAINT fk_store_store_category FOREIGN KEY (category_id) REFERENCES common_store_category(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='餐饮门店';
|
||||
|
||||
@@ -446,7 +457,9 @@ CREATE TABLE user_order (
|
||||
shipped_at DATETIME(3) DEFAULT NULL COMMENT '发货时间(冗余=user_order_delivery.shipping_at)',
|
||||
completed_at DATETIME(3) DEFAULT NULL COMMENT '完成时间',
|
||||
cancelled_at DATETIME(3) DEFAULT NULL COMMENT '取消时间',
|
||||
pay_expire_at DATETIME(3) DEFAULT NULL COMMENT '待付款过期时间',
|
||||
pay_expire_at DATETIME(3) DEFAULT NULL COMMENT '待付款过期时间',
|
||||
partner_account_id_at_pay BIGINT UNSIGNED DEFAULT NULL,
|
||||
order_commission_rate_at_pay DECIMAL(5,4) DEFAULT NULL,
|
||||
remark VARCHAR(512) DEFAULT NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* 将 legacy common_city.partner_id 迁移至 common_city_partner(CITY_WIDE)。
|
||||
* 在 prisma db push 移除 partner_id 列之前运行;若列已不存在则跳过数据拷贝。
|
||||
*
|
||||
* 用法:cd server/dukang-api && pnpm prisma:migrate-city-partner
|
||||
*/
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function columnExists(table: string, column: string): Promise<boolean> {
|
||||
const rows = await prisma.$queryRawUnsafe<Array<{ cnt: bigint }>>(
|
||||
`SELECT COUNT(*) AS cnt FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?`,
|
||||
table,
|
||||
column,
|
||||
);
|
||||
return Number(rows[0]?.cnt ?? 0) > 0;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const hasPartnerId = await columnExists('common_city', 'partner_id');
|
||||
if (!hasPartnerId) {
|
||||
console.log('[migrate-city-partner] common_city.partner_id 已移除,跳过迁移');
|
||||
return;
|
||||
}
|
||||
|
||||
const cities = await prisma.$queryRawUnsafe<
|
||||
Array<{ id: bigint; partner_id: bigint | null }>
|
||||
>(`SELECT id, partner_id FROM common_city WHERE partner_id IS NOT NULL`);
|
||||
|
||||
let migrated = 0;
|
||||
for (const row of cities) {
|
||||
const cityId = row.id;
|
||||
const partnerId = row.partner_id!;
|
||||
|
||||
const existing = await prisma.cityPartner.findFirst({
|
||||
where: { cityId, partnerId },
|
||||
});
|
||||
if (existing) continue;
|
||||
|
||||
const rule = await prisma.commonCityCommissionRule.findUnique({ where: { cityId } });
|
||||
await prisma.cityPartner.create({
|
||||
data: {
|
||||
cityId,
|
||||
partnerId,
|
||||
scopeType: 'CITY_WIDE',
|
||||
orderCommissionRate: rule ? Number((rule as { orderCommissionRate?: unknown }).orderCommissionRate ?? 0) : 0,
|
||||
redeemCommissionRate: rule ? Number((rule as { redeemCommissionRate?: unknown }).redeemCommissionRate ?? 0.03) : 0.03,
|
||||
status: 'ACTIVE',
|
||||
},
|
||||
});
|
||||
migrated += 1;
|
||||
}
|
||||
|
||||
console.log(`[migrate-city-partner] 已迁移 ${migrated} 条 CITY_WIDE 绑定`);
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(() => prisma.$disconnect());
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Partner architecture unification migration stub.
|
||||
*
|
||||
* Schema change: Partner / CityPartner / CommonCityCommissionRule removed;
|
||||
* PartnerAccount is the primary entity; Store.partnerAccountId;
|
||||
* Order.partnerAccountIdAtPay.
|
||||
*
|
||||
* This is a breaking schema change — there is no incremental SQL migration path
|
||||
* from the legacy v3.1 tables. Apply on a fresh or disposable database:
|
||||
*
|
||||
* cd server/dukang-api
|
||||
* npx prisma db push --force-reset
|
||||
* pnpm prisma:seed
|
||||
*
|
||||
* Do NOT run --force-reset against production.
|
||||
*/
|
||||
|
||||
async function main() {
|
||||
console.log(`
|
||||
Partner account unification requires a full schema reset.
|
||||
|
||||
Run:
|
||||
cd server/dukang-api
|
||||
npx prisma db push --force-reset
|
||||
pnpm prisma:seed
|
||||
`);
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -106,6 +106,26 @@ enum CityStatus {
|
||||
PAUSED
|
||||
}
|
||||
|
||||
enum CityPartnerScopeType {
|
||||
CITY_WIDE
|
||||
DISTRICT
|
||||
}
|
||||
|
||||
enum CityPartnerStatus {
|
||||
ACTIVE
|
||||
PAUSED
|
||||
}
|
||||
|
||||
enum WarehouseManagerType {
|
||||
HQ
|
||||
PARTNER
|
||||
}
|
||||
|
||||
enum WarehouseStatus {
|
||||
ACTIVE
|
||||
PAUSED
|
||||
}
|
||||
|
||||
enum PartnerStaffRole {
|
||||
PARTNER
|
||||
INTERNAL
|
||||
@@ -391,103 +411,109 @@ model CommonCity {
|
||||
name String @db.VarChar(64)
|
||||
province String @db.VarChar(32)
|
||||
status CityStatus @default(PENDING)
|
||||
partnerId BigInt? @map("partner_id") @db.UnsignedBigInt
|
||||
localMinQty Int @default(2) @map("local_min_qty")
|
||||
crossMinQty Int @default(6) @map("cross_min_qty")
|
||||
maxPartnerCommissionRate Decimal @default(0.05) @map("max_partner_commission_rate") @db.Decimal(5, 4)
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||||
|
||||
partner Partner? @relation(fields: [partnerId], references: [id], onDelete: SetNull)
|
||||
commissionRule CommonCityCommissionRule?
|
||||
stores Store[]
|
||||
orders Order[]
|
||||
warehouses CityWarehouse[]
|
||||
partnerAccounts PartnerAccount[] @relation("PartnerAccountCity")
|
||||
stores Store[]
|
||||
orders Order[]
|
||||
|
||||
@@index([partnerId])
|
||||
@@map("common_city")
|
||||
}
|
||||
|
||||
model CommonCityCommissionRule {
|
||||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||||
cityId BigInt @unique @map("city_id") @db.UnsignedBigInt
|
||||
orderCommissionRate Decimal @default(0) @map("order_commission_rate") @db.Decimal(5, 4)
|
||||
redeemCommissionRate Decimal @default(0) @map("redeem_commission_rate") @db.Decimal(5, 4)
|
||||
partnerProfitRate Decimal @default(0.35) @map("partner_profit_rate") @db.Decimal(5, 4)
|
||||
storeSettlementRate Decimal @default(0.60) @map("store_settlement_rate") @db.Decimal(5, 4)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||||
model CityWarehouse {
|
||||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||||
cityId BigInt @map("city_id") @db.UnsignedBigInt
|
||||
name String @db.VarChar(128)
|
||||
address String @db.VarChar(256)
|
||||
contactName String @map("contact_name") @db.VarChar(64)
|
||||
contactPhone String @map("contact_phone") @db.VarChar(20)
|
||||
managerType WarehouseManagerType @map("manager_type")
|
||||
partnerAccountId BigInt? @map("partner_account_id") @db.UnsignedBigInt
|
||||
status WarehouseStatus @default(ACTIVE)
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||||
|
||||
city CommonCity @relation(fields: [cityId], references: [id], onDelete: Cascade)
|
||||
city CommonCity @relation(fields: [cityId], references: [id], onDelete: Cascade)
|
||||
partnerAccount PartnerAccount? @relation("WarehouseManager", fields: [partnerAccountId], references: [id], onDelete: SetNull)
|
||||
managedBy PartnerAccount? @relation("ManagedWarehouse")
|
||||
|
||||
@@map("common_city_commission_rule")
|
||||
@@index([cityId])
|
||||
@@index([partnerAccountId])
|
||||
@@map("common_city_warehouse")
|
||||
}
|
||||
|
||||
// ─── PARTNER ──────────────────────────────────────────
|
||||
|
||||
model Partner {
|
||||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||||
companyName String @map("company_name") @db.VarChar(128)
|
||||
address String @db.VarChar(256)
|
||||
contactPhone String @map("contact_phone") @db.VarChar(20)
|
||||
contractNo String? @map("contract_no") @db.VarChar(64)
|
||||
contractSignedAt DateTime? @map("contract_signed_at") @db.DateTime(3)
|
||||
contractExpireAt DateTime? @map("contract_expire_at") @db.DateTime(3)
|
||||
bankAccountName String? @map("bank_account_name") @db.VarChar(64)
|
||||
bankAccountNo String? @map("bank_account_no") @db.VarChar(32)
|
||||
bankBranch String? @map("bank_branch") @db.VarChar(128)
|
||||
weeklyStoreTarget Int? @default(20) @map("weekly_store_target")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||||
|
||||
cities CommonCity[]
|
||||
accounts PartnerAccount[]
|
||||
stores Store[]
|
||||
bills PartnerBill[]
|
||||
|
||||
@@index([contactPhone])
|
||||
@@map("partner_partner")
|
||||
}
|
||||
|
||||
model PartnerAccount {
|
||||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||||
partnerId BigInt @map("partner_id") @db.UnsignedBigInt
|
||||
phone String @unique @db.VarChar(20)
|
||||
name String @db.VarChar(64)
|
||||
wxOpenId String? @map("wx_open_id") @db.VarChar(64)
|
||||
wxUnionId String? @map("wx_union_id") @db.VarChar(64)
|
||||
isPrimary Int @default(0) @map("is_primary") @db.TinyInt
|
||||
parentAccountId BigInt? @map("parent_account_id") @db.UnsignedBigInt
|
||||
staffRole PartnerStaffRole? @map("staff_role")
|
||||
status AccountStatus @default(ACTIVE)
|
||||
lastLoginAt DateTime? @map("last_login_at") @db.DateTime(3)
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||||
phone String @unique @db.VarChar(20)
|
||||
name String @db.VarChar(64)
|
||||
wxOpenId String? @map("wx_open_id") @db.VarChar(64)
|
||||
wxUnionId String? @map("wx_union_id") @db.VarChar(64)
|
||||
isPrimary Int @default(0) @map("is_primary") @db.TinyInt
|
||||
parentAccountId BigInt? @map("parent_account_id") @db.UnsignedBigInt
|
||||
staffRole PartnerStaffRole? @map("staff_role")
|
||||
permissions Json?
|
||||
status AccountStatus @default(ACTIVE)
|
||||
lastLoginAt DateTime? @map("last_login_at") @db.DateTime(3)
|
||||
cityId BigInt? @map("city_id") @db.UnsignedBigInt
|
||||
scopeType CityPartnerScopeType? @map("scope_type")
|
||||
districtCodes Json? @map("district_codes")
|
||||
orderCommissionRate Decimal? @default(0) @map("order_commission_rate") @db.Decimal(5, 4)
|
||||
redeemCommissionRate Decimal? @default(0.03) @map("redeem_commission_rate") @db.Decimal(5, 4)
|
||||
bindingStatus CityPartnerStatus? @default(ACTIVE) @map("binding_status")
|
||||
companyName String? @map("company_name") @db.VarChar(128)
|
||||
address String? @db.VarChar(256)
|
||||
contactPhone String? @map("contact_phone") @db.VarChar(20)
|
||||
contractNo String? @map("contract_no") @db.VarChar(64)
|
||||
contractSignedAt DateTime? @map("contract_signed_at") @db.DateTime(3)
|
||||
contractExpireAt DateTime? @map("contract_expire_at") @db.DateTime(3)
|
||||
bankAccountName String? @map("bank_account_name") @db.VarChar(64)
|
||||
bankAccountNo String? @map("bank_account_no") @db.VarChar(32)
|
||||
bankBranch String? @map("bank_branch") @db.VarChar(128)
|
||||
weeklyStoreTarget Int? @default(20) @map("weekly_store_target")
|
||||
managedWarehouseId BigInt? @unique @map("managed_warehouse_id") @db.UnsignedBigInt
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||||
|
||||
partner Partner @relation(fields: [partnerId], references: [id], onDelete: Restrict)
|
||||
parent PartnerAccount? @relation("PartnerAccountHierarchy", fields: [parentAccountId], references: [id], onDelete: SetNull)
|
||||
children PartnerAccount[] @relation("PartnerAccountHierarchy")
|
||||
city CommonCity? @relation("PartnerAccountCity", fields: [cityId], references: [id], onDelete: Restrict)
|
||||
managedWarehouse CityWarehouse? @relation("ManagedWarehouse", fields: [managedWarehouseId], references: [id], onDelete: SetNull)
|
||||
managedWarehouses CityWarehouse[] @relation("WarehouseManager")
|
||||
parent PartnerAccount? @relation("PartnerAccountHierarchy", fields: [parentAccountId], references: [id], onDelete: SetNull)
|
||||
children PartnerAccount[] @relation("PartnerAccountHierarchy")
|
||||
stores Store[]
|
||||
bills PartnerBill[]
|
||||
|
||||
@@index([partnerId])
|
||||
@@index([cityId, scopeType])
|
||||
@@index([cityId, isPrimary])
|
||||
@@index([parentAccountId])
|
||||
@@index([wxOpenId])
|
||||
@@index([contactPhone])
|
||||
@@map("partner_account")
|
||||
}
|
||||
|
||||
model PartnerBill {
|
||||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||||
billNo String @unique @map("bill_no") @db.VarChar(32)
|
||||
partnerId BigInt @map("partner_id") @db.UnsignedBigInt
|
||||
periodStart DateTime @map("period_start") @db.DateTime(3)
|
||||
periodEnd DateTime @map("period_end") @db.DateTime(3)
|
||||
orderCommission Decimal @default(0) @map("order_commission") @db.Decimal(10, 2)
|
||||
redeemCommission Decimal @default(0) @map("redeem_commission") @db.Decimal(10, 2)
|
||||
totalAmount Decimal @map("total_amount") @db.Decimal(10, 2)
|
||||
status PartnerBillStatus @default(DRAFT)
|
||||
confirmedAt DateTime? @map("confirmed_at") @db.DateTime(3)
|
||||
paidAt DateTime? @map("paid_at") @db.DateTime(3)
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||||
billNo String @unique @map("bill_no") @db.VarChar(32)
|
||||
partnerAccountId BigInt @map("partner_account_id") @db.UnsignedBigInt
|
||||
periodStart DateTime @map("period_start") @db.DateTime(3)
|
||||
periodEnd DateTime @map("period_end") @db.DateTime(3)
|
||||
orderCommission Decimal @default(0) @map("order_commission") @db.Decimal(10, 2)
|
||||
redeemCommission Decimal @default(0) @map("redeem_commission") @db.Decimal(10, 2)
|
||||
totalAmount Decimal @map("total_amount") @db.Decimal(10, 2)
|
||||
status PartnerBillStatus @default(DRAFT)
|
||||
confirmedAt DateTime? @map("confirmed_at") @db.DateTime(3)
|
||||
paidAt DateTime? @map("paid_at") @db.DateTime(3)
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||||
|
||||
partner Partner @relation(fields: [partnerId], references: [id], onDelete: Restrict)
|
||||
partnerAccount PartnerAccount @relation(fields: [partnerAccountId], references: [id], onDelete: Restrict)
|
||||
|
||||
@@index([partnerId, status])
|
||||
@@index([partnerAccountId, status])
|
||||
@@map("partner_bill")
|
||||
}
|
||||
|
||||
@@ -626,7 +652,7 @@ model UserPromoAttribution {
|
||||
model Store {
|
||||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||||
cityId BigInt @map("city_id") @db.UnsignedBigInt
|
||||
partnerId BigInt @map("partner_id") @db.UnsignedBigInt
|
||||
partnerAccountId BigInt @map("partner_account_id") @db.UnsignedBigInt
|
||||
categoryId BigInt? @map("category_id") @db.UnsignedBigInt
|
||||
name String @db.VarChar(128)
|
||||
phone String @db.VarChar(20)
|
||||
@@ -647,11 +673,12 @@ model Store {
|
||||
bankAccountName String? @map("bank_account_name") @db.VarChar(64)
|
||||
bankAccountNo String? @map("bank_account_no") @db.VarChar(32)
|
||||
bankBranch String? @map("bank_branch") @db.VarChar(128)
|
||||
settlementRate Decimal @default(0.60) @map("settlement_rate") @db.Decimal(5, 4)
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||||
|
||||
cityRef CommonCity @relation(fields: [cityId], references: [id], onDelete: Restrict)
|
||||
partner Partner @relation(fields: [partnerId], references: [id], onDelete: Restrict)
|
||||
partnerAccount PartnerAccount @relation(fields: [partnerAccountId], references: [id], onDelete: Restrict)
|
||||
category CommonStoreCategory? @relation(fields: [categoryId], references: [id], onDelete: SetNull)
|
||||
coverResource CommonResource? @relation("StoreCover", fields: [coverResourceId], references: [id], onDelete: SetNull)
|
||||
account StoreAccount?
|
||||
@@ -660,7 +687,7 @@ model Store {
|
||||
payouts StorePayout[]
|
||||
|
||||
@@index([cityId, status])
|
||||
@@index([partnerId])
|
||||
@@index([partnerAccountId])
|
||||
@@map("store_store")
|
||||
}
|
||||
|
||||
@@ -730,10 +757,12 @@ model Order {
|
||||
shippedAt DateTime? @map("shipped_at") @db.DateTime(3)
|
||||
completedAt DateTime? @map("completed_at") @db.DateTime(3)
|
||||
cancelledAt DateTime? @map("cancelled_at") @db.DateTime(3)
|
||||
payExpireAt DateTime? @map("pay_expire_at") @db.DateTime(3)
|
||||
remark String? @db.VarChar(512)
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||||
payExpireAt DateTime? @map("pay_expire_at") @db.DateTime(3)
|
||||
partnerAccountIdAtPay BigInt? @map("partner_account_id_at_pay") @db.UnsignedBigInt
|
||||
orderCommissionRateAtPay Decimal? @map("order_commission_rate_at_pay") @db.Decimal(5, 4)
|
||||
remark String? @db.VarChar(512)
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3)
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Restrict)
|
||||
city CommonCity @relation(fields: [cityId], references: [id], onDelete: Restrict)
|
||||
@@ -915,8 +944,7 @@ model LogStoreAnalytics {
|
||||
|
||||
model LogPartnerAnalytics {
|
||||
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
|
||||
partnerAccountId BigInt? @map("partner_account_id") @db.UnsignedBigInt
|
||||
partnerId BigInt @map("partner_id") @db.UnsignedBigInt
|
||||
partnerAccountId BigInt @map("partner_account_id") @db.UnsignedBigInt
|
||||
eventName String @map("event_name") @db.VarChar(64)
|
||||
clientApp ClientApp? @map("client_app")
|
||||
refType String? @map("ref_type") @db.VarChar(32)
|
||||
@@ -924,7 +952,6 @@ model LogPartnerAnalytics {
|
||||
extraJson Json? @map("extra_json")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||||
|
||||
@@index([partnerId, createdAt])
|
||||
@@index([partnerAccountId, createdAt])
|
||||
@@index([eventName, createdAt])
|
||||
@@map("log_partner_analytics")
|
||||
|
||||
@@ -1,421 +1,873 @@
|
||||
import { PrismaClient, ResourceBizType, ResourceMediaType, ResourceOwnerType } from '@prisma/client';
|
||||
|
||||
import { hashPassword } from '../src/common/crypto/password.util';
|
||||
import { DEFAULT_PRODUCT_DETAIL_TEMPLATES } from './seeds/product-detail-templates.default';
|
||||
|
||||
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
|
||||
|
||||
async function createMockResource(
|
||||
|
||||
ownerType: ResourceOwnerType,
|
||||
|
||||
ownerId: bigint,
|
||||
|
||||
bizType: ResourceBizType,
|
||||
|
||||
url: string,
|
||||
|
||||
) {
|
||||
|
||||
return prisma.commonResource.create({
|
||||
|
||||
data: {
|
||||
|
||||
ownerType,
|
||||
|
||||
ownerId,
|
||||
|
||||
bizType,
|
||||
|
||||
mediaType: ResourceMediaType.IMAGE,
|
||||
|
||||
ossBucket: 'mock-dukang',
|
||||
|
||||
ossKey: `mock/${ownerType.toLowerCase()}/${ownerId}/${bizType.toLowerCase()}`,
|
||||
|
||||
url,
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function main() {
|
||||
|
||||
console.log('Seeding v3.1 data...');
|
||||
|
||||
|
||||
|
||||
await prisma.logUserAnalytics.deleteMany();
|
||||
|
||||
await prisma.logPartnerAnalytics.deleteMany();
|
||||
|
||||
await prisma.logThirdParty.deleteMany();
|
||||
|
||||
await prisma.storePayout.deleteMany();
|
||||
|
||||
await prisma.storeRating.deleteMany();
|
||||
|
||||
await prisma.redeemRecord.deleteMany();
|
||||
|
||||
await prisma.benefitCoupon.deleteMany();
|
||||
|
||||
await prisma.orderDelivery.deleteMany();
|
||||
|
||||
await prisma.order.deleteMany();
|
||||
|
||||
await prisma.commonEvent.deleteMany();
|
||||
|
||||
await prisma.commonTicket.deleteMany();
|
||||
|
||||
await prisma.userPromoAttribution.deleteMany();
|
||||
|
||||
await prisma.userCityPreference.deleteMany();
|
||||
|
||||
await prisma.userAddress.deleteMany();
|
||||
|
||||
await prisma.user.deleteMany();
|
||||
|
||||
await prisma.storeAccount.deleteMany();
|
||||
|
||||
await prisma.store.deleteMany();
|
||||
|
||||
await prisma.partnerBill.deleteMany();
|
||||
|
||||
await prisma.cityWarehouse.deleteMany();
|
||||
|
||||
await prisma.partnerAccount.deleteMany();
|
||||
await prisma.commonCityCommissionRule.deleteMany();
|
||||
|
||||
await prisma.commonCity.deleteMany();
|
||||
await prisma.partner.deleteMany();
|
||||
|
||||
await prisma.commonProductItem.deleteMany();
|
||||
|
||||
await prisma.commonProductDetailTemplate.deleteMany();
|
||||
|
||||
await prisma.commonStoreCategory.deleteMany();
|
||||
|
||||
await prisma.commonPromoCode.deleteMany();
|
||||
|
||||
await prisma.commonResource.deleteMany();
|
||||
|
||||
await prisma.hqAccount.deleteMany();
|
||||
|
||||
const partner = await prisma.partner.create({
|
||||
data: {
|
||||
companyName: '郑州城市合伙人',
|
||||
address: '河南省郑州市金水区',
|
||||
contactPhone: '13700000001',
|
||||
bankAccountName: '郑州合伙人公司',
|
||||
bankAccountNo: '6222021234567890',
|
||||
bankBranch: '工商银行郑州分行',
|
||||
weeklyStoreTarget: 20,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const city = await prisma.commonCity.create({
|
||||
|
||||
data: {
|
||||
|
||||
code: '410100',
|
||||
|
||||
name: '郑州市',
|
||||
|
||||
province: '河南省',
|
||||
|
||||
status: 'ACTIVE',
|
||||
partnerId: partner.id,
|
||||
|
||||
localMinQty: 2,
|
||||
|
||||
crossMinQty: 6,
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
await prisma.commonCityCommissionRule.create({
|
||||
|
||||
|
||||
const primaryAccount = await prisma.partnerAccount.create({
|
||||
|
||||
data: {
|
||||
|
||||
phone: '13700000001',
|
||||
|
||||
name: '郑州合伙人主账号',
|
||||
|
||||
isPrimary: 1,
|
||||
|
||||
staffRole: 'PARTNER',
|
||||
|
||||
status: 'ACTIVE',
|
||||
|
||||
cityId: city.id,
|
||||
|
||||
scopeType: 'CITY_WIDE',
|
||||
|
||||
orderCommissionRate: 0,
|
||||
|
||||
redeemCommissionRate: 0.03,
|
||||
partnerProfitRate: 0.35,
|
||||
storeSettlementRate: 0.6,
|
||||
|
||||
bindingStatus: 'ACTIVE',
|
||||
|
||||
companyName: '郑州城市合伙人',
|
||||
|
||||
address: '河南省郑州市金水区',
|
||||
|
||||
contactPhone: '13700000001',
|
||||
|
||||
bankAccountName: '郑州合伙人公司',
|
||||
|
||||
bankAccountNo: '6222021234567890',
|
||||
|
||||
bankBranch: '工商银行郑州分行',
|
||||
|
||||
weeklyStoreTarget: 20,
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
const warehouse = await prisma.cityWarehouse.create({
|
||||
|
||||
data: {
|
||||
|
||||
cityId: city.id,
|
||||
|
||||
name: '郑州中央仓',
|
||||
|
||||
address: '河南省郑州市金水区物流园1号',
|
||||
|
||||
contactName: '仓管张',
|
||||
|
||||
contactPhone: '13700000009',
|
||||
|
||||
managerType: 'PARTNER',
|
||||
|
||||
partnerAccountId: primaryAccount.id,
|
||||
|
||||
status: 'ACTIVE',
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
await prisma.partnerAccount.update({
|
||||
|
||||
where: { id: primaryAccount.id },
|
||||
|
||||
data: { managedWarehouseId: warehouse.id },
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
await prisma.partnerAccount.create({
|
||||
|
||||
data: {
|
||||
|
||||
phone: '13700000002',
|
||||
|
||||
name: '拓店员小李',
|
||||
|
||||
isPrimary: 0,
|
||||
|
||||
parentAccountId: primaryAccount.id,
|
||||
|
||||
staffRole: 'INTERNAL',
|
||||
|
||||
permissions: ['store:create', 'store:view'],
|
||||
|
||||
status: 'DISABLED',
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
const categories = await Promise.all([
|
||||
|
||||
prisma.commonStoreCategory.create({ data: { code: 'HOTPOT', name: '火锅', sort: 1 } }),
|
||||
|
||||
prisma.commonStoreCategory.create({ data: { code: 'LOCAL', name: '地方菜', sort: 2 } }),
|
||||
|
||||
]);
|
||||
|
||||
|
||||
|
||||
for (const tpl of DEFAULT_PRODUCT_DETAIL_TEMPLATES) {
|
||||
|
||||
await prisma.commonProductDetailTemplate.create({
|
||||
|
||||
data: {
|
||||
|
||||
code: tpl.code,
|
||||
|
||||
name: tpl.name,
|
||||
|
||||
description: tpl.description,
|
||||
|
||||
aromaType: tpl.aromaType as 'QINGXIANG' | 'JIANGXIANG' | 'NONGXIANG' | null,
|
||||
|
||||
storyTitle: tpl.storyTitle,
|
||||
|
||||
storyText: tpl.storyText,
|
||||
|
||||
features: tpl.features,
|
||||
|
||||
detailImageUrls: [],
|
||||
|
||||
suggestedDetailImageCount: tpl.suggestedDetailImageCount,
|
||||
|
||||
sortOrder: tpl.sortOrder,
|
||||
|
||||
status: 'ACTIVE',
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const productDefs = [
|
||||
|
||||
{ skuCode: 'JZ-10', name: '酒祖杜康(国标特级10)', subtitle: '清香型 53度', price: 128, sortOrder: 1, img: 'https://picsum.photos/seed/jiuzu10/400/400' },
|
||||
|
||||
{ skuCode: 'JZ-15', name: '酒祖杜康(国标特级15)', subtitle: '清香型 53度', price: 168, sortOrder: 2, img: 'https://picsum.photos/seed/jiuzu15/400/400' },
|
||||
|
||||
{ skuCode: 'JZ-20', name: '酒祖杜康(国标特级20)', subtitle: '清香型 53度', price: 298, sortOrder: 3, img: 'https://picsum.photos/seed/jiuzu20/400/400' },
|
||||
|
||||
{ skuCode: 'JZ-30', name: '酒祖杜康(国标特级30)', subtitle: '清香型 53度', price: 498, sortOrder: 4, img: 'https://picsum.photos/seed/jiuzu30/400/400' },
|
||||
|
||||
];
|
||||
|
||||
|
||||
|
||||
const products = [];
|
||||
|
||||
for (const [i, def] of productDefs.entries()) {
|
||||
|
||||
const product = await prisma.commonProductItem.create({
|
||||
|
||||
data: {
|
||||
|
||||
skuCode: def.skuCode,
|
||||
|
||||
barcode69: `69000000000${i + 1}`,
|
||||
|
||||
name: def.name,
|
||||
|
||||
subtitle: def.subtitle,
|
||||
|
||||
aromaType: 'QINGXIANG',
|
||||
|
||||
spec: '500ml | 53度',
|
||||
|
||||
price: def.price,
|
||||
|
||||
benefitAmount: def.price,
|
||||
|
||||
status: 'ON_SALE',
|
||||
|
||||
sortOrder: def.sortOrder,
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
const cover = await createMockResource(ResourceOwnerType.PRODUCT, product.id, ResourceBizType.COVER, def.img);
|
||||
|
||||
await prisma.commonProductItem.update({
|
||||
|
||||
where: { id: product.id },
|
||||
|
||||
data: { coverResourceId: cover.id },
|
||||
|
||||
});
|
||||
|
||||
products.push(product);
|
||||
|
||||
}
|
||||
|
||||
await prisma.partnerAccount.create({
|
||||
data: {
|
||||
partnerId: partner.id,
|
||||
phone: '13700000001',
|
||||
name: '郑州合伙人主账号',
|
||||
isPrimary: 1,
|
||||
staffRole: 'PARTNER',
|
||||
},
|
||||
});
|
||||
|
||||
const primaryAccount = await prisma.partnerAccount.findUniqueOrThrow({
|
||||
where: { phone: '13700000001' },
|
||||
});
|
||||
|
||||
await prisma.partnerAccount.create({
|
||||
data: {
|
||||
partnerId: partner.id,
|
||||
phone: '13700000002',
|
||||
name: '拓店员小李',
|
||||
isPrimary: 0,
|
||||
parentAccountId: primaryAccount.id,
|
||||
staffRole: 'INTERNAL',
|
||||
status: 'DISABLED',
|
||||
},
|
||||
});
|
||||
|
||||
const storeDefs = [
|
||||
|
||||
{
|
||||
|
||||
name: '郑州老城店',
|
||||
|
||||
phone: '13910000001',
|
||||
|
||||
district: '金水区',
|
||||
|
||||
address: '花园路100号',
|
||||
|
||||
intro: '正宗河南菜,欢迎核销好客权益',
|
||||
|
||||
img: 'https://picsum.photos/seed/store1/400/300',
|
||||
|
||||
categoryId: categories[0].id,
|
||||
|
||||
settlementRate: 0.6,
|
||||
|
||||
withAccount: true,
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
name: '郑州美食城店',
|
||||
|
||||
phone: '13910000002',
|
||||
|
||||
district: '二七区',
|
||||
|
||||
address: '大学路200号',
|
||||
|
||||
intro: '地方特色餐饮',
|
||||
|
||||
img: 'https://picsum.photos/seed/store2/400/300',
|
||||
|
||||
categoryId: categories[1].id,
|
||||
|
||||
settlementRate: 0.65,
|
||||
|
||||
withAccount: false,
|
||||
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
|
||||
|
||||
const createdStores: { id: bigint; name: string }[] = [];
|
||||
|
||||
for (const def of storeDefs) {
|
||||
|
||||
const store = await prisma.store.create({
|
||||
|
||||
data: {
|
||||
|
||||
cityId: city.id,
|
||||
partnerId: partner.id,
|
||||
|
||||
partnerAccountId: primaryAccount.id,
|
||||
|
||||
categoryId: def.categoryId,
|
||||
|
||||
name: def.name,
|
||||
|
||||
phone: def.phone,
|
||||
|
||||
province: '河南省',
|
||||
|
||||
cityName: '郑州市',
|
||||
|
||||
district: def.district,
|
||||
|
||||
address: def.address,
|
||||
|
||||
intro: def.intro,
|
||||
|
||||
settlementRate: def.settlementRate,
|
||||
|
||||
status: 'OPEN',
|
||||
|
||||
openTime: '10:00',
|
||||
|
||||
closeTime: '22:00',
|
||||
|
||||
bankAccountName: def.name,
|
||||
|
||||
bankAccountNo: '6222029876543210',
|
||||
|
||||
bankBranch: '建设银行郑州分行',
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
const cover = await createMockResource(ResourceOwnerType.STORE, store.id, ResourceBizType.COVER, def.img);
|
||||
|
||||
await prisma.store.update({ where: { id: store.id }, data: { coverResourceId: cover.id } });
|
||||
|
||||
if (def.withAccount) {
|
||||
|
||||
await prisma.storeAccount.create({
|
||||
|
||||
data: { storeId: store.id, phone: def.phone, name: def.name },
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
createdStores.push({ id: store.id, name: def.name });
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const weekStart = (() => {
|
||||
|
||||
const d = new Date();
|
||||
|
||||
d.setHours(12, 0, 0, 0);
|
||||
|
||||
const day = d.getDay();
|
||||
|
||||
const diff = day === 0 ? 6 : day - 1;
|
||||
|
||||
d.setDate(d.getDate() - diff);
|
||||
|
||||
d.setHours(10, 0, 0, 0);
|
||||
|
||||
return d;
|
||||
|
||||
})();
|
||||
|
||||
|
||||
|
||||
const newWeekStore = await prisma.store.create({
|
||||
|
||||
data: {
|
||||
|
||||
cityId: city.id,
|
||||
partnerId: partner.id,
|
||||
|
||||
partnerAccountId: primaryAccount.id,
|
||||
|
||||
categoryId: categories[0].id,
|
||||
|
||||
name: '本周新签体验店',
|
||||
|
||||
phone: '13910000003',
|
||||
|
||||
province: '河南省',
|
||||
|
||||
cityName: '郑州市',
|
||||
|
||||
district: '中原区',
|
||||
|
||||
address: '建设路88号',
|
||||
|
||||
intro: '本周新签约门店',
|
||||
|
||||
settlementRate: 0.6,
|
||||
|
||||
status: 'OPEN',
|
||||
|
||||
openTime: '10:00',
|
||||
|
||||
closeTime: '22:00',
|
||||
|
||||
bankAccountName: '本周新签体验店',
|
||||
|
||||
bankAccountNo: '6222029876543211',
|
||||
|
||||
bankBranch: '农业银行郑州分行',
|
||||
|
||||
createdAt: new Date(weekStart.getTime() + 2 * 24 * 60 * 60 * 1000),
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
createdStores.push({ id: newWeekStore.id, name: newWeekStore.name });
|
||||
|
||||
|
||||
|
||||
await prisma.user.create({
|
||||
|
||||
data: {
|
||||
|
||||
userNo: 'DK88293401',
|
||||
|
||||
phone: '13800000001',
|
||||
|
||||
phoneVerifiedAt: new Date(),
|
||||
|
||||
nickname: '测试用户',
|
||||
|
||||
cityPreference: {
|
||||
|
||||
create: {
|
||||
|
||||
selectedCityCode: '410100',
|
||||
|
||||
selectedDistrict: '郑州市',
|
||||
|
||||
locateCityCode: '410100',
|
||||
|
||||
locateDistrict: '金水区',
|
||||
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
await prisma.hqAccount.create({
|
||||
|
||||
data: {
|
||||
|
||||
phone: '13600000001',
|
||||
|
||||
loginName: 'admin',
|
||||
|
||||
passwordHash: hashPassword('dukang@123!'),
|
||||
|
||||
name: '总部管理员',
|
||||
|
||||
adminRole: 'SUPER_ADMIN',
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
await prisma.commonPromoCode.create({
|
||||
|
||||
data: {
|
||||
|
||||
code: 'DKHQ001',
|
||||
|
||||
name: '总部品鉴会',
|
||||
|
||||
status: 'ACTIVE',
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
await prisma.commonPromoCode.create({
|
||||
|
||||
data: {
|
||||
|
||||
code: 'DKDEMO1',
|
||||
|
||||
name: '郑州品鉴会演示',
|
||||
|
||||
status: 'ACTIVE',
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
const user = await prisma.user.findUniqueOrThrow({ where: { phone: '13800000001' } });
|
||||
|
||||
const product = products[0];
|
||||
|
||||
|
||||
|
||||
const orderDefs = [
|
||||
|
||||
{ dayOffset: 0, payAmount: 1198, quantity: 2 },
|
||||
|
||||
{ dayOffset: 1, payAmount: 599, quantity: 1 },
|
||||
|
||||
{ dayOffset: 2, payAmount: 1760, quantity: 2 },
|
||||
|
||||
{ dayOffset: 4, payAmount: 1299, quantity: 1 },
|
||||
|
||||
{ dayOffset: 5, payAmount: 880, quantity: 1 },
|
||||
|
||||
];
|
||||
|
||||
|
||||
|
||||
const coupons: { id: bigint; balance: number }[] = [];
|
||||
|
||||
for (const [index, def] of orderDefs.entries()) {
|
||||
|
||||
const paidAt = new Date(weekStart.getTime() + def.dayOffset * 24 * 60 * 60 * 1000 + 14 * 60 * 60 * 1000);
|
||||
|
||||
const listAmount = def.payAmount;
|
||||
|
||||
const order = await prisma.order.create({
|
||||
|
||||
data: {
|
||||
|
||||
orderNo: `WK${Date.now()}${index}`,
|
||||
|
||||
userId: user.id,
|
||||
|
||||
cityId: city.id,
|
||||
|
||||
status: 'COMPLETED',
|
||||
|
||||
payStatus: 'PAID',
|
||||
|
||||
deliveryType: 'LOCAL',
|
||||
|
||||
productId: product.id,
|
||||
|
||||
barcode69: product.barcode69,
|
||||
|
||||
productName: product.name,
|
||||
|
||||
productSpec: product.spec,
|
||||
|
||||
quantity: def.quantity,
|
||||
|
||||
listUnitPrice: product.price,
|
||||
|
||||
listAmount,
|
||||
|
||||
productAmount: listAmount,
|
||||
|
||||
payAmount: listAmount,
|
||||
|
||||
benefitAmount: listAmount,
|
||||
|
||||
receiverName: '测试用户',
|
||||
receiverPhone: user.phone,
|
||||
|
||||
receiverPhone: user.phone!,
|
||||
|
||||
receiverAddress: '郑州市金水区测试路1号',
|
||||
|
||||
receiverProvince: '河南省',
|
||||
|
||||
receiverCity: '郑州市',
|
||||
|
||||
receiverDistrict: '金水区',
|
||||
|
||||
paidAt,
|
||||
|
||||
completedAt: paidAt,
|
||||
|
||||
partnerAccountIdAtPay: primaryAccount.id,
|
||||
|
||||
orderCommissionRateAtPay: 0,
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
const coupon = await prisma.benefitCoupon.create({
|
||||
|
||||
data: {
|
||||
|
||||
couponNo: `CPN${Date.now()}${index}`,
|
||||
|
||||
userId: user.id,
|
||||
|
||||
orderId: order.id,
|
||||
|
||||
totalAmount: listAmount,
|
||||
|
||||
balance: listAmount,
|
||||
|
||||
sourceProduct: product.name,
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
coupons.push({ id: coupon.id, balance: listAmount });
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const redeemDefs = [
|
||||
{ storeIndex: 0, amount: 42800, dayOffset: 1 },
|
||||
{ storeIndex: 1, amount: 38400, dayOffset: 2 },
|
||||
{ storeIndex: 2, amount: 31200, dayOffset: 3 },
|
||||
|
||||
{ storeIndex: 0, amount: 42800, dayOffset: 1, settlementRate: 0.6 },
|
||||
|
||||
{ storeIndex: 1, amount: 38400, dayOffset: 2, settlementRate: 0.65 },
|
||||
|
||||
{ storeIndex: 2, amount: 31200, dayOffset: 3, settlementRate: 0.6 },
|
||||
|
||||
];
|
||||
|
||||
for (const [index, def] of redeemDefs.entries()) {
|
||||
|
||||
const coupon = coupons[index];
|
||||
|
||||
const store = createdStores[def.storeIndex];
|
||||
|
||||
const createdAt = new Date(weekStart.getTime() + def.dayOffset * 24 * 60 * 60 * 1000 + 16 * 60 * 60 * 1000);
|
||||
|
||||
const settleAmount = Math.round(def.amount * def.settlementRate * 100) / 100;
|
||||
|
||||
await prisma.redeemRecord.create({
|
||||
|
||||
data: {
|
||||
|
||||
redeemNo: `RD${Date.now()}${index}`,
|
||||
|
||||
userId: user.id,
|
||||
|
||||
couponId: coupon.id,
|
||||
|
||||
storeId: store.id,
|
||||
|
||||
amount: def.amount,
|
||||
settleAmount: Math.round(def.amount * 0.6 * 100) / 100,
|
||||
|
||||
settleAmount,
|
||||
|
||||
createdAt,
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
await prisma.benefitCoupon.update({
|
||||
|
||||
where: { id: coupon.id },
|
||||
|
||||
data: {
|
||||
|
||||
usedAmount: def.amount,
|
||||
|
||||
balance: Math.max(0, coupon.balance - def.amount),
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const now = new Date();
|
||||
|
||||
const periodStart = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
|
||||
const periodEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0);
|
||||
|
||||
await prisma.partnerBill.create({
|
||||
|
||||
data: {
|
||||
|
||||
billNo: `PB${Date.now()}`,
|
||||
partnerId: partner.id,
|
||||
|
||||
partnerAccountId: primaryAccount.id,
|
||||
|
||||
periodStart,
|
||||
|
||||
periodEnd,
|
||||
|
||||
orderCommission: 1200,
|
||||
|
||||
redeemCommission: 800,
|
||||
|
||||
totalAmount: 2000,
|
||||
|
||||
status: 'CONFIRMED',
|
||||
|
||||
confirmedAt: now,
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
console.log('Seed complete:', {
|
||||
|
||||
city: city.name,
|
||||
|
||||
warehouse: warehouse.name,
|
||||
|
||||
primaryAccount: primaryAccount.phone,
|
||||
|
||||
detailTemplates: DEFAULT_PRODUCT_DETAIL_TEMPLATES.length,
|
||||
|
||||
products: products.length,
|
||||
stores: storeDefs.length,
|
||||
|
||||
stores: createdStores.length,
|
||||
|
||||
testPhones: {
|
||||
|
||||
user: '13800000001',
|
||||
|
||||
partner: '13700000001',
|
||||
|
||||
partnerStaff: '13700000002',
|
||||
|
||||
hq: '13600000001',
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
main()
|
||||
|
||||
.catch((e) => {
|
||||
|
||||
console.error(e);
|
||||
|
||||
process.exit(1);
|
||||
|
||||
})
|
||||
|
||||
.finally(async () => {
|
||||
|
||||
await prisma.$disconnect();
|
||||
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user