// 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() }