XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

EARSystem

【ASP】耳间,网页端的情绪智能推荐系统

公开
关注 0 Fork 0 Star 0
UTF-8
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>(T value) => JsonSerializer.Serialize(value);
    public static T[] Read<T>(string? value) => string.IsNullOrWhiteSpace(value) ? [] : JsonSerializer.Deserialize<T[]>(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));
}