feat(ECR-011): 昵称、首页贴士与档案 Self 唯一/合盘交叉校验
每账号仅一条 self(migration 40902);合盘只选 TA;账号昵称可改;首页穿衣/颜色/养生贴士。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
// Package nickname generates default display names: 意象词 + 场景词.
|
||||
package nickname
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// imageryWords: 100 soft, product-safe imagery stems (愈心谷 tone).
|
||||
var imageryWords = []string{
|
||||
"心语", "微光", "星愿", "清风", "暖心", "柔光", "浅梦", "静水", "云舒", "月白",
|
||||
"初晴", "晚风", "晨露", "细雨", "薄雾", "青禾", "橘光", "蜜语", "安然", "念初",
|
||||
"听澜", "拾光", "半夏", "秋迟", "春晓", "夏安", "冬暖", "暮色", "晓风", "星河",
|
||||
"流年", "浅唱", "低语", "轻舟", "远山", "近水", "素心", "明澄", "澄澈", "清欢",
|
||||
"淡然", "悠然", "恬静", "安宁", "和风", "暖阳", "软光", "碎金", "银杏", "白露",
|
||||
"谷雨", "立秋", "小满", "惊蛰", "霜降", "芒种", "处暑", "大寒", "小雪", "清明",
|
||||
"听雪", "望舒", "栖云", "栖月", "栖星", "栖心", "栖光", "栖梦", "栖风", "栖雨",
|
||||
"知夏", "知秋", "知春", "知暖", "知光", "知心", "知安", "知宁", "知澄", "知柔",
|
||||
"若水", "若云", "若风", "若光", "若梦", "若安", "若澄", "若暖", "若柔", "若心",
|
||||
"一念", "一息", "一寸", "一抹", "一缕", "一盏", "一程", "一湾", "一隅", "一席",
|
||||
}
|
||||
|
||||
// sceneWords: 100 place-like endings that read naturally after imagery stems.
|
||||
var sceneWords = []string{
|
||||
"岛", "谷", "屿", "里", "湾", "庭", "舍", "居", "苑", "港",
|
||||
"岸", "亭", "巷", "院", "轩", "斋", "庐", "屋", "堂", "阁",
|
||||
"台", "畔", "角", "隅", "间", "处", "边", "旁", "侧", "前",
|
||||
"后", "上", "下", "中", "外", "内", "东", "西", "南", "北",
|
||||
"林", "原", "坡", "丘", "岭", "峰", "溪", "泉", "湖", "海",
|
||||
"田", "野", "陌", "径", "路", "桥", "门", "窗", "廊", "阶",
|
||||
"坊", "市", "镇", "村", "庄", "乡", "社", "园", "圃", "畦",
|
||||
"巢", "窝", "坞", "渡", "津", "洲", "渚", "汀", "滩", "礁",
|
||||
"岚", "岫", "岩", "石", "沙", "草", "花", "叶", "枝", "根",
|
||||
"灯", "烛", "席", "枕", "案", "书", "笺", "墨", "茶", "盏",
|
||||
}
|
||||
|
||||
// Random returns 意象词+场景词, e.g. 心语岛.
|
||||
func Random() string {
|
||||
a := pick(len(imageryWords))
|
||||
b := pick(len(sceneWords))
|
||||
return imageryWords[a] + sceneWords[b]
|
||||
}
|
||||
|
||||
func pick(n int) int {
|
||||
if n <= 0 {
|
||||
return 0
|
||||
}
|
||||
v, err := rand.Int(rand.Reader, big.NewInt(int64(n)))
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return int(v.Int64())
|
||||
}
|
||||
|
||||
// Normalize trims and clamps nickname length (1–16 runes).
|
||||
func Normalize(s string) string {
|
||||
r := []rune(s)
|
||||
for len(r) > 0 && (r[0] == ' ' || r[0] == '\t' || r[0] == '\n') {
|
||||
r = r[1:]
|
||||
}
|
||||
for len(r) > 0 {
|
||||
last := r[len(r)-1]
|
||||
if last == ' ' || last == '\t' || last == '\n' {
|
||||
r = r[:len(r)-1]
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if len(r) > 16 {
|
||||
r = r[:16]
|
||||
}
|
||||
return string(r)
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package nickname
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func TestWordBankSize(t *testing.T) {
|
||||
if len(imageryWords) != 100 {
|
||||
t.Fatalf("imagery want 100 got %d", len(imageryWords))
|
||||
}
|
||||
if len(sceneWords) != 100 {
|
||||
t.Fatalf("scene want 100 got %d", len(sceneWords))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomFormat(t *testing.T) {
|
||||
seen := map[string]bool{}
|
||||
for i := 0; i < 50; i++ {
|
||||
n := Random()
|
||||
if utf8.RuneCountInString(n) < 2 {
|
||||
t.Fatalf("too short: %q", n)
|
||||
}
|
||||
seen[n] = true
|
||||
ok := false
|
||||
for _, im := range imageryWords {
|
||||
for _, sc := range sceneWords {
|
||||
if n == im+sc {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("not from bank: %q", n)
|
||||
}
|
||||
}
|
||||
if len(seen) < 5 {
|
||||
t.Fatalf("expected some variety, got %d unique", len(seen))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalize(t *testing.T) {
|
||||
if got := Normalize(" 心语岛 "); got != "心语岛" {
|
||||
t.Fatalf("got %q", got)
|
||||
}
|
||||
long := strings.Repeat("啊", 20)
|
||||
if utf8.RuneCountInString(Normalize(long)) != 16 {
|
||||
t.Fatalf("clamp failed")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user