Files
platforms/backend/api/internal/seed/mock.go

811 lines
31 KiB
Go

// Package seed writes linked development data without replacing existing rows.
package seed
import (
"errors"
"fmt"
"reflect"
"time"
"git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common"
"git.apinb.com/heqiapp/platforms/backend/api/internal/models"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
const mockIdentityPrefix = "00000000-0000-7000-8000-"
// MockData idempotently writes one connected development scenario across all
// domain tables. Fixed identities and unique business numbers make reruns safe.
func MockData(database *gorm.DB) error {
passwordHash, err := bcrypt.GenerateFromPassword([]byte("Mock@123456"), bcrypt.DefaultCost)
if err != nil {
return fmt.Errorf("hash mock password: %w", err)
}
now := time.Date(2026, time.July, 1, 9, 0, 0, 0, time.Local)
yesterday := now.Add(-24 * time.Hour)
nextYear := now.AddDate(1, 0, 0)
return database.Transaction(func(tx *gorm.DB) error {
gas := models.GasBasic{
Entity: entity(1, common.StatusEnable), Code: "MOCK-GAS-001", Name: "和气示例气站",
CreditCode: "91310000MOCKGAS001", Principal: "张站长",
Address: "上海市浦东新区示例路 1 号", Longitude: "121.5440", Latitude: "31.2210",
}
if err := put(tx, &gas); err != nil {
return err
}
gasAccount := models.GasAccount{
Entity: entity(2, common.StatusEnable), GasBasicID: gas.ID, Username: "mock_gas_admin",
DisplayName: "示例气站管理员", PasswordHash: string(passwordHash), RoleCode: "admin",
}
if err := put(tx, &gasAccount); err != nil {
return err
}
delivery := models.DeliveryBasic{
Entity: entity(3, common.StatusEnable), DeliveryCode: "MOCK-DELIVERY-001",
GasBasicID: gas.ID, Name: "和气示例配送点", Principal: "李主管",
Address: "上海市浦东新区示例路 18 号",
}
if err := put(tx, &delivery); err != nil {
return err
}
deliveryAccount := models.DeliveryAccount{
Entity: entity(4, common.StatusEnable), DeliveryBasicID: delivery.ID,
Username: "mock_delivery_admin", DisplayName: "示例配送点管理员",
PasswordHash: string(passwordHash), RoleCode: "admin",
}
if err := put(tx, &deliveryAccount); err != nil {
return err
}
staff := models.StaffAccount{
Entity: entity(5, common.StatusEnable), Username: "mock_driver", PasswordHash: string(passwordHash),
Name: "王师傅", Phone: "13900000001", RoleCode: "delivery",
GasBasicID: gas.ID, DeliveryBasicID: delivery.ID, WorkStatus: "on_duty",
}
if err := put(tx, &staff); err != nil {
return err
}
credential := models.StaffCredential{
Entity: entity(6, common.StatusEnable), StaffAccountID: staff.ID,
CredentialType: "delivery", CredentialNo: "MOCK-CERT-001", ExpiredAt: &nextYear,
}
if err := put(tx, &credential); err != nil {
return err
}
user := models.UserAccount{
Entity: entity(7, common.StatusEnable), Username: "mock_customer", PasswordHash: string(passwordHash),
Name: "陈女士", Phone: "13800000001", RealName: "陈示例",
}
if err := put(tx, &user); err != nil {
return err
}
address := models.UserAddress{
Entity: entity(8, common.StatusEnable), UserAccountID: user.ID,
Address: "上海市浦东新区客户路 88 号", Longitude: "121.5500",
Latitude: "31.2250", IsDefault: true,
}
if err := put(tx, &address); err != nil {
return err
}
serviceRelation := models.UserServiceRelation{
Entity: entity(9, common.StatusEnable), UserAccountID: user.ID, GasBasicID: gas.ID,
DeliveryBasicID: delivery.ID, StaffAccountID: staff.ID,
}
if err := put(tx, &serviceRelation); err != nil {
return err
}
productType := models.ProductType{
Entity: entity(10, common.StatusEnable), Code: "MOCK-LPG-15KG", Name: "15kg 液化气钢瓶",
}
if err := put(tx, &productType); err != nil {
return err
}
warehouse := models.ProductWarehouse{
Entity: entity(11, common.StatusEnable), Code: "MOCK-WH-001", Name: "示例中心库房",
Address: gas.Address, Manager: "赵库管", Phone: "13700000001",
}
if err := put(tx, &warehouse); err != nil {
return err
}
enabledAt := yesterday
productInfo := models.ProductInfo{
Entity: entity(12, common.StatusEnable), Code: "MOCK-CYLINDER-001", Name: "示例液化气钢瓶",
ProductStatus: common.StatusInStock,
ProductTypeID: productType.ID, Params: `{"weight":"15kg","medium":"LPG"}`,
WarehouseID: warehouse.ID, ProducedAt: now.AddDate(-1, 0, 0), EnabledAt: &enabledAt,
}
if err := put(tx, &productInfo); err != nil {
return err
}
productOwner := models.ProductOwner{
Entity: entity(13, common.StatusEnable), ProductInfoID: productInfo.ID,
WarehouseID: warehouse.ID, Action: "stock_in",
OccurredAt: yesterday, Reason: "模拟数据初始化", OperatorName: "系统",
}
if err := put(tx, &productOwner); err != nil {
return err
}
completedAt := yesterday.Add(2 * time.Hour)
productRepair := models.ProductRepair{
Entity: entity(14, common.StatusEnable), ProductInfoID: productInfo.ID,
RepairNo: "MOCK-REPAIR-001", RepairType: "inspection", StartedAt: yesterday,
CompletedAt: &completedAt, Result: "passed", TargetProductStatus: common.StatusInStock,
Content: "外观、阀门与气密性检查", Operator: staff.Name,
}
if err := put(tx, &productRepair); err != nil {
return err
}
contract := models.GasorderContract{
Entity: entity(15, common.StatusEnable), ContractStatus: common.StatusActive, ContractNo: "MOCK-CONTRACT-001",
UserAccountID: user.ID, GasBasicID: gas.ID, DeliveryBasicID: delivery.ID,
Title: "居民瓶装气配送示例合同", Terms: "按需配送,安全使用。",
DefaultDeliveryFee: 500, SignedAt: yesterday, EffectiveAt: yesterday, ExpiredAt: &nextYear,
}
if err := put(tx, &contract); err != nil {
return err
}
contractRevision := models.GasorderContractRevision{
Entity: entity(16, common.StatusEnable), GasorderContractID: contract.ID, Action: "activate",
ContractStatus: common.StatusActive, EffectiveAt: contract.EffectiveAt, ExpiredAt: contract.ExpiredAt,
OperatorIdentity: gasAccount.Identity, OperatorName: gasAccount.DisplayName,
OccurredAt: yesterday, Reason: "模拟合同启用",
}
if err := put(tx, &contractRevision); err != nil {
return err
}
contractProduct := models.GasorderContractProduct{
Entity: entity(17, common.StatusEnable), GasorderContractID: contract.ID,
ProductInfoID: productInfo.ID, ProductCode: productInfo.Code,
ProductTypeName: productType.Name, ProductParams: productInfo.Params,
UnitPrice: 9800, BoundAt: yesterday,
}
if err := put(tx, &contractProduct); err != nil {
return err
}
gasOrder := models.GasorderBasic{
Entity: entity(18, common.StatusEnable), OrderStatus: common.StatusCompleted, OrderNo: "MOCK-GASORDER-001",
RequestNo: "MOCK-REQ-GASORDER-001", GasorderContractID: contract.ID,
UserAccountID: user.ID, CreatorType: "user", CreatorID: user.ID,
CreatorIdentity: user.Identity, GasBasicID: gas.ID, DeliveryBasicID: delivery.ID,
StaffAccountID: staff.ID, Address: address.Address, Longitude: address.Longitude,
Latitude: address.Latitude, ContactName: user.Name, ContactPhone: user.Phone,
ProductAmount: 9800, DeliveryFee: 500, PayableAmount: 10300,
OperatorIdentity: user.Identity, OperatorName: user.Name, Remark: "示例配送订单",
}
if err := put(tx, &gasOrder); err != nil {
return err
}
gasOrderItem := models.GasorderItem{
Entity: entity(19, common.StatusEnable), GasorderBasicID: gasOrder.ID,
GasorderContractProductID: contractProduct.ID, ProductInfoID: productInfo.ID,
ProductCode: productInfo.Code, ProductTypeName: productType.Name,
ProductParams: productInfo.Params, UnitPrice: contractProduct.UnitPrice,
}
if err := put(tx, &gasOrderItem); err != nil {
return err
}
assignment := models.GasorderAssign{
Entity: entity(20, common.StatusEnable), GasorderBasicID: gasOrder.ID, GasBasicID: gas.ID,
DeliveryBasicID: delivery.ID, StaffAccountID: staff.ID,
AssignerIdentity: deliveryAccount.Identity, AssignerName: deliveryAccount.DisplayName,
AssignedAt: now.Add(-2 * time.Hour), Reason: "系统示例派单",
}
if err := put(tx, &assignment); err != nil {
return err
}
orderStatus := models.GasorderStatus{
Entity: entity(21, common.StatusEnable), GasorderBasicID: gasOrder.ID,
FromStatus: common.StatusDelivering, ToStatus: common.StatusCompleted,
OperatorIdentity: staff.Identity, OperatorName: staff.Name,
OccurredAt: now, Reason: "用户已签收",
}
if err := put(tx, &orderStatus); err != nil {
return err
}
trackCompletedAt := now.Add(-10 * time.Minute)
track := models.GasorderTrack{
Entity: entity(22, common.StatusEnable), GasorderBasicID: gasOrder.ID,
StaffAccountID: staff.ID, AttemptNo: 1, StartedAt: now.Add(-90 * time.Minute),
CompletedAt: &trackCompletedAt,
}
if err := put(tx, &track); err != nil {
return err
}
trackPoint := models.GasorderTrackPoint{
Entity: entity(23, common.StatusEnable), GasorderTrackID: track.ID,
Longitude: address.Longitude, Latitude: address.Latitude,
OccurredAt: trackCompletedAt, Source: "gps", Accuracy: "10m",
}
if err := put(tx, &trackPoint); err != nil {
return err
}
confirmation := models.GasorderConfirm{
Entity: entity(24, common.StatusEnable), GasorderBasicID: gasOrder.ID,
ConfirmType: "signature", RecipientName: user.Name, RecipientPhone: user.Phone,
ProofURI: "/mock/proofs/gasorder-001.png", ConfirmedAt: now, Remark: "模拟签收",
}
if err := put(tx, &confirmation); err != nil {
return err
}
category := models.EcCategory{
Entity: entity(25, common.StatusEnable), Name: "瓶装燃气", SortNo: 10,
}
if err := put(tx, &category); err != nil {
return err
}
ecProduct := models.EcProduct{
Entity: entity(26, common.StatusEnable), EcCategoryID: category.ID,
ProductCode: "MOCK-EC-LPG-001", Name: "15kg 液化气配送服务",
PriceAmount: 10300, StockQuantity: 50,
}
if err := put(tx, &ecProduct); err != nil {
return err
}
attribute := models.EcProductAttribute{
Entity: entity(27, common.StatusEnable), EcProductID: ecProduct.ID,
Name: "规格", Value: "15kg/瓶", SortNo: 1,
}
if err := put(tx, &attribute); err != nil {
return err
}
image := models.EcProductImage{
Entity: entity(28, common.StatusEnable), EcProductID: ecProduct.ID,
ImageURI: "/mock/products/lpg-15kg.png", SortNo: 1, IsCover: true,
}
if err := put(tx, &image); err != nil {
return err
}
cart := models.EcCart{
Entity: entity(29, common.StatusEnable), UserAccountID: user.ID,
EcProductID: ecProduct.ID, Quantity: 1, Selected: true,
}
if err := put(tx, &cart); err != nil {
return err
}
ecOrder := models.EcOrder{
Entity: entity(30, common.StatusEnable), OrderStatus: common.StatusPaid, OrderNo: "MOCK-ECORDER-001",
UserAccountID: user.ID, GasStationID: gas.ID, DeliveryPointID: delivery.ID,
TotalAmount: ecProduct.PriceAmount,
}
if err := put(tx, &ecOrder); err != nil {
return err
}
ecOrderItem := models.EcOrderItem{
Entity: entity(31, common.StatusEnable), EcOrderID: ecOrder.ID, EcProductID: ecProduct.ID,
ProductSnapshot: `{"code":"MOCK-EC-LPG-001","name":"15kg 液化气配送服务"}`,
Quantity: 1, SaleAmount: ecProduct.PriceAmount,
}
if err := put(tx, &ecOrderItem); err != nil {
return err
}
review := models.EcReview{
Entity: entity(32, common.StatusEnable), EcOrderID: ecOrder.ID,
EcProductID: ecProduct.ID, UserAccountID: user.ID,
Score: 5, Content: "配送及时,服务规范。",
}
if err := put(tx, &review); err != nil {
return err
}
wallet := models.WalletBasic{
Entity: entity(33, common.StatusEnable), OwnerType: "user", OwnerID: user.ID,
OwnerIdentity: user.Identity, AlipayID: "mock@example.com", AlipayName: user.Name,
WxpayID: "mock_customer", WxpayName: user.Name,
PayPasswordHash: string(passwordHash), Balance: 50000, WithdrawalBalance: 30000,
}
if err := put(tx, &wallet); err != nil {
return err
}
bank := models.WalletBank{
Entity: entity(34, common.StatusEnable), WalletBasicID: wallet.ID,
CardNoCiphertext: "mock-ciphertext-card", CardFingerprint: "mock-card-fingerprint-001",
CardNoLast4: "8888", BankName: "示例银行", CardOwner: user.RealName,
IDCardCiphertext: "mock-ciphertext-id", PhoneCiphertext: "mock-ciphertext-phone",
BindID: "MOCK-BIND-001", BankType: "debit", Bank: "mock_bank",
}
if err := put(tx, &bank); err != nil {
return err
}
walletPayment := models.PaymentOrder{
Entity: entity(35, common.StatusEnable), PaymentStatus: 23,
PaymentNo: "MOCK-PAYMENT-001", RequestNo: "MOCK-REQ-PAYMENT-001", BusinessType: "gasorder", BusinessIdentity: gasOrder.Identity,
UserIdentity: user.Identity, MerchantIdentity: "platform", Channel: "wallet", PayType: "wallet", ChannelTradeNo: "MOCK-TRADE-001",
Amount: gasOrder.PayableAmount, Subject: "模拟供气订单", ClientArgs: `{}`, ExpiresAt: now.Add(30 * time.Minute), PaidAt: &now,
}
if err := put(tx, &walletPayment); err != nil {
return err
}
walletRecord := models.WalletRecord{
Entity: entity(36, common.StatusEnable), WalletBasicID: wallet.ID,
RecordNo: "MOCK-RECORD-001", RequestNo: "MOCK-REQ-RECORD-001",
Direction: "in", TradeType: "recharge", Amount: 50000,
BalanceAfter: 50000, WithdrawalBalanceAfter: 30000,
InTradeNo: walletPayment.PaymentNo, PayChannel: "manual",
OperatorIdentity: gasAccount.Identity, OperatorName: gasAccount.DisplayName,
Ymd: 20260701, Ym: 202607, Remark: "模拟钱包充值",
}
if err := put(tx, &walletRecord); err != nil {
return err
}
refundCompletedAt := now
refund := models.PaymentRefund{
Entity: entity(37, common.StatusEnable), RefundStatus: 20, WalletBasicID: wallet.ID,
PaymentOrderID: walletPayment.ID, RefundNo: "MOCK-REFUND-001", RequestNo: "MOCK-REQ-REFUND-001",
BusinessType: "gasorder", BusinessIdentity: gasOrder.Identity, UserIdentity: user.Identity,
Amount: 1000, Reason: "模拟部分退款", ReviewerIdentity: gasAccount.Identity, ReviewedAt: &refundCompletedAt, CompletedAt: &refundCompletedAt,
}
if err := put(tx, &refund); err != nil {
return err
}
applyCash := models.WalletApplyCash{
Entity: entity(38, common.StatusEnable), ApplyStatus: common.StatusApproved, WalletBasicID: wallet.ID, WalletBankID: bank.ID,
CashNo: "MOCK-CASH-001", RequestNo: "MOCK-REQ-CASH-001", Amount: 5000,
Channel: "bank", TradeNo: "MOCK-CASH-TRADE-001", Remark: "模拟提现",
ReviewerIdentity: gasAccount.Identity, ReviewerName: gasAccount.DisplayName,
ReviewedAt: &now, ReviewReason: "模拟审核通过", CompletedAt: &now, BalanceReserved: true,
}
if err := put(tx, &applyCash); err != nil {
return err
}
gasOrderPayment := models.GasorderPayment{
Entity: entity(39, common.StatusEnable), GasorderBasicID: gasOrder.ID,
PaymentOrderID: walletPayment.ID, AttemptNo: 1, Amount: gasOrder.PayableAmount,
}
if err := put(tx, &gasOrderPayment); err != nil {
return err
}
finPayment := models.FinPayment{
Entity: entity(40, common.StatusEnable), PaymentStatus: common.StatusPaid, EcOrderID: ecOrder.ID,
Channel: "wallet", Amount: ecOrder.TotalAmount, PaidAt: &now,
}
if err := put(tx, &finPayment); err != nil {
return err
}
settlement := models.FinSettlement{
Entity: entity(41, common.StatusEnable), SettlementNo: "MOCK-SETTLEMENT-001",
SubjectType: "gas", SubjectID: gas.ID,
PeriodStart: now.AddDate(0, 0, -30), PeriodEnd: now,
}
if err := put(tx, &settlement); err != nil {
return err
}
reconciliation := models.FinReconciliation{
Entity: entity(42, common.StatusEnable), ReconciliationStatus: common.StatusMatched, Channel: "wallet",
BillDate: now, DifferenceAmount: 0,
}
if err := put(tx, &reconciliation); err != nil {
return err
}
content := models.CmsContent{
Entity: entity(43, common.StatusEnable), ContentType: "notice",
Title: "模拟数据使用说明", Body: "本内容由 platform-cli mock-data 生成。",
VersionNo: 1, PublishStatus: "published",
}
if err := put(tx, &content); err != nil {
return err
}
ticket := models.CsTicket{
Entity: entity(44, common.StatusEnable), TicketStatus: common.StatusOpen, TicketNo: "MOCK-TICKET-001",
UserAccountID: user.ID, Category: "delivery", Priority: "normal",
}
if err := put(tx, &ticket); err != nil {
return err
}
attendance := models.StaffAttendance{
Entity: entity(48, common.StatusEnable), StaffAccountID: staff.ID,
RoleCode: staff.RoleCode, Action: "clock_in", OccurredAt: yesterday,
Longitude: gas.Longitude, Latitude: gas.Latitude,
DeviceIdentity: "mock-staff-device-001", RequestNo: "MOCK-ATTENDANCE-001",
}
if err := put(tx, &attendance); err != nil {
return err
}
producer := models.ProducerAccount{
Entity: entity(49, common.StatusEnable), ProducerCode: "MOCK-PRODUCER-001",
Name: "和气示例生产企业", CreditCode: "91310000MOCKPROD01",
Principal: "周厂长", Phone: "13600000001", Address: "上海市示例工业路 6 号",
Username: "mock_producer_admin", DisplayName: "示例生产管理员",
PasswordHash: string(passwordHash), RoleCode: "admin", Remark: "仅用于开发联调",
}
if err := put(tx, &producer); err != nil {
return err
}
recharge := models.WalletRechargeOrder{
Entity: entity(50, common.StatusEnable), RechargeStatus: common.StatusCompleted,
WalletBasicID: wallet.ID, RechargeNo: "MOCK-RECHARGE-001",
RequestNo: "MOCK-REQ-RECHARGE-001", Amount: 50000, Channel: "mock",
OwnerType: wallet.OwnerType, OwnerIdentity: wallet.OwnerIdentity, CompletedAt: &now,
}
if err := put(tx, &recharge); err != nil {
return err
}
refundItem := models.PaymentRefundItem{
Identity: entity(51, common.StatusEnable).Identity, PaymentRefundID: refund.ID,
OrderItemIdentity: gasOrderItem.Identity, Quantity: 1, Amount: refund.Amount,
}
if err := put(tx, &refundItem); err != nil {
return err
}
contentRead := models.CmsContentRead{
Entity: entity(52, common.StatusEnable), UserAccountID: user.ID, CmsContentID: content.ID,
VersionNo: content.VersionNo, ShownAt: yesterday, ConfirmedAt: &now,
ClientVersion: "mock-1.0.0", DeviceIdentity: "mock-user-device-001",
RequestNo: "MOCK-CONTENT-READ-001",
}
if err := put(tx, &contentRead); err != nil {
return err
}
evidence := models.CsTicketEvidence{
Entity: entity(53, common.StatusEnable), CsTicketID: ticket.ID,
EvidenceType: "created", MediaType: "image", FileURI: "/mock/tickets/ticket-001.png",
CapturedAt: yesterday, ReceivedAt: now, Longitude: address.Longitude, Latitude: address.Latitude,
Source: "mock", IntegrityStatus: "verified", OperatorIdentity: staff.Identity,
RequestNo: "MOCK-TICKET-EVIDENCE-001",
}
if err := put(tx, &evidence); err != nil {
return err
}
command := models.IotCommand{
Entity: entity(54, common.StatusEnable), DeviceIdentity: productInfo.Identity,
DeviceID: "0000000000000001", IdempotencyKey: "MOCK-IOT-COMMAND-001",
Action: "query_status", RequestPayload: `{"source":"mock-data"}`,
CommandStatus: "pending", ExpiresAt: now.Add(10 * time.Minute),
}
if err := put(tx, &command); err != nil {
return err
}
deviceMessage := models.IotDeviceMessage{
Identity: entity(55, common.StatusEnable).Identity, DeviceID: command.DeviceID,
Topic: "mock/device/0000000000000001/up", MessageType: "telemetry",
PayloadHex: "0000", DecodedFrame: `{"source":"mock-data"}`,
DeviceOccurredAt: &yesterday, ReceivedAt: now,
}
if err := put(tx, &deviceMessage); err != nil {
return err
}
outbox := models.IotOutbox{
Identity: entity(56, common.StatusEnable).Identity, CommandIdentity: command.Identity,
EventType: "iot.command.requested", Payload: `{"source":"mock-data"}`,
OutboxStatus: "pending", AvailableAt: now,
}
if err := put(tx, &outbox); err != nil {
return err
}
if err := ensureMockRoot(tx, string(passwordHash)); err != nil {
return err
}
var rootRole models.PlatformRole
if err := tx.Where("role_code = ?", "root").First(&rootRole).Error; err != nil {
return fmt.Errorf("find root role for mock menu: %w", err)
}
roleMenu := models.PlatformRoleMenu{PlatformRoleID: rootRole.ID, MenuIdentity: "dashboard_overview"}
if err := tx.Where("platform_role_id = ? AND menu_identity = ?", rootRole.ID, roleMenu.MenuIdentity).FirstOrCreate(&roleMenu).Error; err != nil {
return fmt.Errorf("seed root role menu: %w", err)
}
return seedAdditionalCoreScenarios(tx, string(passwordHash), now)
})
}
// seedAdditionalCoreScenarios 补齐十组可独立检索的核心主数据。
// 每组记录都使用本组父表的数据库 ID 建立关联,避免仅有展示字段而无真实关系。
func seedAdditionalCoreScenarios(database *gorm.DB, passwordHash string, now time.Time) error {
for scenario := 2; scenario <= 10; scenario++ {
sequence := 1000 + scenario*100
suffix := fmt.Sprintf("%03d", scenario)
phoneSuffix := fmt.Sprintf("%08d", scenario)
gas := models.GasBasic{
Entity: entity(sequence+1, common.StatusEnable), Code: "MOCK-GAS-" + suffix,
Name: fmt.Sprintf("和气示例气站 %d", scenario), CreditCode: "91310000MOCKGAS" + suffix,
Principal: fmt.Sprintf("示例站长%d", scenario), Address: fmt.Sprintf("上海市示例路 %d 号", scenario),
Longitude: fmt.Sprintf("121.5%03d", scenario), Latitude: fmt.Sprintf("31.2%03d", scenario),
}
if err := put(database, &gas); err != nil {
return err
}
delivery := models.DeliveryBasic{
Entity: entity(sequence+2, common.StatusEnable), DeliveryCode: "MOCK-DELIVERY-" + suffix,
GasBasicID: gas.ID, Name: fmt.Sprintf("和气示例配送点 %d", scenario),
Principal: fmt.Sprintf("示例主管%d", scenario), Address: fmt.Sprintf("上海市配送路 %d 号", scenario),
}
if err := put(database, &delivery); err != nil {
return err
}
staff := models.StaffAccount{
Entity: entity(sequence+3, common.StatusEnable), Username: "mock_driver_" + suffix,
PasswordHash: passwordHash, Name: fmt.Sprintf("示例配送员%d", scenario), Phone: "139" + phoneSuffix,
RoleCode: "delivery", GasBasicID: gas.ID, DeliveryBasicID: delivery.ID, WorkStatus: "on_duty",
}
if err := put(database, &staff); err != nil {
return err
}
user := models.UserAccount{
Entity: entity(sequence+4, common.StatusEnable), Username: "mock_customer_" + suffix,
PasswordHash: passwordHash, Name: fmt.Sprintf("示例客户%d", scenario), Phone: "138" + phoneSuffix,
RealName: fmt.Sprintf("模拟用户%d", scenario),
}
if err := put(database, &user); err != nil {
return err
}
address := models.UserAddress{
Entity: entity(sequence+5, common.StatusEnable), UserAccountID: user.ID,
Address: fmt.Sprintf("上海市客户路 %d 号", scenario), Longitude: gas.Longitude,
Latitude: gas.Latitude, IsDefault: true,
}
if err := put(database, &address); err != nil {
return err
}
relation := models.UserServiceRelation{
Entity: entity(sequence+6, common.StatusEnable), UserAccountID: user.ID,
GasBasicID: gas.ID, DeliveryBasicID: delivery.ID, StaffAccountID: staff.ID,
}
if err := put(database, &relation); err != nil {
return err
}
productType := models.ProductType{
Entity: entity(sequence+7, common.StatusEnable), Code: "MOCK-TYPE-" + suffix,
Name: fmt.Sprintf("示例智能瓶阀类型 %d", scenario),
}
if err := put(database, &productType); err != nil {
return err
}
warehouse := models.ProductWarehouse{
Entity: entity(sequence+8, common.StatusEnable), Code: "MOCK-WH-" + suffix,
Name: fmt.Sprintf("示例库房 %d", scenario), Address: gas.Address,
Manager: fmt.Sprintf("示例库管%d", scenario), Phone: "137" + phoneSuffix,
}
if err := put(database, &warehouse); err != nil {
return err
}
product := models.ProductInfo{
Entity: entity(sequence+9, common.StatusEnable), Code: "MOCK-CYLINDER-" + suffix,
Name: fmt.Sprintf("示例智能瓶阀 %d", scenario), ProductStatus: common.StatusInStock,
ProductTypeID: productType.ID, Params: `{"source":"mock-data"}`,
WarehouseID: warehouse.ID, ProducedAt: now.AddDate(-1, 0, scenario),
}
if err := put(database, &product); err != nil {
return err
}
contract := models.GasorderContract{
Entity: entity(sequence+10, common.StatusEnable), ContractStatus: common.StatusActive,
ContractNo: "MOCK-CONTRACT-" + suffix, UserAccountID: user.ID, GasBasicID: gas.ID,
DeliveryBasicID: delivery.ID, Title: fmt.Sprintf("示例供气合同 %d", scenario),
Terms: "仅用于开发联调。", DefaultDeliveryFee: 500,
SignedAt: now, EffectiveAt: now,
}
if err := put(database, &contract); err != nil {
return err
}
contractProduct := models.GasorderContractProduct{
Entity: entity(sequence+11, common.StatusEnable), GasorderContractID: contract.ID,
ProductInfoID: product.ID, ProductCode: product.Code, ProductTypeName: productType.Name,
ProductParams: product.Params, UnitPrice: int64(9000 + scenario*100), BoundAt: now,
}
if err := put(database, &contractProduct); err != nil {
return err
}
gasOrder := models.GasorderBasic{
Entity: entity(sequence+12, common.StatusEnable), OrderStatus: common.StatusPending,
OrderNo: "MOCK-GASORDER-" + suffix, RequestNo: "MOCK-REQ-GASORDER-" + suffix,
GasorderContractID: contract.ID, UserAccountID: user.ID, CreatorType: "user", CreatorID: user.ID,
CreatorIdentity: user.Identity, GasBasicID: gas.ID, DeliveryBasicID: delivery.ID,
StaffAccountID: staff.ID, Address: address.Address, Longitude: address.Longitude, Latitude: address.Latitude,
ContactName: user.Name, ContactPhone: user.Phone, ProductAmount: contractProduct.UnitPrice,
DeliveryFee: 500, PayableAmount: contractProduct.UnitPrice + 500,
OperatorIdentity: user.Identity, OperatorName: user.Name, Remark: "模拟待处理订单",
}
if err := put(database, &gasOrder); err != nil {
return err
}
gasOrderItem := models.GasorderItem{
Entity: entity(sequence+13, common.StatusEnable), GasorderBasicID: gasOrder.ID,
GasorderContractProductID: contractProduct.ID, ProductInfoID: product.ID,
ProductCode: product.Code, ProductTypeName: productType.Name,
ProductParams: product.Params, UnitPrice: contractProduct.UnitPrice,
}
if err := put(database, &gasOrderItem); err != nil {
return err
}
category := models.EcCategory{
Entity: entity(sequence+14, common.StatusEnable), Name: fmt.Sprintf("示例商品分类 %d", scenario), SortNo: scenario,
}
if err := put(database, &category); err != nil {
return err
}
ecProduct := models.EcProduct{
Entity: entity(sequence+15, common.StatusEnable), EcCategoryID: category.ID,
ProductCode: "MOCK-EC-PRODUCT-" + suffix, Name: fmt.Sprintf("示例配送商品 %d", scenario),
PriceAmount: gasOrder.PayableAmount, StockQuantity: 20 + scenario,
}
if err := put(database, &ecProduct); err != nil {
return err
}
ecOrder := models.EcOrder{
Entity: entity(sequence+16, common.StatusEnable), OrderStatus: common.StatusPending,
OrderNo: "MOCK-ECORDER-" + suffix, UserAccountID: user.ID,
GasStationID: gas.ID, DeliveryPointID: delivery.ID, TotalAmount: ecProduct.PriceAmount,
}
if err := put(database, &ecOrder); err != nil {
return err
}
wallet := models.WalletBasic{
Entity: entity(sequence+17, common.StatusEnable), OwnerType: "user", OwnerID: user.ID,
OwnerIdentity: user.Identity, AlipayID: "mock-" + suffix + "@example.invalid", AlipayName: user.Name,
WxpayID: "mock_customer_" + suffix, WxpayName: user.Name, PayPasswordHash: passwordHash,
Balance: int64(scenario * 10000), WithdrawalBalance: int64(scenario * 5000),
}
if err := put(database, &wallet); err != nil {
return err
}
content := models.CmsContent{
Entity: entity(sequence+18, common.StatusEnable), ContentType: "notice",
Title: fmt.Sprintf("模拟公告 %d", scenario), Body: "本内容由 mock-data 生成。",
VersionNo: 1, PublishStatus: "published",
}
if err := put(database, &content); err != nil {
return err
}
ticket := models.CsTicket{
Entity: entity(sequence+19, common.StatusEnable), TicketStatus: common.StatusOpen,
TicketNo: "MOCK-TICKET-" + suffix, UserAccountID: user.ID, Category: "delivery", Priority: "normal",
}
if err := put(database, &ticket); err != nil {
return err
}
producer := models.ProducerAccount{
Entity: entity(sequence+20, common.StatusEnable), ProducerCode: "MOCK-PRODUCER-" + suffix,
Name: fmt.Sprintf("示例生产企业 %d", scenario), CreditCode: "91310000MOCKPROD" + suffix,
Principal: fmt.Sprintf("示例厂长%d", scenario), Phone: "136" + phoneSuffix,
Address: fmt.Sprintf("上海市工业路 %d 号", scenario), Username: "mock_producer_" + suffix,
DisplayName: fmt.Sprintf("示例生产管理员%d", scenario), PasswordHash: passwordHash,
RoleCode: "admin", Remark: "仅用于开发联调",
}
if err := put(database, &producer); err != nil {
return err
}
}
return nil
}
// ensureMockRoot 复用已有 root 账号,仅在账号不存在时写入默认 root 用户。
func ensureMockRoot(database *gorm.DB, passwordHash string) error {
role := models.PlatformRole{
Entity: entity(45, common.StatusEnable), RoleCode: "root",
Name: "系统管理员", LocationScope: "precise", IsSystem: true,
}
if err := database.Where("role_code = ?", role.RoleCode).FirstOrCreate(&role).Error; err != nil {
return fmt.Errorf("seed root platform role: %w", err)
}
var account models.PlatformAccount
err := database.Where("username = ?", "root").First(&account).Error
if err == nil {
return nil
}
if !errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("find root platform account: %w", err)
}
account = models.PlatformAccount{
Entity: entity(46, common.StatusEnable), Username: "root",
DisplayName: "平台根管理员", PasswordHash: passwordHash,
PlatformRoleCode: role.RoleCode,
}
if err := database.Create(&account).Error; err != nil {
return fmt.Errorf("seed root platform account: %w", err)
}
return nil
}
func entity(sequence int, status int) models.Entity {
return models.Entity{
Identity: fmt.Sprintf("%s%012d", mockIdentityPrefix, sequence),
Status: status,
}
}
func put[T any](database *gorm.DB, value *T) error {
identity := identityOf(value)
if identity == "" {
return fmt.Errorf("seed %T: missing identity", value)
}
if err := database.Where("identity = ?", identity).FirstOrCreate(value).Error; err != nil {
return fmt.Errorf("seed %T: %w", value, err)
}
return nil
}
func identityOf(value any) string {
record := reflect.Indirect(reflect.ValueOf(value))
if !record.IsValid() {
return ""
}
directIdentity := record.FieldByName("Identity")
if directIdentity.IsValid() && directIdentity.Kind() == reflect.String {
return directIdentity.String()
}
entityField := record.FieldByName("Entity")
if !entityField.IsValid() {
return ""
}
identityField := entityField.FieldByName("Identity")
if !identityField.IsValid() || identityField.Kind() != reflect.String {
return ""
}
return identityField.String()
}