取名小程序开发
This commit is contained in:
114
DouyinApi.Api/Controllers/MiniProgram/NamingController.cs
Normal file
114
DouyinApi.Api/Controllers/MiniProgram/NamingController.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using DouyinApi.Controllers;
|
||||
using DouyinApi.IServices;
|
||||
using DouyinApi.Model.Naming;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace DouyinApi.Api.Controllers.MiniProgram
|
||||
{
|
||||
[Produces("application/json")]
|
||||
[Route("api/naming")]
|
||||
[AllowAnonymous]
|
||||
public class NamingController : BaseApiController
|
||||
{
|
||||
private readonly INamingService _namingService;
|
||||
private readonly ILogger<NamingController> _logger;
|
||||
|
||||
public NamingController(INamingService namingService, ILogger<NamingController> logger)
|
||||
{
|
||||
_namingService = namingService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpPost("validate-surname")]
|
||||
public async Task<IActionResult> ValidateSurname([FromBody] SurnameValidationRequest request)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return Ok(new { isValid = false, message = "请输入合法姓氏" });
|
||||
}
|
||||
|
||||
var surname = request.Surname?.Trim() ?? string.Empty;
|
||||
var isValid = await _namingService.ValidateSurnameAsync(surname);
|
||||
return Ok(new
|
||||
{
|
||||
isValid,
|
||||
message = isValid ? string.Empty : "请输入合法姓氏"
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("generate")]
|
||||
public async Task<IActionResult> Generate([FromBody] NamingRequest request)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(new { message = "参数不合法" });
|
||||
}
|
||||
|
||||
var normalizedRequest = NormalizeRequest(request);
|
||||
|
||||
try
|
||||
{
|
||||
var response = await _namingService.GenerateNamesAsync(normalizedRequest);
|
||||
return Ok(new { results = response.Results });
|
||||
}
|
||||
catch (ArgumentException ex) when (ex.Message == "invalid_surname")
|
||||
{
|
||||
return BadRequest(new { message = "INVALID_SURNAME" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Generate naming failed");
|
||||
return StatusCode(500, new { message = "GENERATION_FAILED" });
|
||||
}
|
||||
}
|
||||
|
||||
private static NamingRequest NormalizeRequest(NamingRequest request)
|
||||
{
|
||||
var normalized = new NamingRequest
|
||||
{
|
||||
Surname = request.Surname?.Trim() ?? string.Empty,
|
||||
Gender = string.IsNullOrWhiteSpace(request.Gender) ? "male" : request.Gender.Trim().ToLowerInvariant(),
|
||||
NameLength = string.IsNullOrWhiteSpace(request.NameLength) ? "double" : request.NameLength.Trim().ToLowerInvariant(),
|
||||
BirthDate = NormalizeDate(request.BirthDate),
|
||||
BirthTime = NormalizeTime(request.BirthTime)
|
||||
};
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private static string NormalizeDate(string date)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(date))
|
||||
{
|
||||
return DateTime.UtcNow.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (DateTime.TryParse(date, out var parsed))
|
||||
{
|
||||
return parsed.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
private static string NormalizeTime(string time)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(time))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if (TimeSpan.TryParse(time, out var parsed))
|
||||
{
|
||||
return DateTime.Today.Add(parsed).ToString("HH:mm", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
return time;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
DouyinApi.IServices/INamingService.cs
Normal file
13
DouyinApi.IServices/INamingService.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using DouyinApi.Model.Naming;
|
||||
|
||||
namespace DouyinApi.IServices
|
||||
{
|
||||
public interface INamingService
|
||||
{
|
||||
Task<bool> ValidateSurnameAsync(string surname);
|
||||
|
||||
Task<NamingResponse> GenerateNamesAsync(NamingRequest request);
|
||||
}
|
||||
}
|
||||
26
DouyinApi.Model/Naming/NamingRequest.cs
Normal file
26
DouyinApi.Model/Naming/NamingRequest.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace DouyinApi.Model.Naming
|
||||
{
|
||||
public class NamingRequest
|
||||
{
|
||||
[Required]
|
||||
[MinLength(1)]
|
||||
[MaxLength(2)]
|
||||
public string Surname { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
[RegularExpression("male|female", ErrorMessage = "gender_invalid")]
|
||||
public string Gender { get; set; } = "male";
|
||||
|
||||
[Required]
|
||||
public string BirthDate { get; set; } = string.Empty;
|
||||
|
||||
public string BirthTime { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
[RegularExpression("single|double", ErrorMessage = "name_length_invalid")]
|
||||
public string NameLength { get; set; } = "double";
|
||||
}
|
||||
}
|
||||
16
DouyinApi.Model/Naming/NamingResponse.cs
Normal file
16
DouyinApi.Model/Naming/NamingResponse.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DouyinApi.Model.Naming
|
||||
{
|
||||
public class NamingResponse
|
||||
{
|
||||
public IEnumerable<NamingSuggestion> Results { get; set; } = new List<NamingSuggestion>();
|
||||
}
|
||||
|
||||
public class NamingSuggestion
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public string Meaning { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
12
DouyinApi.Model/Naming/SurnameValidationRequest.cs
Normal file
12
DouyinApi.Model/Naming/SurnameValidationRequest.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace DouyinApi.Model.Naming
|
||||
{
|
||||
public class SurnameValidationRequest
|
||||
{
|
||||
[Required]
|
||||
[MinLength(1)]
|
||||
[MaxLength(2)]
|
||||
public string Surname { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
204
DouyinApi.Services/NamingService.cs
Normal file
204
DouyinApi.Services/NamingService.cs
Normal file
@@ -0,0 +1,204 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using DouyinApi.IServices;
|
||||
using DouyinApi.Model.Naming;
|
||||
|
||||
namespace DouyinApi.Services
|
||||
{
|
||||
public class NamingService : INamingService
|
||||
{
|
||||
private static readonly Regex SurnameRegex = new(@"^[\u4e00-\u9fa5]{1,2}$", RegexOptions.Compiled);
|
||||
|
||||
private static readonly IReadOnlyList<char> MaleCharacters = new[]
|
||||
{
|
||||
'辰', '昇', '曜', '瀚', '宸', '沐', '景', '霖', '晟', '玥', '骁', '煜', '澜', '珩', '聿', '澄', '钧'
|
||||
};
|
||||
|
||||
private static readonly IReadOnlyList<char> FemaleCharacters = new[]
|
||||
{
|
||||
'瑶', '霏', '婉', '绮', '璇', '芷', '乂', '灵', '沁', '语', '晴', '若', '绫', '芸', '络', '梦', '澜'
|
||||
};
|
||||
|
||||
private static readonly IReadOnlyList<char> NeutralCharacters = new[]
|
||||
{
|
||||
'玄', '洛', '岚', '澈', '岑', '泓', '澜', '烨', '闻', '黎', '墨', '夙', '羲', '霆', '渊', '翎'
|
||||
};
|
||||
|
||||
private static readonly Dictionary<char, string> CharacterMeanings = new()
|
||||
{
|
||||
['辰'] = "辰曜星光",
|
||||
['昇'] = "旭日东升",
|
||||
['曜'] = "光耀万里",
|
||||
['瀚'] = "瀚海无垠",
|
||||
['宸'] = "王者气度",
|
||||
['沐'] = "沐浴祥瑞",
|
||||
['景'] = "景行德高",
|
||||
['霖'] = "甘霖润泽",
|
||||
['晟'] = "光明昌盛",
|
||||
['玥'] = "王者之玉",
|
||||
['骁'] = "英勇不屈",
|
||||
['煜'] = "照耀四方",
|
||||
['澜'] = "碧波浩渺",
|
||||
['珩'] = "璞玉内敛",
|
||||
['聿'] = "持守正道",
|
||||
['澄'] = "心境澄明",
|
||||
['钧'] = "乾坤平衡",
|
||||
['瑶'] = "瑶光琼华",
|
||||
['霏'] = "霏霏瑞雪",
|
||||
['婉'] = "柔婉清扬",
|
||||
['绮'] = "绮丽灵动",
|
||||
['璇'] = "璇玑回转",
|
||||
['芷'] = "香草高洁",
|
||||
['乂'] = "安然有序",
|
||||
['灵'] = "灵秀夺目",
|
||||
['沁'] = "沁心甘露",
|
||||
['语'] = "言语有光",
|
||||
['晴'] = "晴空暖阳",
|
||||
['若'] = "若水明澈",
|
||||
['绫'] = "绫罗轻盈",
|
||||
['芸'] = "芸芸芳草",
|
||||
['络'] = "络绎华彩",
|
||||
['梦'] = "梦想成真",
|
||||
['玄'] = "玄妙莫测",
|
||||
['洛'] = "洛水灵韵",
|
||||
['岚'] = "山岚清气",
|
||||
['澈'] = "心境通透",
|
||||
['岑'] = "峻岭深沉",
|
||||
['泓'] = "泓泉清澈",
|
||||
['烨'] = "火光通明",
|
||||
['闻'] = "名闻四海",
|
||||
['黎'] = "黎明曙光",
|
||||
['墨'] = "墨香书韵",
|
||||
['夙'] = "夙愿成真",
|
||||
['羲'] = "伏羲灵息",
|
||||
['霆'] = "雷霆万钧",
|
||||
['渊'] = "渊源深厚",
|
||||
['翎'] = "翎羽轻灵"
|
||||
};
|
||||
|
||||
private static readonly string[] Blessings =
|
||||
{
|
||||
"引瑞气入怀,扶摇直上",
|
||||
"承先祖之德,守家风之和",
|
||||
"与四时共鸣,步步生辉",
|
||||
"纳天地灵气,行稳致远",
|
||||
"凝万象之精魄,护佑昌隆",
|
||||
"藏锋于怀,静待时来",
|
||||
"兼济天下情怀,拥抱广阔未来"
|
||||
};
|
||||
|
||||
public Task<bool> ValidateSurnameAsync(string surname)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(surname))
|
||||
{
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
return Task.FromResult(SurnameRegex.IsMatch(surname.Trim()));
|
||||
}
|
||||
|
||||
public async Task<NamingResponse> GenerateNamesAsync(NamingRequest request)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
}
|
||||
|
||||
if (!await ValidateSurnameAsync(request.Surname))
|
||||
{
|
||||
throw new ArgumentException("invalid_surname");
|
||||
}
|
||||
|
||||
var seed = CreateSeed(request);
|
||||
var random = new Random(seed);
|
||||
var pool = request.Gender == "female" ? FemaleCharacters : MaleCharacters;
|
||||
var fallbackPool = NeutralCharacters;
|
||||
|
||||
var suggestions = new List<NamingSuggestion>();
|
||||
var attempts = 0;
|
||||
|
||||
while (suggestions.Count < 5 && attempts < 100)
|
||||
{
|
||||
attempts++;
|
||||
var given = GenerateGivenName(request.NameLength, pool, fallbackPool, random);
|
||||
var fullName = $"{request.Surname}{given}";
|
||||
if (suggestions.Any(s => s.Name == fullName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var meaning = ComposeMeaning(request.Surname, given, request.BirthDate, request.BirthTime, random);
|
||||
suggestions.Add(new NamingSuggestion
|
||||
{
|
||||
Name = fullName,
|
||||
Meaning = meaning
|
||||
});
|
||||
}
|
||||
|
||||
if (!suggestions.Any())
|
||||
{
|
||||
suggestions.Add(new NamingSuggestion
|
||||
{
|
||||
Name = $"{request.Surname}玄珏",
|
||||
Meaning = "玄珠映月,珏石生辉,与生辰相应,寓意行稳致远"
|
||||
});
|
||||
}
|
||||
|
||||
return new NamingResponse
|
||||
{
|
||||
Results = suggestions
|
||||
};
|
||||
}
|
||||
|
||||
private static int CreateSeed(NamingRequest request)
|
||||
{
|
||||
var raw = $"{request.Surname}-{request.Gender}-{request.BirthDate}-{request.BirthTime}-{request.NameLength}";
|
||||
var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(raw));
|
||||
return Math.Abs(BitConverter.ToInt32(bytes, 0));
|
||||
}
|
||||
|
||||
private static string GenerateGivenName(
|
||||
string nameLength,
|
||||
IReadOnlyList<char> primaryPool,
|
||||
IReadOnlyList<char> fallbackPool,
|
||||
Random random)
|
||||
{
|
||||
var buffer = new StringBuilder();
|
||||
var length = nameLength == "single" ? 1 : 2;
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
var pool = (i == 0) ? primaryPool : fallbackPool;
|
||||
buffer.Append(pool[random.Next(pool.Count)]);
|
||||
}
|
||||
return buffer.ToString();
|
||||
}
|
||||
|
||||
private static string ComposeMeaning(string surname, string given, string birthDate, string birthTime, Random random)
|
||||
{
|
||||
var fragments = new List<string>();
|
||||
foreach (var ch in given)
|
||||
{
|
||||
if (CharacterMeanings.TryGetValue(ch, out var desc))
|
||||
{
|
||||
fragments.Add(desc);
|
||||
}
|
||||
else
|
||||
{
|
||||
fragments.Add($"{ch}寓意祥瑞");
|
||||
}
|
||||
}
|
||||
|
||||
var blessing = Blessings[random.Next(Blessings.Length)];
|
||||
var birthFragment = string.IsNullOrWhiteSpace(birthTime)
|
||||
? $"{birthDate}之辰"
|
||||
: $"{birthDate} {birthTime} 时刻";
|
||||
|
||||
return $"{string.Join(",", fragments)},与{surname}氏气脉相连,于{birthFragment}呼应,{blessing}。";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user