This commit is contained in:
jiangdong
2025-10-03 11:24:11 +08:00
commit d81cf186b0
67 changed files with 10243 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
using FateMaster.API.Data;
using FateMaster.API.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace FateMaster.API.Controllers.Admin;
[ApiController]
[Route("api/admin/[controller]")]
public class PricesController : ControllerBase
{
private readonly ApplicationDbContext _context;
public PricesController(ApplicationDbContext context)
{
_context = context;
}
/// <summary>
/// 获取所有价格配置
/// </summary>
[HttpGet]
public async Task<ActionResult<List<PriceConfig>>> GetAll()
{
return await _context.PriceConfigs.ToListAsync();
}
/// <summary>
/// 更新价格配置
/// </summary>
[HttpPut("{id}")]
public async Task<ActionResult> Update(int id, [FromBody] UpdatePriceRequest request)
{
var price = await _context.PriceConfigs.FindAsync(id);
if (price == null)
{
return NotFound();
}
price.Price = request.Price;
price.IsEnabled = request.IsEnabled;
price.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
return Ok(price);
}
}
public record UpdatePriceRequest(decimal Price, bool IsEnabled);

View File

@@ -0,0 +1,87 @@
using FateMaster.API.Data;
using FateMaster.API.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace FateMaster.API.Controllers;
[ApiController]
[Route("api/admin/[controller]")]
public class RecordsController : ControllerBase
{
private readonly ApplicationDbContext _context;
public RecordsController(ApplicationDbContext context)
{
_context = context;
}
/// <summary>
/// 获取卜卦记录列表
/// </summary>
[HttpGet]
public async Task<ActionResult> GetRecords(
[FromQuery] int page = 1,
[FromQuery] int pageSize = 20,
[FromQuery] string? type = null,
[FromQuery] string? paymentStatus = null)
{
var query = _context.DivinationRecords.AsQueryable();
if (!string.IsNullOrEmpty(type))
{
query = query.Where(r => r.Type == type);
}
if (!string.IsNullOrEmpty(paymentStatus))
{
query = query.Where(r => r.PaymentStatus == paymentStatus);
}
var total = await query.CountAsync();
var records = await query
.OrderByDescending(r => r.CreatedAt)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
return Ok(new
{
total,
page,
pageSize,
data = records
});
}
/// <summary>
/// 获取统计数据
/// </summary>
[HttpGet("statistics")]
public async Task<ActionResult> GetStatistics()
{
var total = await _context.DivinationRecords.CountAsync();
var paidCount = await _context.DivinationRecords
.CountAsync(r => r.PaymentStatus == "paid");
var totalRevenue = await _context.DivinationRecords
.Where(r => r.PaymentStatus == "paid")
.SumAsync(r => r.Amount);
var typeStats = await _context.DivinationRecords
.GroupBy(r => r.Type)
.Select(g => new
{
Type = g.Key,
Count = g.Count()
})
.ToListAsync();
return Ok(new
{
total,
paidCount,
totalRevenue,
typeStats
});
}
}

View File

@@ -0,0 +1,88 @@
using FateMaster.API.Data;
using FateMaster.API.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace FateMaster.API.Controllers;
[ApiController]
[Route("api/[controller]")]
public class DivinationController : ControllerBase
{
private readonly ApplicationDbContext _context;
private readonly ILogger<DivinationController> _logger;
public DivinationController(
ApplicationDbContext context,
ILogger<DivinationController> logger)
{
_context = context;
_logger = logger;
}
/// <summary>
/// 获取价格配置
/// </summary>
[HttpGet("prices")]
public async Task<ActionResult<List<PriceConfig>>> GetPrices()
{
var prices = await _context.PriceConfigs
.Where(p => p.IsEnabled)
.ToListAsync();
return Ok(prices);
}
/// <summary>
/// 提交卜卦请求
/// </summary>
[HttpPost("submit")]
public async Task<ActionResult<DivinationRecord>> Submit([FromBody] SubmitRequest request)
{
try
{
var record = new DivinationRecord
{
Type = request.Type,
InputData = request.InputData,
PaymentStatus = "pending",
PaymentMethod = request.PaymentMethod,
Amount = request.Amount,
ClientIp = HttpContext.Connection.RemoteIpAddress?.ToString(),
Language = request.Language ?? "zh-CN"
};
_context.DivinationRecords.Add(record);
await _context.SaveChangesAsync();
return Ok(record);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error submitting divination request");
return StatusCode(500, new { message = "提交失败" });
}
}
/// <summary>
/// 获取卜卦结果
/// </summary>
[HttpGet("{id}")]
public async Task<ActionResult<DivinationRecord>> GetResult(long id)
{
var record = await _context.DivinationRecords.FindAsync(id);
if (record == null)
{
return NotFound();
}
return Ok(record);
}
}
public record SubmitRequest(
string Type,
string InputData,
string? PaymentMethod,
decimal Amount,
string? Language
);

View File

@@ -0,0 +1,47 @@
using FateMaster.API.Models;
using Microsoft.EntityFrameworkCore;
namespace FateMaster.API.Data;
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<DivinationRecord> DivinationRecords { get; set; }
public DbSet<SystemConfig> SystemConfigs { get; set; }
public DbSet<PriceConfig> PriceConfigs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// 配置索引
modelBuilder.Entity<DivinationRecord>()
.HasIndex(d => d.Type);
modelBuilder.Entity<DivinationRecord>()
.HasIndex(d => d.PaymentStatus);
modelBuilder.Entity<DivinationRecord>()
.HasIndex(d => d.CreatedAt);
modelBuilder.Entity<SystemConfig>()
.HasIndex(s => s.ConfigKey)
.IsUnique();
modelBuilder.Entity<PriceConfig>()
.HasIndex(p => p.ServiceType);
// 初始化数据
modelBuilder.Entity<PriceConfig>().HasData(
new PriceConfig { Id = 1, ServiceType = "bazi", Price = 99, Currency = "CNY" },
new PriceConfig { Id = 2, ServiceType = "career", Price = 88, Currency = "CNY" },
new PriceConfig { Id = 3, ServiceType = "marriage", Price = 88, Currency = "CNY" },
new PriceConfig { Id = 4, ServiceType = "tarot", Price = 66, Currency = "CNY" },
new PriceConfig { Id = 5, ServiceType = "zodiac", Price = 29, Currency = "CNY" }
);
}
}

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,86 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace FateMaster.API.Models;
/// <summary>
/// 卜卦记录表
/// </summary>
public class DivinationRecord
{
[Key]
public long Id { get; set; }
/// <summary>
/// 卜卦类型: bazi, career, marriage, tarot, zodiac
/// </summary>
[Required]
[MaxLength(50)]
public string Type { get; set; } = string.Empty;
/// <summary>
/// 用户输入数据(JSON)
/// </summary>
[Required]
[Column(TypeName = "json")]
public string InputData { get; set; } = string.Empty;
/// <summary>
/// 传统算法结果(JSON)
/// </summary>
[Column(TypeName = "json")]
public string? TraditionalResult { get; set; }
/// <summary>
/// AI解读结果
/// </summary>
[Column(TypeName = "text")]
public string? AIInterpretation { get; set; }
/// <summary>
/// 支付状态: pending, paid, failed
/// </summary>
[Required]
[MaxLength(20)]
public string PaymentStatus { get; set; } = "pending";
/// <summary>
/// 支付方式: alipay, paypal, stripe
/// </summary>
[MaxLength(20)]
public string? PaymentMethod { get; set; }
/// <summary>
/// 支付金额
/// </summary>
[Column(TypeName = "decimal(10,2)")]
public decimal Amount { get; set; }
/// <summary>
/// 支付订单号
/// </summary>
[MaxLength(100)]
public string? PaymentOrderId { get; set; }
/// <summary>
/// 客户端IP
/// </summary>
[MaxLength(50)]
public string? ClientIp { get; set; }
/// <summary>
/// 语言
/// </summary>
[MaxLength(10)]
public string? Language { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
/// <summary>
/// 更新时间
/// </summary>
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
}

View File

@@ -0,0 +1,49 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace FateMaster.API.Models;
/// <summary>
/// 价格配置表
/// </summary>
public class PriceConfig
{
[Key]
public int Id { get; set; }
/// <summary>
/// 服务类型: bazi, career, marriage, tarot, zodiac
/// </summary>
[Required]
[MaxLength(50)]
public string ServiceType { get; set; } = string.Empty;
/// <summary>
/// 价格
/// </summary>
[Required]
[Column(TypeName = "decimal(10,2)")]
public decimal Price { get; set; }
/// <summary>
/// 货币: CNY, USD
/// </summary>
[Required]
[MaxLength(10)]
public string Currency { get; set; } = "CNY";
/// <summary>
/// 是否启用
/// </summary>
public bool IsEnabled { get; set; } = true;
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
/// <summary>
/// 更新时间
/// </summary>
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
}

View File

@@ -0,0 +1,41 @@
using System.ComponentModel.DataAnnotations;
namespace FateMaster.API.Models;
/// <summary>
/// 系统配置表
/// </summary>
public class SystemConfig
{
[Key]
public int Id { get; set; }
/// <summary>
/// 配置键
/// </summary>
[Required]
[MaxLength(100)]
public string ConfigKey { get; set; } = string.Empty;
/// <summary>
/// 配置值
/// </summary>
[Required]
public string ConfigValue { get; set; } = string.Empty;
/// <summary>
/// 描述
/// </summary>
[MaxLength(500)]
public string? Description { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
/// <summary>
/// 更新时间
/// </summary>
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
}

View File

@@ -0,0 +1,41 @@
using FateMaster.API.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Configure MySQL
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
// Configure CORS
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowFrontend", policy =>
{
policy.WithOrigins("http://localhost:3000", "http://localhost:3001")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseCors("AllowFrontend");
app.UseAuthorization();
app.MapControllers();
app.Run();

View File

@@ -0,0 +1,12 @@
{
"profiles": {
"FateMaster.API": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:63347;http://localhost:63348"
}
}
}

View File

@@ -0,0 +1,34 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Port=3306;Database=fatemaster;User=root;Password=your_password;"
},
"PaymentSettings": {
"Alipay": {
"AppId": "",
"PrivateKey": "",
"PublicKey": ""
},
"PayPal": {
"ClientId": "",
"ClientSecret": "",
"Mode": "sandbox"
},
"Stripe": {
"SecretKey": "",
"PublishableKey": ""
}
},
"AISettings": {
"Provider": "OpenAI",
"ApiKey": "",
"Model": "gpt-4",
"BaseUrl": "https://api.openai.com/v1"
}
}