1045 lines
40 KiB
Go
1045 lines
40 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
|
||
}
|
||
|
||
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
|
||
}
|
||
|
||
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 := linkMockProductProducer(&productInfo, producer); err != nil {
|
||
return err
|
||
}
|
||
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,
|
||
RequestNo: mockTrackPointRequestNo, OccurredAt: trackCompletedAt,
|
||
ReceivedAt: trackCompletedAt.Add(mockTrackPointReceiveDelay),
|
||
Source: "gps", Accuracy: "10m", Speed: mockTrackPointSpeed,
|
||
Direction: mockTrackPointDirection,
|
||
}
|
||
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: mockEcOrderPaidStatus(), OrderNo: "MOCK-ECORDER-001",
|
||
RequestNo: "MOCK-REQ-ECORDER-001", UserAccountID: user.ID,
|
||
GasStationID: gas.ID, DeliveryPointID: delivery.ID, UserAddressID: address.ID,
|
||
Address: address.Address, Longitude: address.Longitude, Latitude: address.Latitude,
|
||
ContactName: user.Name, ContactPhone: user.Phone, Remark: "模拟已支付商城订单",
|
||
ProductAmount: ecProduct.PriceAmount, PayableAmount: ecProduct.PriceAmount,
|
||
TotalAmount: ecProduct.PriceAmount, PaidAt: &now,
|
||
}
|
||
desiredEcOrder := ecOrder
|
||
if err := put(tx, &ecOrder); err != nil {
|
||
return err
|
||
}
|
||
if err := repairMockEcOrder(tx, desiredEcOrder, mockEcOrderPaidStatus(), ecProduct.PriceAmount, &now); 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
|
||
}
|
||
|
||
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 {
|
||
// 联调合同统一设置一年有效期,保证合同详情具有明确到期时间。
|
||
nextYear := now.AddDate(1, 0, 0)
|
||
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
|
||
}
|
||
|
||
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
|
||
}
|
||
|
||
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 := linkMockProductProducer(&product, producer); err != nil {
|
||
return err
|
||
}
|
||
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, ExpiredAt: &nextYear,
|
||
}
|
||
if err := put(database, &contract); err != nil {
|
||
return err
|
||
}
|
||
// 仅回填本函数生成的联调合同,修复旧种子执行后已存在的空到期时间。
|
||
if err := database.Model(&models.GasorderContract{}).
|
||
Where("identity = ? AND contract_no = ? AND expired_at IS NULL", contract.Identity, contract.ContractNo).
|
||
Update("expired_at", nextYear).Error; err != nil {
|
||
return fmt.Errorf("backfill mock contract expiry: %w", 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: mockGasorderInitialStatus(),
|
||
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
|
||
}
|
||
if err := repairMockGasorderInitialStatus(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: mockEcOrderPendingStatus(),
|
||
OrderNo: "MOCK-ECORDER-" + suffix, RequestNo: "MOCK-REQ-ECORDER-" + suffix,
|
||
UserAccountID: user.ID, GasStationID: gas.ID, DeliveryPointID: delivery.ID,
|
||
UserAddressID: address.ID, Address: address.Address,
|
||
Longitude: address.Longitude, Latitude: address.Latitude,
|
||
ContactName: user.Name, ContactPhone: user.Phone, Remark: "模拟待支付商城订单",
|
||
ProductAmount: ecProduct.PriceAmount, PayableAmount: ecProduct.PriceAmount,
|
||
TotalAmount: ecProduct.PriceAmount,
|
||
}
|
||
desiredEcOrder := ecOrder
|
||
if err := put(database, &ecOrder); err != nil {
|
||
return err
|
||
}
|
||
if err := repairMockEcOrder(database, desiredEcOrder, mockEcOrderPendingStatus(), ecProduct.PriceAmount, nil); 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
|
||
}
|
||
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// mockGasorderInitialStatus 统一 Mock 配送订单初始状态,必须与正式订单状态机保持一致。
|
||
func mockGasorderInitialStatus() int {
|
||
return common.StatusCreated
|
||
}
|
||
|
||
// Mock 商城订单使用商城交易状态,不复用其他领域含义相近的通用状态。
|
||
func mockEcOrderPendingStatus() int { return 16 }
|
||
func mockEcOrderPaidStatus() int { return 18 }
|
||
|
||
// repairMockEcOrder 幂等修复固定 Mock 商城订单的状态、金额与支付时间。
|
||
func repairMockEcOrder(database *gorm.DB, order models.EcOrder, status int, amount int64, paidAt *time.Time) error {
|
||
result := database.Model(&models.EcOrder{}).
|
||
Where("identity = ? AND order_no LIKE ?", order.Identity, "MOCK-ECORDER-%").
|
||
Updates(map[string]any{
|
||
"order_status": status, "product_amount": amount, "payable_amount": amount,
|
||
"total_amount": amount, "paid_at": paidAt, "request_no": order.RequestNo,
|
||
"user_address_id": order.UserAddressID, "address": order.Address,
|
||
"longitude": order.Longitude, "latitude": order.Latitude,
|
||
"contact_name": order.ContactName, "contact_phone": order.ContactPhone,
|
||
"remark": order.Remark,
|
||
})
|
||
if result.Error != nil {
|
||
return fmt.Errorf("repair mock ec order: %w", result.Error)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
type mockEcOrderRepairSpec struct {
|
||
identity string
|
||
orderNo string
|
||
requestNo string
|
||
userIdentity string
|
||
addressIdentity string
|
||
contactName string
|
||
contactPhone string
|
||
address string
|
||
longitude string
|
||
latitude string
|
||
status int
|
||
amount int64
|
||
paidAt *time.Time
|
||
remark string
|
||
}
|
||
|
||
// mockEcOrderRepairSpecs 返回固定 Mock 商城订单的文档化交易状态与金额。
|
||
func mockEcOrderRepairSpecs() []mockEcOrderRepairSpec {
|
||
paidAt := time.Date(2026, time.July, 1, 9, 0, 0, 0, time.Local)
|
||
specs := []mockEcOrderRepairSpec{{
|
||
identity: entity(30, common.StatusEnable).Identity,
|
||
orderNo: "MOCK-ECORDER-001",
|
||
requestNo: "MOCK-REQ-ECORDER-001",
|
||
userIdentity: entity(7, common.StatusEnable).Identity,
|
||
addressIdentity: entity(8, common.StatusEnable).Identity,
|
||
contactName: "陈女士",
|
||
contactPhone: "13800000001",
|
||
address: "上海市浦东新区客户路 88 号",
|
||
longitude: "121.5500",
|
||
latitude: "31.2250",
|
||
status: mockEcOrderPaidStatus(),
|
||
amount: 10300,
|
||
paidAt: &paidAt,
|
||
remark: "模拟已支付商城订单",
|
||
}}
|
||
for scenario := 2; scenario <= 10; scenario++ {
|
||
sequence := 1000 + scenario*100
|
||
specs = append(specs, mockEcOrderRepairSpec{
|
||
identity: entity(sequence+16, common.StatusEnable).Identity,
|
||
orderNo: fmt.Sprintf("MOCK-ECORDER-%03d", scenario),
|
||
requestNo: fmt.Sprintf("MOCK-REQ-ECORDER-%03d", scenario),
|
||
userIdentity: entity(sequence+4, common.StatusEnable).Identity,
|
||
addressIdentity: entity(sequence+5, common.StatusEnable).Identity,
|
||
contactName: fmt.Sprintf("示例客户%d", scenario),
|
||
contactPhone: "138" + fmt.Sprintf("%08d", scenario),
|
||
address: fmt.Sprintf("上海市客户路 %d 号", scenario),
|
||
longitude: fmt.Sprintf("121.5%03d", scenario),
|
||
latitude: fmt.Sprintf("31.2%03d", scenario),
|
||
status: mockEcOrderPendingStatus(),
|
||
amount: int64(9500 + scenario*100),
|
||
remark: "模拟待支付商城订单",
|
||
})
|
||
}
|
||
return specs
|
||
}
|
||
|
||
// RepairMockEcOrders 仅修复固定 Mock 商城订单,不触发其他模拟资源写入。
|
||
func RepairMockEcOrders(database *gorm.DB) (int64, error) {
|
||
var repaired int64
|
||
err := database.Transaction(func(tx *gorm.DB) error {
|
||
for _, spec := range mockEcOrderRepairSpecs() {
|
||
var user models.UserAccount
|
||
if err := tx.Where("identity = ?", spec.userIdentity).First(&user).Error; err != nil {
|
||
return fmt.Errorf("load mock ec order user %s: %w", spec.orderNo, err)
|
||
}
|
||
if err := tx.Model(&user).Updates(map[string]any{
|
||
"name": spec.contactName, "phone": spec.contactPhone,
|
||
}).Error; err != nil {
|
||
return fmt.Errorf("repair mock ec order user %s: %w", spec.orderNo, err)
|
||
}
|
||
var address models.UserAddress
|
||
if err := tx.Where("identity = ?", spec.addressIdentity).First(&address).Error; err != nil {
|
||
return fmt.Errorf("load mock ec order address %s: %w", spec.orderNo, err)
|
||
}
|
||
if err := tx.Model(&address).Updates(map[string]any{
|
||
"address": spec.address, "longitude": spec.longitude, "latitude": spec.latitude,
|
||
}).Error; err != nil {
|
||
return fmt.Errorf("repair mock ec order address %s: %w", spec.orderNo, err)
|
||
}
|
||
result := tx.Model(&models.EcOrder{}).
|
||
Where("identity = ? AND order_no = ?", spec.identity, spec.orderNo).
|
||
Updates(map[string]any{
|
||
"order_status": spec.status, "product_amount": spec.amount,
|
||
"payable_amount": spec.amount, "total_amount": spec.amount,
|
||
"paid_at": spec.paidAt, "request_no": spec.requestNo,
|
||
"user_address_id": address.ID, "address": spec.address,
|
||
"longitude": spec.longitude, "latitude": spec.latitude,
|
||
"contact_name": spec.contactName, "contact_phone": spec.contactPhone,
|
||
"remark": spec.remark,
|
||
})
|
||
if result.Error != nil {
|
||
return fmt.Errorf("repair mock ec order %s: %w", spec.orderNo, result.Error)
|
||
}
|
||
repaired += result.RowsAffected
|
||
}
|
||
return nil
|
||
})
|
||
return repaired, err
|
||
}
|
||
|
||
// repairMockGasorderInitialStatus 幂等修复历史种子数据中误写为“待处理”的 Mock 配送订单。
|
||
func repairMockGasorderInitialStatus(database *gorm.DB, order models.GasorderBasic) error {
|
||
result := database.Model(&models.GasorderBasic{}).
|
||
Where("identity = ? AND order_no LIKE ? AND order_status = ?", order.Identity, "MOCK-GASORDER-%", common.StatusPending).
|
||
Update("order_status", mockGasorderInitialStatus())
|
||
if result.Error != nil {
|
||
return fmt.Errorf("repair mock gasorder initial status: %w", result.Error)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// RepairMockGasorderInitialStatuses 批量修复固定 Mock 标识范围内误写为“待处理”的配送订单,返回修复数量。
|
||
func RepairMockGasorderInitialStatuses(database *gorm.DB) (int64, error) {
|
||
result := database.Model(&models.GasorderBasic{}).
|
||
Where("identity LIKE ? AND order_no LIKE ? AND order_status = ?", mockIdentityPrefix+"%", "MOCK-GASORDER-%", common.StatusPending).
|
||
Update("order_status", mockGasorderInitialStatus())
|
||
if result.Error != nil {
|
||
return 0, fmt.Errorf("repair mock gasorder initial statuses: %w", result.Error)
|
||
}
|
||
return result.RowsAffected, nil
|
||
}
|
||
|
||
const (
|
||
mockTrackPointRequestNo = "MOCK-TRACK-POINT-001"
|
||
mockTrackPointSpeed = "12 km/h"
|
||
mockTrackPointDirection = "东北(45°)"
|
||
mockTrackPointReceiveDelay = 2 * time.Second
|
||
)
|
||
|
||
// RepairMockTrackPointSample 仅补全固定演示轨迹点的缺失上报字段,不覆盖已有有效数据。
|
||
func RepairMockTrackPointSample(database *gorm.DB) (int64, error) {
|
||
var point models.GasorderTrackPoint
|
||
identity := fmt.Sprintf("%s%012d", mockIdentityPrefix, 23)
|
||
if err := database.Where("identity = ?", identity).First(&point).Error; err != nil {
|
||
return 0, fmt.Errorf("find mock track point sample: %w", err)
|
||
}
|
||
updates := map[string]any{}
|
||
if point.RequestNo == "" {
|
||
updates["request_no"] = mockTrackPointRequestNo
|
||
}
|
||
if point.ReceivedAt.IsZero() {
|
||
updates["received_at"] = point.OccurredAt.Add(mockTrackPointReceiveDelay)
|
||
}
|
||
if point.Speed == "" {
|
||
updates["speed"] = mockTrackPointSpeed
|
||
}
|
||
if point.Direction == "" {
|
||
updates["direction"] = mockTrackPointDirection
|
||
}
|
||
if len(updates) == 0 {
|
||
return 0, nil
|
||
}
|
||
result := database.Model(&point).Updates(updates)
|
||
if result.Error != nil {
|
||
return 0, fmt.Errorf("repair mock track point sample: %w", result.Error)
|
||
}
|
||
return result.RowsAffected, nil
|
||
}
|
||
|
||
// linkMockProductProducer 为未来生成的 Mock 智能气阀建立真实生产商关联。
|
||
func linkMockProductProducer(product *models.ProductInfo, producer models.ProducerAccount) error {
|
||
if product == nil || producer.ID == 0 {
|
||
return errors.New("mock product producer is missing")
|
||
}
|
||
product.ProducerAccountID = producer.ID
|
||
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()
|
||
}
|