95 lines
3.9 KiB
Go
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
|
|
}
|