feat: add linked mock data seeding CLI
This commit is contained in:
@@ -4,8 +4,15 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.apinb.com/bsm-sdk/core/database"
|
||||
"git.apinb.com/heqiapp/platforms/backend/api/internal/config"
|
||||
"git.apinb.com/heqiapp/platforms/backend/api/internal/initdb"
|
||||
"git.apinb.com/heqiapp/platforms/backend/api/internal/seed"
|
||||
)
|
||||
|
||||
const serviceKey = "heqi"
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
printUsage()
|
||||
@@ -19,6 +26,12 @@ func main() {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
case "mock-data":
|
||||
if err := writeMockData(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("mock data written successfully")
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "unknown command: %s\n", os.Args[1])
|
||||
printUsage()
|
||||
@@ -27,5 +40,27 @@ func main() {
|
||||
}
|
||||
|
||||
func printUsage() {
|
||||
fmt.Fprintln(os.Stderr, "usage: platform-cli <version|resource-contract>")
|
||||
fmt.Fprintln(os.Stderr, "usage: platform-cli <version|resource-contract|mock-data>")
|
||||
}
|
||||
|
||||
func writeMockData() error {
|
||||
config.New(serviceKey)
|
||||
if config.Spec.Databases == nil {
|
||||
return fmt.Errorf("database configuration is required")
|
||||
}
|
||||
databaseService, err := database.NewDatabase(
|
||||
config.Spec.Databases.Driver,
|
||||
config.Spec.Databases.Source,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("connect database: %w", err)
|
||||
}
|
||||
if err := initdb.New(databaseService); err != nil {
|
||||
return fmt.Errorf("initialize platform data: %w", err)
|
||||
}
|
||||
if err := seed.MockData(databaseService); err != nil {
|
||||
return fmt.Errorf("write mock data: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
506
backend/api/internal/seed/mock.go
Normal file
506
backend/api/internal/seed/mock.go
Normal file
@@ -0,0 +1,506 @@
|
||||
// Package seed writes linked development data without replacing existing rows.
|
||||
package seed
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"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, "enabled"), 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, "enabled"), 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, "enabled"), 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, "enabled"), 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, "enabled"), 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, "enabled"), 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, "enabled"), 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, "enabled"), 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, "enabled"), 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, "enabled"), Code: "MOCK-LPG-15KG", Name: "15kg 液化气钢瓶",
|
||||
}
|
||||
if err := put(tx, &productType); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
warehouse := models.ProductWarehouse{
|
||||
Entity: entity(11, "enabled"), 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, "enabled"), 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, "enabled"), 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, "completed"), ProductInfoID: productInfo.ID,
|
||||
RepairNo: "MOCK-REPAIR-001", RepairType: "inspection", StartedAt: yesterday,
|
||||
CompletedAt: &completedAt, Result: "passed", TargetStatus: "enabled",
|
||||
Content: "外观、阀门与气密性检查", Operator: staff.Name,
|
||||
}
|
||||
if err := put(tx, &productRepair); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
contract := models.GasorderContract{
|
||||
Entity: entity(15, "active"), 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, "active"), GasorderContractID: contract.ID, Action: "activate",
|
||||
ContractStatus: "active", 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, "active"), 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, "completed"), 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, "completed"), 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, "completed"), 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, "completed"), GasorderBasicID: gasOrder.ID,
|
||||
FromStatus: "delivering", ToStatus: "completed",
|
||||
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, "completed"), 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, "completed"), 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, "completed"), 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, "enabled"), Name: "瓶装燃气", SortNo: 10,
|
||||
}
|
||||
if err := put(tx, &category); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ecProduct := models.EcProduct{
|
||||
Entity: entity(26, "enabled"), 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, "enabled"), EcProductID: ecProduct.ID,
|
||||
Name: "规格", Value: "15kg/瓶", SortNo: 1,
|
||||
}
|
||||
if err := put(tx, &attribute); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
image := models.EcProductImage{
|
||||
Entity: entity(28, "enabled"), 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, "enabled"), 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, "paid"), 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, "paid"), 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, "published"), 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, "enabled"), 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, "enabled"), 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, "success"), 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, "completed"), 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, "completed"), 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, "approved"), 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, "success"), 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, "paid"), 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, "completed"), 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, "matched"), Channel: "wallet",
|
||||
BillDate: now, DifferenceAmount: 0,
|
||||
}
|
||||
if err := put(tx, &reconciliation); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
content := models.CmsContent{
|
||||
Entity: entity(43, "enabled"), 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, "open"), 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, "enabled"), RoleCode: "mock_operator",
|
||||
Name: "模拟运营人员", DataScope: "global", IsSystem: false,
|
||||
}
|
||||
if err := put(tx, &role); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
platformAccount := models.PlatformAccount{
|
||||
Entity: entity(46, "enabled"), Username: "mock_operator",
|
||||
DisplayName: "模拟运营人员", PasswordHash: string(passwordHash),
|
||||
PlatformRoleCode: role.RoleCode, Phone: "13600000001",
|
||||
}
|
||||
if err := put(tx, &platformAccount); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var menu models.PlatformMenu
|
||||
if err := tx.Where("menu_code = ?", "dashboard").First(&menu).Error; err != nil {
|
||||
return fmt.Errorf("find dashboard menu: %w", err)
|
||||
}
|
||||
roleMenu := models.PlatformRoleMenu{
|
||||
PlatformRoleID: role.ID, PlatformMenuID: menu.ID,
|
||||
}
|
||||
if err := tx.Where(
|
||||
"platform_role_id = ? AND platform_menu_id = ?",
|
||||
role.ID, menu.ID,
|
||||
).FirstOrCreate(&roleMenu).Error; err != nil {
|
||||
return fmt.Errorf("seed platform_role_menu: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func entity(sequence int, status string) models.Entity {
|
||||
return models.Entity{
|
||||
Identity: fmt.Sprintf("%s%012d", mockIdentityPrefix, sequence),
|
||||
Status: status,
|
||||
Version: 1,
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
41
backend/api/internal/seed/mock_test.go
Normal file
41
backend/api/internal/seed/mock_test.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package seed
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMockEntityIdentitiesAreStableAndUnique(t *testing.T) {
|
||||
identityPattern := regexp.MustCompile(
|
||||
`^00000000-0000-7000-8000-[0-9]{12}$`,
|
||||
)
|
||||
seen := make(map[string]struct{}, 46)
|
||||
for sequence := 1; sequence <= 46; sequence++ {
|
||||
record := entity(sequence, "enabled")
|
||||
if !identityPattern.MatchString(record.Identity) {
|
||||
t.Fatalf("entity(%d) identity = %q", sequence, record.Identity)
|
||||
}
|
||||
if _, exists := seen[record.Identity]; exists {
|
||||
t.Fatalf("duplicate identity %q", record.Identity)
|
||||
}
|
||||
seen[record.Identity] = struct{}{}
|
||||
if record.Status != "enabled" || record.Version != 1 {
|
||||
t.Fatalf("entity(%d) has unexpected defaults: %#v", sequence, record)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdentityOfEmbeddedEntity(t *testing.T) {
|
||||
record := struct {
|
||||
Entity struct {
|
||||
Identity string
|
||||
}
|
||||
}{}
|
||||
record.Entity.Identity = "mock-identity"
|
||||
if got := identityOf(&record); got != record.Entity.Identity {
|
||||
t.Fatalf("identityOf() = %q, want %q", got, record.Entity.Identity)
|
||||
}
|
||||
if got := identityOf(nil); got != "" {
|
||||
t.Fatalf("identityOf(nil) = %q, want empty", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user