package main import ( "log" "time" "golang.org/x/crypto/bcrypt" "kefu-cloud/server/internal/config" "kefu-cloud/server/internal/model" ) func main() { cfg := config.Load() model.InitDB(cfg.Database.DSN()) seed() log.Println("种子数据填充完成") } func seed() { // 默认后台管理员:kefu_admin / kefu_admin123(其它种子账号同密码) hash, _ := bcrypt.GenerateFromPassword([]byte("kefu_admin123"), bcrypt.DefaultCost) pwd := string(hash) // Plans plans := []model.Plan{ {Name: "基础版", PriceMonthly: 299, Seats: 2, StorageDays: 30, KBLimit: 50, Status: "active", Features: `{"stats":"basic","api":false,"channels":["web"],"brand":false}`}, {Name: "专业版", PriceMonthly: 699, Seats: 10, StorageDays: 180, KBLimit: 500, Status: "active", Features: `{"stats":"advanced","api":true,"channels":["web","wechat","app"],"brand":true}`}, {Name: "企业版", PriceMonthly: 1999, Seats: 50, StorageDays: 0, KBLimit: 0, Status: "active", Features: `{"stats":"custom","api":true,"channels":["all"],"brand":true,"dedicated":true}`}, } model.DB.Create(&plans) // Tenants now := time.Now() tenants := []model.Tenant{ {Name: "赵六科技", PlanID: &plans[2].ID, SeatCount: 30, ExpireAt: now.AddDate(1, 0, 0), Status: "normal", ContactName: "赵总", ContactPhone: "13800001111", ContactEmail: "zhao@tech.com"}, {Name: "李四商贸", PlanID: &plans[1].ID, SeatCount: 10, ExpireAt: now.AddDate(0, 6, 0), Status: "normal", ContactName: "李经理", ContactPhone: "13900002222", ContactEmail: "li@trade.com"}, {Name: "王五集团", PlanID: &plans[2].ID, SeatCount: 50, ExpireAt: now.AddDate(0, 0, 7), Status: "expiring", ContactName: "王总", ContactPhone: "13700003333", ContactEmail: "wang@group.com"}, {Name: "孙八信息", PlanID: &plans[0].ID, SeatCount: 2, ExpireAt: now.AddDate(1, 0, 0), Status: "suspended", ContactName: "孙经理", ContactPhone: "13600004444", ContactEmail: "sun@info.com"}, {Name: "周九科技", PlanID: &plans[1].ID, SeatCount: 8, ExpireAt: now.AddDate(0, -1, 0), Status: "expired", ContactName: "周总", ContactPhone: "13500005555", ContactEmail: "zhou@tech.com"}, } model.DB.Create(&tenants) // Platform admin model.DB.Create(&model.User{ TenantID: 0, Role: "platform_admin", Username: "platform_admin", PasswordHash: pwd, Nickname: "平台管理员", Status: "online", }) // Tenant 1 users model.DB.Create(&model.User{ TenantID: tenants[0].ID, Role: "admin", Username: "kefu_admin", PasswordHash: pwd, Nickname: "赵总(管理员)", Status: "online", }) model.DB.Create(&model.User{ TenantID: tenants[0].ID, Role: "supervisor", Username: "supervisor", PasswordHash: pwd, Nickname: "客服主管", Status: "online", }) agent1 := model.User{TenantID: tenants[0].ID, Role: "agent", Username: "agent1", PasswordHash: pwd, Nickname: "客服小王", Status: "online"} agent2 := model.User{TenantID: tenants[0].ID, Role: "agent", Username: "agent2", PasswordHash: pwd, Nickname: "客服小李", Status: "online"} agent3 := model.User{TenantID: tenants[0].ID, Role: "agent", Username: "agent3", PasswordHash: pwd, Nickname: "客服小张", Status: "online"} model.DB.Create(&[]model.User{agent1, agent2, agent3}) // Tenant 2 users model.DB.Create(&model.User{ TenantID: tenants[1].ID, Role: "admin", Username: "litrade_admin", PasswordHash: pwd, Nickname: "李经理", Status: "online", }) model.DB.Create(&model.User{ TenantID: tenants[1].ID, Role: "agent", Username: "litrade_agent1", PasswordHash: pwd, Nickname: "客服小李2", Status: "online", }) // Channels for Tenant 1 channels := []model.Channel{ {TenantID: tenants[0].ID, Type: "web", Name: "网页聊天", Status: "enabled", ScriptCode: ``}, {TenantID: tenants[0].ID, Type: "wechat", Name: "微信公众号", Status: "enabled"}, {TenantID: tenants[0].ID, Type: "app", Name: "APP内嵌", Status: "disabled"}, } model.DB.Create(&channels) // Customers customers := []model.Customer{ {TenantID: tenants[0].ID, Name: "张三", Phone: "138****8888", Email: "zhang@example.com", Tags: `["VIP客户","新客户"]`, Source: "网页", Status: "online", ConversationCount: 12}, {TenantID: tenants[0].ID, Name: "李四", Phone: "139****7777", Email: "li@example.com", Tags: `["活跃"]`, Source: "微信", Status: "offline", ConversationCount: 8}, {TenantID: tenants[0].ID, Name: "王五", Phone: "137****6666", Email: "wang@example.com", Tags: `["企业客户","VIP客户"]`, Source: "APP", Status: "busy", ConversationCount: 25}, {TenantID: tenants[0].ID, Name: "赵六科技", Phone: "136****5555", Email: "zhao@tech.com", Tags: `["企业客户"]`, Source: "网页", Status: "online", ConversationCount: 6}, {TenantID: tenants[0].ID, Name: "钱七", Phone: "135****4444", Tags: `["沉默"]`, Source: "邮件", Status: "offline", ConversationCount: 3}, {TenantID: tenants[0].ID, Name: "孙八", Phone: "134****3333", Email: "sun@example.com", Tags: `["新客户","活跃"]`, Source: "网页", Status: "online", ConversationCount: 2}, } model.DB.Create(&customers) // Sessions(含访客 IP/地区,与工作台顶栏展示一致) sessions := []model.Session{ {TenantID: tenants[0].ID, ChannelID: channels[0].ID, CustomerID: customers[0].ID, AgentID: &agent1.ID, Status: "active", Priority: "urgent", VisitorIP: "192.168.1.105", VisitorRegion: "北京", UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0"}, {TenantID: tenants[0].ID, ChannelID: channels[1].ID, CustomerID: customers[1].ID, AgentID: &agent2.ID, Status: "active", Priority: "normal", VisitorIP: "10.0.0.23", VisitorRegion: "内网", UserAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0) Safari/604.1"}, {TenantID: tenants[0].ID, ChannelID: channels[0].ID, CustomerID: customers[2].ID, AgentID: nil, Status: "waiting", Priority: "normal", VisitorIP: "223.5.5.5", VisitorRegion: "中国", UserAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X) Chrome/120.0.0.0"}, {TenantID: tenants[0].ID, ChannelID: channels[0].ID, CustomerID: customers[3].ID, AgentID: &agent3.ID, Status: "ended", Priority: "normal", SatisfactionScore: ptr(5), VisitorIP: "114.114.114.114", VisitorRegion: "中国"}, {TenantID: tenants[0].ID, ChannelID: channels[0].ID, CustomerID: customers[4].ID, AgentID: &agent1.ID, Status: "ended", Priority: "normal", SatisfactionScore: ptr(4), VisitorIP: "8.8.8.8", VisitorRegion: "美国"}, } model.DB.Create(&sessions) // Messages - 模拟真实对话 messages := []model.Message{ // Session 1: 张三 - 订单物流咨询 (urgent) {SessionID: sessions[0].ID, SenderType: "visitor", Content: "你好,我的订单怎么还没发货?已经3天了", Type: "text", Seq: 1, SentAt: now.Add(-10 * time.Minute)}, {SessionID: sessions[0].ID, SenderType: "agent", SenderID: &agent1.ID, Content: "您好,请提供一下您的订单号,我帮您查询", Type: "text", Seq: 2, SentAt: now.Add(-9 * time.Minute)}, {SessionID: sessions[0].ID, SenderType: "visitor", Content: "订单号 AB20260714001", Type: "text", Seq: 3, SentAt: now.Add(-8 * time.Minute)}, {SessionID: sessions[0].ID, SenderType: "agent", SenderID: &agent1.ID, Content: "稍等,正在为您查询物流信息...", Type: "text", Seq: 4, SentAt: now.Add(-7 * time.Minute)}, {SessionID: sessions[0].ID, SenderType: "agent", SenderID: &agent1.ID, Content: "您好,您的包裹已到达北京分拨中心,预计明天下午送达。给您带来不便敬请谅解", Type: "text", Seq: 5, SentAt: now.Add(-6 * time.Minute)}, // Session 2: 李四 - 退换货咨询 (active) {SessionID: sessions[1].ID, SenderType: "visitor", Content: "你好,我买的商品有质量问题,想退货", Type: "text", Seq: 1, SentAt: now.Add(-5 * time.Minute)}, {SessionID: sessions[1].ID, SenderType: "agent", SenderID: &agent2.ID, Content: "非常抱歉给您带来不便。请拍照上传问题商品图片,我们核实后会为您办理退货", Type: "text", Seq: 2, SentAt: now.Add(-4 * time.Minute)}, {SessionID: sessions[1].ID, SenderType: "visitor", Content: "好的,我拍照上传", Type: "text", Seq: 3, SentAt: now.Add(-3 * time.Minute)}, // Session 3: 王五 - 等待分配 (waiting) {SessionID: sessions[2].ID, SenderType: "visitor", Content: "您好,请问企业版是否有优惠?我们公司需要50个坐席", Type: "text", Seq: 1, SentAt: now.Add(-2 * time.Minute)}, // Session 4: 赵六科技 - 已结束 {SessionID: sessions[3].ID, SenderType: "visitor", Content: "产品使用手册在哪里下载?", Type: "text", Seq: 1, SentAt: now.Add(-2 * time.Hour)}, {SessionID: sessions[3].ID, SenderType: "agent", SenderID: &agent3.ID, Content: "您好,请在官网帮助中心→文档下载中获取,也可直接访问 docs.example.com", Type: "text", Seq: 2, SentAt: now.Add(-1 * time.Hour + 55*time.Minute)}, {SessionID: sessions[3].ID, SenderType: "visitor", Content: "找到了,谢谢!", Type: "text", Seq: 3, SentAt: now.Add(-1 * time.Hour + 50*time.Minute)}, // Session 5: 钱七 - 已结束 {SessionID: sessions[4].ID, SenderType: "visitor", Content: "退款什么时候到账?已经3天了", Type: "text", Seq: 1, SentAt: now.Add(-3 * time.Hour)}, {SessionID: sessions[4].ID, SenderType: "agent", SenderID: &agent1.ID, Content: "您好,退款一般在3-5个工作日到账,我帮您催一下财务", Type: "text", Seq: 2, SentAt: now.Add(-2 * time.Hour + 55*time.Minute)}, {SessionID: sessions[4].ID, SenderType: "agent", SenderID: &agent1.ID, Content: "已为您加急处理,预计明天到账,请注意查收", Type: "text", Seq: 3, SentAt: now.Add(-2 * time.Hour + 50*time.Minute)}, } model.DB.Create(&messages) // Knowledge categories(两级树,对齐设计稿) tid := tenants[0].ID rootFAQ := model.Category{TenantID: tid, Name: "产品常见问题"} rootAfter := model.Category{TenantID: tid, Name: "售后服务"} rootTech := model.Category{TenantID: tid, Name: "技术支持"} rootPolicy := model.Category{TenantID: tid, Name: "政策条款"} rootQuick := model.Category{TenantID: tid, Name: "快捷回复模板"} model.DB.Create(&rootFAQ) model.DB.Create(&rootAfter) model.DB.Create(&rootTech) model.DB.Create(&rootPolicy) model.DB.Create(&rootQuick) subAccount := model.Category{TenantID: tid, Name: "账户相关", ParentID: &rootFAQ.ID} subFeature := model.Category{TenantID: tid, Name: "功能使用", ParentID: &rootFAQ.ID} subBilling := model.Category{TenantID: tid, Name: "计费问题", ParentID: &rootFAQ.ID} model.DB.Create(&subAccount) model.DB.Create(&subFeature) model.DB.Create(&subBilling) entries := []model.KnowledgeEntry{ {TenantID: tid, CategoryID: subAccount.ID, Title: "如何修改登录密码", Content: "登录后在右上角头像→个人设置→修改密码。也可通过手机验证码或邮箱链接重置。", Status: "published", UsageCount: 156}, {TenantID: tid, CategoryID: subBilling.ID, Title: "支持哪些支付方式", Content: "支持微信支付、支付宝、银行转账", Status: "published", UsageCount: 98}, {TenantID: tid, CategoryID: subFeature.ID, Title: "如何升级为高级会员", Content: "进入账户设置页面,选择会员升级选项,支持月付和年付两种方式,年付享8折优惠", Status: "published", UsageCount: 88}, {TenantID: tid, CategoryID: rootAfter.ID, Title: "退货流程说明", Content: "在线申请→审核→寄回→退款,全程3-5个工作日", Status: "published", UsageCount: 45}, {TenantID: tid, CategoryID: rootTech.ID, Title: "API接口文档", Content: "开发者文档请访问 docs.example.com/api", Status: "draft", UsageCount: 0}, {TenantID: tid, CategoryID: rootPolicy.ID, Title: "隐私政策更新说明", Content: "我们会定期更新隐私政策,重大变更将通过站内信通知", Status: "published", UsageCount: 12}, {TenantID: tid, CategoryID: rootQuick.ID, Title: "欢迎语模板", Content: "您好!欢迎来到客服云,请问有什么可以帮您的?", Status: "published", UsageCount: 230}, } model.DB.Create(&entries) // 团队快捷回复(独立模块;知识库「快捷回复模板」分类可逐步弃用) quickReplies := []model.QuickReply{ {TenantID: tid, Scope: "team", Title: "打招呼", Content: "您好!欢迎来到客服云,请问有什么可以帮您的?", Shortcut: "nh", GroupName: "通用", Status: "published", UsageCount: 120}, {TenantID: tid, Scope: "team", Title: "稍等查询", Content: "您好,正在为您查询,请稍候。", Shortcut: "sd", GroupName: "通用", Status: "published", UsageCount: 80}, {TenantID: tid, Scope: "team", Title: "提供订单号", Content: "为更快处理,请您提供订单号或相关截图,谢谢。", Shortcut: "ddh", GroupName: "售后", Status: "published", UsageCount: 56}, {TenantID: tid, Scope: "team", Title: "结束语", Content: "感谢您的咨询,祝您生活愉快!如有其它问题随时联系我们。", Shortcut: "js", GroupName: "通用", Status: "draft", UsageCount: 0}, } model.DB.Create(&quickReplies) // Announcements model.DB.Create(&[]model.Announcement{ {Title: "系统维护通知", Content: "平台将于7月20日 02:00-04:00 进行例行维护", Status: "published"}, {Title: "新功能上线", Content: "知识库批量导入功能已上线", Status: "published"}, }) log.Println("默认租户管理员: kefu_admin / kefu_admin123") log.Println("其它种子账号密码均为: kefu_admin123") } func ptr[T any](v T) *T { return &v }