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

503 lines
18 KiB
Go

// Package seed writes linked development data without replacing existing rows.
package seed
import (
"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: "driver",
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", IsEnabled: true,
}
if err := put(tx, &warehouse); err != nil {
return err
}
enabledAt := yesterday
productInfo := models.ProductInfo{
Entity: entity(12, common.StatusEnable), Code: "MOCK-CYLINDER-001", Name: "示例液化气钢瓶",
ProductTypeID: productType.ID, Params: `{"weight":"15kg","medium":"LPG"}`,
WarehouseID: warehouse.ID, GasBasicID: gas.ID, ProducedAt: now.AddDate(-1, 0, 0),
IsEnabled: true, 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, GasBasicID: gas.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.StatusCompleted), ProductInfoID: productInfo.ID,
RepairNo: "MOCK-REPAIR-001", RepairType: "inspection", StartedAt: yesterday,
CompletedAt: &completedAt, Result: "passed", TargetStatus: common.StatusEnable,
Content: "外观、阀门与气密性检查", Operator: staff.Name,
}
if err := put(tx, &productRepair); err != nil {
return err
}
contract := models.GasorderContract{
Entity: entity(15, 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.StatusActive), 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.StatusActive), 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.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.StatusCompleted), 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.StatusCompleted), 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.StatusCompleted), 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.StatusCompleted), 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.StatusCompleted), 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.StatusCompleted), 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.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.StatusPaid), 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.StatusPublished), 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.WalletPayment{
Entity: entity(35, common.StatusSuccess), WalletBasicID: wallet.ID,
PaymentNo: "MOCK-PAYMENT-001", OrderNo: gasOrder.OrderNo,
TradeNo: "MOCK-TRADE-001", PaymentType: "gasorder",
PayChannel: "balance", PayType: "wallet", Amount: gasOrder.PayableAmount,
Args: `{}`, CallbackMsg: `{"status":"success"}`,
}
if err := put(tx, &walletPayment); err != nil {
return err
}
walletRecord := models.WalletRecord{
Entity: entity(36, common.StatusCompleted), 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.WalletRefund{
Entity: entity(37, common.StatusCompleted), WalletBasicID: wallet.ID,
WalletPaymentID: walletPayment.ID, RefundNo: "MOCK-REFUND-001",
OrderIdentity: gasOrder.Identity, Amount: 1000, Reason: "模拟部分退款",
OrderInfo: `{"order_no":"MOCK-GASORDER-001"}`, Result: `{"status":"success"}`,
TradeNo: "MOCK-REFUND-TRADE-001", CompletedAt: &refundCompletedAt,
}
if err := put(tx, &refund); err != nil {
return err
}
applyCash := models.WalletApplyCash{
Entity: entity(38, 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,
}
if err := put(tx, &applyCash); err != nil {
return err
}
gasOrderPayment := models.GasorderPayment{
Entity: entity(39, common.StatusSuccess), GasorderBasicID: gasOrder.ID,
WalletPaymentID: walletPayment.ID, AttemptNo: 1, Amount: gasOrder.PayableAmount,
}
if err := put(tx, &gasOrderPayment); err != nil {
return err
}
finPayment := models.FinPayment{
Entity: entity(40, 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.StatusCompleted), 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.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.StatusOpen), TicketNo: "MOCK-TICKET-001",
UserAccountID: user.ID, Category: "delivery", Priority: "normal",
}
if err := put(tx, &ticket); err != nil {
return err
}
role := models.PlatformRole{
Entity: entity(45, common.StatusEnable), RoleCode: "mock_operator",
Name: "模拟运营人员", DataScope: "global", IsSystem: false,
}
if err := put(tx, &role); err != nil {
return err
}
platformAccount := models.PlatformAccount{
Entity: entity(46, common.StatusEnable), Username: "mock_operator",
DisplayName: "模拟运营人员", PasswordHash: string(passwordHash),
PlatformRoleCode: role.RoleCode, Phone: "13600000001",
}
if err := put(tx, &platformAccount); err != nil {
return err
}
roleMenu := models.PlatformRoleMenu{
PlatformRoleID: role.ID, MenuIdentity: "dashboard_overview",
}
if err := tx.Where(
"platform_role_id = ? AND menu_identity = ?",
role.ID, roleMenu.MenuIdentity,
).FirstOrCreate(&roleMenu).Error; err != nil {
return fmt.Errorf("seed platform_role_menu: %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 ""
}
entityField := record.FieldByName("Entity")
if !entityField.IsValid() {
return ""
}
identityField := entityField.FieldByName("Identity")
if !identityField.IsValid() || identityField.Kind() != reflect.String {
return ""
}
return identityField.String()
}