Files
kefu/im/backend/internal/testusers/fixtures.go
T
2026-09-03 08:38:17 +08:00

95 lines
3.9 KiB
Go

// Package testusers creates explicitly labelled, non-loginable test profiles.
// It is only used by an operator-invoked CLI, never during server startup.
package testusers
import (
"fmt"
"net/url"
"strings"
)
const Batch = "cn-adults-20260831-v1"
const Count = 100
type Profile struct {
PublicID string `json:"publicId"`
IsTest bool `json:"isTest"`
TestBatch string `json:"testBatch"`
Label string `json:"label"`
Nickname string `json:"nickname"`
Gender int `json:"gender"`
Birthday string `json:"birthday"`
Height int `json:"height"`
CityCode string `json:"cityCode"`
City string `json:"city"`
Occupation string `json:"occupation"`
Bio string `json:"bio"`
AvatarFile string `json:"avatarFile"`
Avatar string `json:"avatar"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
}
type fixtureCity struct {
Code string
Name string
Latitude float64
Longitude float64
}
// Avatars contains five independently generated adult portraits per gender.
// Reuse is intentional for fixtures; these are not 100 real identities.
func Avatars() []string {
files := make([]string, 0, 10)
for gender := 1; gender <= 2; gender++ {
for n := 1; n <= 5; n++ {
files = append(files, fmt.Sprintf("900028%d%d-1.png", gender, n))
}
}
return files
}
func Generate(publicBase string) ([]Profile, error) {
base, err := url.Parse(strings.TrimRight(publicBase, "/"))
if err != nil || base.Host == "" || base.User != nil || base.RawQuery != "" || base.Fragment != "" || (base.Scheme != "http" && base.Scheme != "https") {
return nil, fmt.Errorf("public base must be an absolute HTTP(S) uploads URL without credentials, query or fragment")
}
if base.Scheme == "http" && base.Hostname() != "127.0.0.1" && base.Hostname() != "localhost" && base.Hostname() != "::1" {
return nil, fmt.Errorf("non-local avatar URLs require HTTPS")
}
surnames := []string{"陈", "林", "周", "许", "苏", "沈", "陆", "顾", "方", "季"}
given := [][]string{{"沐川", "知远", "星河", "景行", "予安"}, {"晚晴", "知夏", "语桐", "清禾", "若宁"}}
cities := []fixtureCity{
{"310100", "上海", 31.2304, 121.4737}, {"440100", "广州", 23.1291, 113.2644},
{"440300", "深圳", 22.5431, 114.0579}, {"330100", "杭州", 30.2741, 120.1551},
{"510100", "成都", 30.5728, 104.0668}, {"420100", "武汉", 30.5928, 114.3055},
{"320100", "南京", 32.0603, 118.7969}, {"350200", "厦门", 24.4798, 118.0894},
{"610100", "西安", 34.3416, 108.9398}, {"370200", "青岛", 36.0671, 120.3826},
}
jobs := []string{"设计师", "工程师", "教师", "摄影师", "产品经理"}
hobbies := []string{"摄影与城市漫步", "阅读与咖啡", "跑步与音乐", "旅行与美食", "电影与绘画"}
ages := [2][5]int{{27, 30, 24, 33, 28}, {26, 29, 24, 32, 27}}
items := make([]Profile, 0, Count)
for gender := 1; gender <= 2; gender++ {
for i := 0; i < 50; i++ {
avatarFile := fmt.Sprintf("900028%d%d-1.png", gender, i%5+1)
city := cities[i%len(cities)]
ring := i / len(cities)
latitude := city.Latitude + float64(ring-2)*0.012 + float64(gender-1)*0.006
longitude := city.Longitude + float64((ring*2+gender)%5-2)*0.012
items = append(items, Profile{
PublicID: fmt.Sprintf("TESTCN%06d", (gender-1)*50+i+1),
IsTest: true, TestBatch: Batch, Label: "测试用户",
Nickname: "测试·" + surnames[i/5] + given[gender-1][i%5],
Gender: gender, Birthday: fmt.Sprintf("%d-%02d-%02d", 2026-ages[gender-1][i%5], i%6+1, i%27+1),
Height: 160 + (2-gender)*12 + i%12,
CityCode: city.Code, City: city.Name, Occupation: jobs[i%len(jobs)],
Bio: "【测试数据】虚构资料,AI生成中国成年人头像,仅供功能测试,非真实交友用户。兴趣示例:" + hobbies[i%len(hobbies)] + "。",
AvatarFile: avatarFile, Avatar: base.String() + "/" + avatarFile,
Latitude: latitude, Longitude: longitude,
})
}
}
return items, nil
}