using System.Text.Json; namespace EARSystem.Core; public enum PlaceCategory { Dining, Cafe, Park, Bookstore, Gallery, Entertainment } public enum RecommendationGoal { Relax, Explore, Social, Focus } public enum DataMode { Demo, Online } public enum PlaceSource { Demo, Collected } public static class Labels { public static readonly string[] EmotionTags = ["开心", "平静", "焦虑", "烦躁", "低落", "疲惫"]; public static readonly string[] SceneTags = ["学习", "工作", "独处", "聚会", "准备学习", "准备工作"]; public static string Category(PlaceCategory c) => c switch { PlaceCategory.Dining => "餐饮", PlaceCategory.Cafe => "咖啡馆", PlaceCategory.Park => "公园", PlaceCategory.Bookstore => "书店", PlaceCategory.Gallery => "展馆", _ => "休闲娱乐" }; public static string Goal(RecommendationGoal g) => g switch { RecommendationGoal.Relax => "放松恢复", RecommendationGoal.Social => "社交玩乐", RecommendationGoal.Focus => "专注学习", _ => "轻松探索" }; public static string Json(T value) => JsonSerializer.Serialize(value); public static T[] Read(string? value) => string.IsNullOrWhiteSpace(value) ? [] : JsonSerializer.Deserialize(value) ?? []; } public class MoodEntry { public Guid Id { get; set; } = Guid.NewGuid(); public string UserId { get; set; } = ""; public int Mood { get; set; } public int Energy { get; set; } public int Stress { get; set; } public string TagsJson { get; set; } = "[]"; public string Thoughts { get; set; } = ""; public DateTimeOffset RecordedAt { get; set; } public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; public Guid Version { get; set; } = Guid.NewGuid(); public Guid? ExperienceId { get; set; } } public class UserPreference { public string UserId { get; set; } = ""; public string FavoriteCategoriesJson { get; set; } = "[]"; public string BlockedCategoriesJson { get; set; } = "[]"; public string BlockedPlacesJson { get; set; } = "[]"; public decimal? Budget { get; set; } public int RadiusMeters { get; set; } = 3000; } public class Place { public Guid Id { get; set; } = Guid.NewGuid(); public string Name { get; set; } = ""; public string Address { get; set; } = ""; public string Description { get; set; } = ""; public PlaceCategory Category { get; set; } public PlaceSource Source { get; set; } = PlaceSource.Demo; public double Longitude { get; set; } public double Latitude { get; set; } public decimal? Price { get; set; } public double? Rating { get; set; } public int? OpenHour { get; set; } public int? CloseHour { get; set; } public string? AuthorizedExternalId { get; set; } public PlaceAtmosphere? Atmosphere { get; set; } } // Baselines are kept separately from user contributions, so editing/deleting a vote is reversible. public class PlaceAtmosphere { public Guid PlaceId { get; set; } public double Quiet { get; set; } = 3; public double Comfort { get; set; } = 3; public double Nature { get; set; } = 3; public double Liveliness { get; set; } = 3; public double Social { get; set; } = 3; public double Focus { get; set; } = 3; public int BaselineCount { get; set; } public string Evidence { get; set; } = ""; public double[] Values() => [Quiet, Comfort, Nature, Liveliness, Social, Focus]; } public class Experience { public Guid Id { get; set; } = Guid.NewGuid(); public string UserId { get; set; } = ""; public Guid PlaceId { get; set; } public Place Place { get; set; } = null!; public DateTimeOffset VisitedAt { get; set; } public int VisitDay { get; set; } public int Satisfaction { get; set; } public int? MoodBefore { get; set; } public int? MoodAfter { get; set; } public int Quiet { get; set; } = 3; public int Comfort { get; set; } = 3; public int Nature { get; set; } = 3; public int Liveliness { get; set; } = 3; public int Social { get; set; } = 3; public int Focus { get; set; } = 3; public string Thoughts { get; set; } = ""; public bool Contribute { get; set; } public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; public Guid Version { get; set; } = Guid.NewGuid(); public double[] Values() => [Quiet, Comfort, Nature, Liveliness, Social, Focus]; } public class Favorite { public string UserId { get; set; } = ""; public Guid PlaceId { get; set; } public Place Place { get; set; } = null!; public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; } public class RecommendationRecord { public Guid Id { get; set; } = Guid.NewGuid(); public string UserId { get; set; } = ""; public RecommendationGoal Goal { get; set; } public DataMode Mode { get; set; } public string AlgorithmVersion { get; set; } = "1"; public string ResultsJson { get; set; } = "[]"; public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; public double DurationMs { get; set; } } public class RuleConfiguration { public int Id { get; set; } public string Version { get; set; } = "1"; public string WeightsJson { get; set; } = Labels.Json(new AlgorithmWeights()); public Guid ConcurrencyStamp { get; set; } = Guid.NewGuid(); public DateTimeOffset? DemoSeededAt { get; set; } } public static class ChinaTime { public static DateTimeOffset Local(DateTimeOffset time) => time.ToOffset(TimeSpan.FromHours(8)); public static DateOnly Day(DateTimeOffset time) => DateOnly.FromDateTime(Local(time).DateTime); public static int DayKey(DateTimeOffset time) => Day(time).DayNumber; public static DateTimeOffset Start(DateOnly day) => new(day.ToDateTime(TimeOnly.MinValue), TimeSpan.FromHours(8)); public static DateOnly Monday(DateOnly day) => day.AddDays(-(((int)day.DayOfWeek + 6) % 7)); }