comment system

This commit is contained in:
Tsanie Lily 2023-08-09 17:34:18 +08:00
parent ce94a401d3
commit 7a44b7fd9c
17 changed files with 1152 additions and 65 deletions

View File

@ -297,15 +297,16 @@ public abstract partial class BaseController : ControllerBase
{ {
return data; return data;
} }
using var bitmap = SKBitmap.Decode(data); using var image = SKImage.FromEncodedData(data);
if (maxWidth >= bitmap.Width) if (maxWidth >= image.Width)
{ {
using var enc = bitmap.Encode(SKEncodedImageFormat.Jpeg, 80); using var enc = image.Encode(SKEncodedImageFormat.Jpeg, 80);
return enc.ToArray(); return enc.ToArray();
} }
var height = maxWidth * bitmap.Height / bitmap.Width; var height = maxWidth * image.Height / image.Width;
using var image = bitmap.Resize(new SKImageInfo(maxWidth, height), quality); using var bitmap = SKBitmap.FromImage(image);
using var encode = image.Encode(SKEncodedImageFormat.Jpeg, 80); using var resized = bitmap.Resize(new SKImageInfo(maxWidth, height), quality);
using var encode = resized.Encode(SKEncodedImageFormat.Jpeg, 80);
return encode.ToArray(); return encode.ToArray();
} }

View File

@ -47,7 +47,7 @@ partial class BaseController
protected UserItem? QueryUserItemForAuthentication(string id) protected UserItem? QueryUserItemForAuthentication(string id)
{ {
return database.Users return database.Users
.FromSql($"SELECT \"uid\",\"id\",\"password\",0 AS \"level\",0 AS \"regdate\",\"activedate\",\"\" AS \"name\",NULL AS \"email\",NULL AS \"mobile\",NULL as \"avatar\" FROM \"users\" WHERE \"id\" = {id} LIMIT 1") .FromSql($"SELECT \"uid\",\"id\",\"password\",\"level\",\"regdate\",\"activedate\",\"name\",\"email\",\"mobile\",NULL as \"avatar\" FROM \"users\" WHERE \"id\" = {id} LIMIT 1")
.SingleOrDefault(); .SingleOrDefault();
} }

View File

@ -0,0 +1,265 @@
using Blahblah.FlowerStory.Server.Data;
using Blahblah.FlowerStory.Server.Data.Model;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
namespace Blahblah.FlowerStory.Server.Controller;
/// <summary>
/// 评论相关 API 服务
/// </summary>
[ApiController]
[Produces("application/json")]
[Route("api/comment")]
public class CommentApiController : BaseController
{
static readonly int?[] specialTypes = { 1, 2, 3 };
/// <inheritdoc/>
public CommentApiController(FlowerDatabase database, ILogger<BaseController>? logger = null) : base(database, logger)
{
}
/// <summary>
/// 获取符合条件的评论
/// </summary>
/// <remarks>
/// 请求示例:
///
/// GET /api/comment/query
/// Authorization: authorization id
///
/// 参数:
///
/// rid: int
/// t: int?
/// from: long?
/// to: long?
/// size: int?
///
/// </remarks>
/// <param name="recordId">事件唯一 id</param>
/// <param name="typeId">评论类型 id</param>
/// <param name="from">起始日期</param>
/// <param name="to">结束日期</param>
/// <param name="pageSize">分页大小</param>
/// <returns>会话有效则返回符合条件的评论集</returns>
/// <response code="200">返回符合条件的评论集</response>
/// <response code="401">未找到登录会话或已过期</response>
/// <response code="403">用户已禁用</response>
/// <response code="404">未找到关联用户</response>
[Route("query", Name = "queryComments")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesErrorResponseType(typeof(ErrorResponse))]
[HttpGet]
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
public ActionResult<CommentItem[]> GetComments(
[FromQuery(Name = "rid")][Required] int recordId,
[FromQuery(Name = "t")] int? typeId,
[FromQuery] long? from,
[FromQuery] long? to,
[FromQuery(Name = "size")] int? pageSize = 20)
{
var (result, user) = CheckPermission();
if (result != null)
{
return result;
}
if (user == null)
{
return NotFound();
}
SaveDatabase();
var comments = database.Comments.Where(c => c.RecordId == recordId);
if (typeId == null)
{
comments = comments.Where(c => !specialTypes.Contains(c.CommentCategoryId));
}
else
{
comments = comments.Where(c => c.CommentCategoryId == typeId);
}
if (from != null)
{
comments = comments.Where(r => r.DateUnixTime >= from);
}
if (to != null)
{
comments = comments.Where(r => r.DateUnixTime <= to);
}
comments = comments.Select(r => new CommentItem
{
Id = r.Id,
RecordId = r.RecordId,
CommentCategoryId = r.CommentCategoryId,
OwnerId = r.OwnerId,
ByUserId = r.ByUserId,
DateUnixTime = r.DateUnixTime,
Latitude = r.Latitude,
Longitude = r.Longitude,
Text = r.Text,
ByUserName = string.IsNullOrEmpty(r.ByUserName) && r.ByUserId != null ? database.Users.Single(u => u.Id == r.ByUserId).Name : r.ByUserName
});
var size = pageSize ?? 20;
comments = comments.OrderByDescending(r => r.DateUnixTime).Take(size);
var array = comments.ToArray();
foreach (var r in array)
{
if (string.IsNullOrEmpty(r.ByUserName))
{
r.ByUserName = user.Name;
}
}
return Ok(array);
}
/// <summary>
/// 移除用户的评论
/// </summary>
/// <remarks>
/// 请求示例:
///
/// DELETE /api/comment/remove
/// Authorization: authorization id
///
/// 参数:
///
/// id: int
///
/// </remarks>
/// <param name="id">评论唯一 id</param>
/// <returns>会话有效则返回操作影响的数据库行数</returns>
/// <response code="200">返回操作影响的数据库行数</response>
/// <response code="401">未找到登录会话或已过期</response>
/// <response code="403">用户已禁用</response>
/// <response code="404">未找到关联用户</response>
[Route("remove", Name = "removeComment")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesErrorResponseType(typeof(ErrorResponse))]
[HttpDelete]
public ActionResult<int> RemoveComment([FromQuery][Required] int id)
{
var (result, user) = CheckPermission();
if (result != null)
{
return result;
}
if (user == null)
{
return NotFound();
}
var records = database.Records.Where(r => database.Comments.Any(c => c.OwnerId == user.Id && c.Id == id && c.RecordId == r.Id)).ToList();
var count = database.Comments.Where(c => c.OwnerId == user.Id && c.Id == id).ExecuteDelete();
if (count > 0)
{
foreach (var r in records)
{
r.LikeCount = database.Comments.Count(c => c.CommentCategoryId == 1 && c.RecordId == r.Id);
r.FavoriteCount = database.Comments.Count(c => c.CommentCategoryId == 2 && c.RecordId == r.Id);
r.CommentCount = database.Comments.Count(c => !specialTypes.Contains(c.CommentCategoryId) && c.RecordId == r.Id);
}
}
SaveDatabase();
return Ok(count);
}
/// <summary>
/// 用户添加评论
/// </summary>
/// <remarks>
/// 请求示例:
///
/// POST /api/comment/add
/// Authorization: authorization id
///
/// 参数:
///
/// recordId: 1
/// typeId: 0
/// text: "这朵花好漂亮"
/// byUser: "来宾"
/// lon: 29.5462794
/// lat: 106.5380034
///
/// </remarks>
/// <param name="comment">评论参数</param>
/// <returns>添加成功则返回已添加的评论对象</returns>
/// <response code="200">返回已添加的评论对象</response>
/// <response code="401">未找到登录会话或已过期</response>
/// <response code="403">用户已禁用</response>
/// <response code="404">未找到关联用户</response>
[Route("add", Name = "addComment")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesErrorResponseType(typeof(ErrorResponse))]
[HttpPost]
public ActionResult<CommentItem> AddEvent([FromBody] CommentParameter comment)
{
var (result, user) = CheckPermission();
if (result != null)
{
return result;
}
if (user == null)
{
return NotFound();
}
var now = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var item = new CommentItem
{
RecordId = comment.RecordId,
OwnerId = user.Id,
CommentCategoryId = comment.TypeId,
DateUnixTime = now,
ByUserId = comment.ByUser == null ? user.Id : null,
ByUserName = comment.ByUser,
Text = comment.Text,
Latitude = comment.Latitude,
Longitude = comment.Longitude
};
database.Comments.Add(item);
var record = database.Records.SingleOrDefault(r => r.Id == comment.RecordId);
if (record != null)
{
switch (comment.TypeId)
{
case 1:
record.LikeCount = (record.LikeCount ?? 0) + 1;
break;
case 2:
record.FavoriteCount = (record.FavoriteCount ?? 0) + 1;
break;
case not 3:
record.CommentCount = (record.CommentCount ?? 0) + 1;
break;
}
}
SaveDatabase();
return Ok(item);
}
}

View File

@ -0,0 +1,44 @@
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
namespace Blahblah.FlowerStory.Server.Controller;
/// <summary>
/// 评论参数
/// </summary>
public record CommentParameter
{
/// <summary>
/// 事件唯一 id
/// </summary>
[Required]
public required int RecordId { get; init; }
/// <summary>
/// 内容
/// </summary>
[Required]
public required string Text { get; init; }
/// <summary>
/// 评论分类 id
/// </summary>
public int? TypeId { get; init; }
/// <summary>
/// 操作人姓名
/// </summary>
public string? ByUser { get; init; }
/// <summary>
/// 纬度
/// </summary>
[JsonPropertyName("lat")]
public double? Latitude { get; init; }
/// <summary>
/// 经度
/// </summary>
[JsonPropertyName("lon")]
public double? Longitude { get; set; }
}

View File

@ -20,7 +20,7 @@ public class EventApiController : BaseController
} }
/// <summary> /// <summary>
/// 获取用户相关所有符合条件的事件 /// 获取符合条件的公共事件
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// 请求示例: /// 请求示例:
@ -30,11 +30,13 @@ public class EventApiController : BaseController
/// ///
/// 参数: /// 参数:
/// ///
/// fid: int?
/// eid: int? /// eid: int?
/// key: string? /// key: string?
/// from: long? /// from: long?
/// to: long? /// to: long?
/// p: bool? /// p: bool?
/// size: int?
/// ///
/// </remarks> /// </remarks>
/// <param name="flowerId">花草唯一 id</param> /// <param name="flowerId">花草唯一 id</param>
@ -43,8 +45,9 @@ public class EventApiController : BaseController
/// <param name="from">起始日期</param> /// <param name="from">起始日期</param>
/// <param name="to">结束日期</param> /// <param name="to">结束日期</param>
/// <param name="includePhoto">是否包含图片</param> /// <param name="includePhoto">是否包含图片</param>
/// <returns>会话有效则返回符合条件的花草集</returns> /// <param name="pageSize">分页大小</param>
/// <response code="200">返回符合条件的花草集</response> /// <returns>会话有效则返回符合条件的事件集</returns>
/// <response code="200">返回符合条件的事件集</response>
/// <response code="401">未找到登录会话或已过期</response> /// <response code="401">未找到登录会话或已过期</response>
/// <response code="403">用户已禁用</response> /// <response code="403">用户已禁用</response>
/// <response code="404">未找到关联用户</response> /// <response code="404">未找到关联用户</response>
@ -62,7 +65,8 @@ public class EventApiController : BaseController
[FromQuery] string? key, [FromQuery] string? key,
[FromQuery] long? from, [FromQuery] long? from,
[FromQuery] long? to, [FromQuery] long? to,
[FromQuery(Name = "p")] bool? includePhoto) [FromQuery(Name = "p")] bool? includePhoto,
[FromQuery(Name = "size")] int? pageSize = 20)
{ {
var (result, user) = CheckPermission(); var (result, user) = CheckPermission();
if (result != null) if (result != null)
@ -76,21 +80,8 @@ public class EventApiController : BaseController
SaveDatabase(); SaveDatabase();
var records = database.Records.Where(r => r.OwnerId == user.Id).Select(r => new RecordItem var records = database.Records.Where(r => r.IsHidden != true);
{
Id = r.Id,
OwnerId = r.OwnerId,
ByUserId = r.ByUserId,
DateUnixTime = r.DateUnixTime,
EventId = r.EventId,
FlowerId = r.FlowerId,
IsHidden = r.IsHidden,
Latitude = r.Latitude,
Longitude = r.Longitude,
Title = r.Title,
Memo = r.Memo,
ByUserName = string.IsNullOrEmpty(r.ByUserName) && r.ByUserId != null ? database.Users.Single(u => u.Id == r.ByUserId).Name : r.ByUserName
});
if (flowerId != null) if (flowerId != null)
{ {
records = records.Where(r => r.FlowerId == flowerId); records = records.Where(r => r.FlowerId == flowerId);
@ -116,23 +107,48 @@ public class EventApiController : BaseController
if (includePhoto == true) if (includePhoto == true)
{ {
foreach (var r in records) records = records.Include(r => r.Photos);
{
r.Photos = database.Photos.Where(p => p.RecordId == r.Id).ToList();
foreach (var photo in r.Photos)
{
photo.Url = $"photo/flower/{r.FlowerId}/{photo.Path}?thumb=1";
}
}
} }
records = records.Select(r => new RecordItem
{
Id = r.Id,
OwnerId = r.OwnerId,
ByUserId = r.ByUserId,
DateUnixTime = r.DateUnixTime,
EventId = r.EventId,
FlowerId = r.FlowerId,
IsHidden = r.IsHidden,
Latitude = r.Latitude,
Longitude = r.Longitude,
Title = r.Title,
Memo = r.Memo,
ByUserName = string.IsNullOrEmpty(r.ByUserName) && r.ByUserId != null ? database.Users.Single(u => u.Id == r.ByUserId).Name : r.ByUserName,
Photos = r.Photos,
LikeCount = r.LikeCount,
FavoriteCount = r.FavoriteCount,
CommentCount = r.CommentCount
});
var size = pageSize ?? 20;
records = records.OrderByDescending(r => r.DateUnixTime).Take(size);
var array = records.ToArray(); var array = records.ToArray();
foreach (var r in array) foreach (var r in array)
{ {
if (string.IsNullOrEmpty(r.ByUserName)) if (string.IsNullOrEmpty(r.ByUserName))
{ {
r.ByUserName = user.Name; r.ByUserName = user.Name;
} }
if (r.Photos == null)
{
continue;
}
foreach (var photo in r.Photos)
{
photo.Url = $"photo/flower/{r.FlowerId}/{photo.Path}?thumb=1";
}
} }
return Ok(array); return Ok(array);
@ -306,7 +322,7 @@ public class EventApiController : BaseController
/// 参数: /// 参数:
/// ///
/// flowerId: 1 /// flowerId: 1
/// eventId": 4 // 浇水 /// eventId: 4 // 浇水
/// byUser: "朋友" /// byUser: "朋友"
/// memo: "快干死了" /// memo: "快干死了"
/// lon: 29.5462794 /// lon: 29.5462794

View File

@ -168,7 +168,7 @@ public class FlowerApiController : BaseController
bool? includePhoto, double? latitude, double? longitude, int? distance, bool? includePhoto, double? latitude, double? longitude, int? distance,
int? page = 0, int? pageSize = 20) int? page = 0, int? pageSize = 20)
{ {
IEnumerable<FlowerItem> flowers; IQueryable<FlowerItem> flowers;
if (userId != null) if (userId != null)
{ {
flowers = database.Flowers.Where(f => f.OwnerId == userId); flowers = database.Flowers.Where(f => f.OwnerId == userId);
@ -210,9 +210,17 @@ public class FlowerApiController : BaseController
flowers = flowers.Where(f => database.Records.Any(r => r.FlowerId == f.Id && r.EventId == eventId)); flowers = flowers.Where(f => database.Records.Any(r => r.FlowerId == f.Id && r.EventId == eventId));
} }
if (includePhoto == true)
{
flowers = flowers.Include(f => f.Photos);
}
flowers = flowers.OrderByDescending(f => f.DateBuyUnixTime);
IEnumerable<FlowerItem> items;
if (distance != null && latitude != null && longitude != null) if (distance != null && latitude != null && longitude != null)
{ {
flowers = flowers.Where(f => f.Latitude != null && f.Longitude != null) items = flowers.Where(f => f.Latitude != null && f.Longitude != null)
.AsEnumerable() .AsEnumerable()
.Where(f => .Where(f =>
{ {
@ -221,18 +229,26 @@ public class FlowerApiController : BaseController
return d <= distance; return d <= distance;
}); });
} }
else
{
items = flowers;
}
int count = flowers.Count(); int count = items.Count();
var size = pageSize ?? 20; var size = pageSize ?? 20;
var p = page ?? 0; var p = page ?? 0;
flowers = flowers.OrderByDescending(f => f.DateBuyUnixTime).Skip(p * size).Take(size); items = items.Skip(p * size).Take(size);
if (includePhoto == true) if (includePhoto == true)
{ {
foreach (var f in flowers) foreach (var f in items)
{ {
f.Photos = database.Photos.Where(p => p.FlowerId == f.Id && p.RecordId == null).ToList(); //f.Photos = database.Photos.Where(p => p.FlowerId == f.Id && p.RecordId == null).ToList();
if (f.Photos == null)
{
continue;
}
foreach (var photo in f.Photos) foreach (var photo in f.Photos)
{ {
photo.Url = $"photo/flower/{f.Id}/{photo.Path}?thumb=1"; photo.Url = $"photo/flower/{f.Id}/{photo.Path}?thumb=1";
@ -242,7 +258,7 @@ public class FlowerApiController : BaseController
return new() return new()
{ {
Flowers = flowers.ToArray(), Flowers = items.ToArray(),
Count = count Count = count
}; };
} }

View File

@ -41,4 +41,9 @@ public class FlowerDatabase : DbContext
/// 照片集 /// 照片集
/// </summary> /// </summary>
public DbSet<PhotoItem> Photos { get; set; } public DbSet<PhotoItem> Photos { get; set; }
/// <summary>
/// 评论集
/// </summary>
public DbSet<CommentItem> Comments { get; set; }
} }

View File

@ -0,0 +1,99 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
namespace Blahblah.FlowerStory.Server.Data.Model;
/// <summary>
/// 评论对象
/// </summary>
[Table("comments")]
public class CommentItem
{
/// <summary>
/// 自增 id主键
/// </summary>
[Column("cid")]
[Key]
[Required]
public int Id { get; set; }
/// <summary>
/// 关联人 id
/// </summary>
[Column("uid")]
[ForeignKey(nameof(Owner))]
[Required]
public int OwnerId { get; set; }
/// <summary>
/// 关联人
/// </summary>
[JsonIgnore]
public UserItem? Owner { get; set; }
/// <summary>
/// 操作时间
/// </summary>
[Column("date")]
[Required]
[JsonPropertyName("date")]
public long DateUnixTime { get; set; }
/// <summary>
/// 关联的事件 id
/// </summary>
[Column("rid")]
[Required]
[ForeignKey(nameof(Record))]
public int RecordId { get; set; }
/// <summary>
/// 关联的事件
/// </summary>
[JsonIgnore]
public RecordItem Record { get; set; } = null!;
/// <summary>
/// 评论类别1-点赞2-收藏3-标签default-评论
/// </summary>
[Column("category")]
public int? CommentCategoryId { get; set; }
/// <summary>
/// 操作人 uid
/// </summary>
[Column("byuid")]
public int? ByUserId { get; set; }
/// <summary>
/// 操作人名称
/// </summary>
[Column("byname")]
public string? ByUserName { get; set; }
/// <summary>
/// 备注
/// </summary>
[Column("text")]
public string? Text { get; set; }
/// <summary>
/// 操作时间
/// </summary>
[NotMapped]
[JsonIgnore]
public DateTimeOffset Date => DateTimeOffset.FromUnixTimeMilliseconds(DateUnixTime);
/// <summary>
/// 纬度
/// </summary>
[Column("latitude")]
public double? Latitude { get; set; }
/// <summary>
/// 经度
/// </summary>
[Column("longitude")]
public double? Longitude { get; set; }
}

View File

@ -24,7 +24,7 @@ public class FlowerItem : ILocation
[Column("uid")] [Column("uid")]
[ForeignKey(nameof(Owner))] [ForeignKey(nameof(Owner))]
[Required] [Required]
public required int OwnerId { get; set; } public int OwnerId { get; set; }
/// <summary> /// <summary>
/// 所有人 /// 所有人
@ -37,14 +37,14 @@ public class FlowerItem : ILocation
/// </summary> /// </summary>
[Column("categoryid")] [Column("categoryid")]
[Required] [Required]
public required int CategoryId { get; set; } public int CategoryId { get; set; }
/// <summary> /// <summary>
/// 花草名称 /// 花草名称
/// </summary> /// </summary>
[Column("name")] [Column("name")]
[Required] [Required]
public required string Name { get; set; } public string Name { get; set; } = null!;
/// <summary> /// <summary>
/// 购买时间 /// 购买时间
@ -52,7 +52,7 @@ public class FlowerItem : ILocation
[Column("datebuy")] [Column("datebuy")]
[Required] [Required]
[JsonPropertyName("dateBuy")] [JsonPropertyName("dateBuy")]
public required long DateBuyUnixTime { get; set; } public long DateBuyUnixTime { get; set; }
/// <summary> /// <summary>
/// 购买花费 /// 购买花费

View File

@ -24,7 +24,7 @@ public class PhotoItem
[Column("uid")] [Column("uid")]
[ForeignKey(nameof(Owner))] [ForeignKey(nameof(Owner))]
[Required] [Required]
public required int OwnerId { get; set; } public int OwnerId { get; set; }
/// <summary> /// <summary>
/// 关联人 /// 关联人
@ -63,21 +63,21 @@ public class PhotoItem
/// </summary> /// </summary>
[Column("filetype")] [Column("filetype")]
[Required] [Required]
public required string FileType { get; set; } public string FileType { get; set; } = null!;
/// <summary> /// <summary>
/// 文件名 /// 文件名
/// </summary> /// </summary>
[Column("filename")] [Column("filename")]
[Required] [Required]
public required string FileName { get; set; } public string FileName { get; set; } = null!;
/// <summary> /// <summary>
/// 文件路径 /// 文件路径
/// </summary> /// </summary>
[Column("path")] [Column("path")]
[Required] [Required]
public required string Path { get; set; } public string Path { get; set; } = null!;
/// <summary> /// <summary>
/// 上传时间 /// 上传时间
@ -85,7 +85,7 @@ public class PhotoItem
[Column("dateupload")] [Column("dateupload")]
[Required] [Required]
[JsonPropertyName("dateUpload")] [JsonPropertyName("dateUpload")]
public required long DateUploadUnixTime { get; set; } public long DateUploadUnixTime { get; set; }
/// <summary> /// <summary>
/// 上传时间 /// 上传时间

View File

@ -24,7 +24,7 @@ public class RecordItem : ILocation
[Column("uid")] [Column("uid")]
[ForeignKey(nameof(Owner))] [ForeignKey(nameof(Owner))]
[Required] [Required]
public required int OwnerId { get; set; } public int OwnerId { get; set; }
/// <summary> /// <summary>
/// 关联人 /// 关联人
@ -38,7 +38,7 @@ public class RecordItem : ILocation
[Column("fid")] [Column("fid")]
[ForeignKey(nameof(Flower))] [ForeignKey(nameof(Flower))]
[Required] [Required]
public required int FlowerId { get; set; } public int FlowerId { get; set; }
/// <summary> /// <summary>
/// 关联花草 /// 关联花草
@ -51,7 +51,7 @@ public class RecordItem : ILocation
/// </summary> /// </summary>
[Column("eid")] [Column("eid")]
[Required] [Required]
public required int EventId { get; set; } public int EventId { get; set; }
/// <summary> /// <summary>
/// 操作时间 /// 操作时间
@ -59,7 +59,7 @@ public class RecordItem : ILocation
[Column("date")] [Column("date")]
[Required] [Required]
[JsonPropertyName("date")] [JsonPropertyName("date")]
public required long DateUnixTime { get; set; } public long DateUnixTime { get; set; }
/// <summary> /// <summary>
/// 是否为私有记录 /// 是否为私有记录
@ -114,4 +114,22 @@ public class RecordItem : ILocation
/// </summary> /// </summary>
[Column("longitude")] [Column("longitude")]
public double? Longitude { get; set; } public double? Longitude { get; set; }
/// <summary>
/// 点赞数
/// </summary>
[Column("likes")]
public int? LikeCount { get; set; }
/// <summary>
/// 收藏数
/// </summary>
[Column("favs")]
public int? FavoriteCount { get; set; }
/// <summary>
/// 评论数
/// </summary>
[Column("comments")]
public int? CommentCount { get; set; }
} }

View File

@ -16,14 +16,14 @@ public class TokenItem
[Column("tid")] [Column("tid")]
[Key] [Key]
[Required] [Required]
public required string Id { get; set; } public string Id { get; set; } = null!;
/// <summary> /// <summary>
/// 关联用户 uid /// 关联用户 uid
/// </summary> /// </summary>
[Column("uid")] [Column("uid")]
[Required] [Required]
public required int UserId { get; set; } public int UserId { get; set; }
/// <summary> /// <summary>
/// 登录时间 /// 登录时间
@ -31,7 +31,7 @@ public class TokenItem
[Column("logondate")] [Column("logondate")]
[Required] [Required]
[JsonPropertyName("logonDate")] [JsonPropertyName("logonDate")]
public required long LogonDateUnixTime { get; set; } public long LogonDateUnixTime { get; set; }
/// <summary> /// <summary>
/// 活动时间 /// 活动时间
@ -39,7 +39,7 @@ public class TokenItem
[Column("activedate")] [Column("activedate")]
[Required] [Required]
[JsonPropertyName("activeDate")] [JsonPropertyName("activeDate")]
public required long ActiveDateUnixTime { get; set; } public long ActiveDateUnixTime { get; set; }
/// <summary> /// <summary>
/// 过期时间 /// 过期时间
@ -47,14 +47,14 @@ public class TokenItem
[Column("expiredate")] [Column("expiredate")]
[Required] [Required]
[JsonPropertyName("expireDate")] [JsonPropertyName("expireDate")]
public required long ExpireDateUnixTime { get; set; } public long ExpireDateUnixTime { get; set; }
/// <summary> /// <summary>
/// 过期秒数 /// 过期秒数
/// </summary> /// </summary>
[Column("expiresecs")] [Column("expiresecs")]
[Required] [Required]
public required int ExpireSeconds { get; set; } public int ExpireSeconds { get; set; }
/// <summary> /// <summary>
/// 验证码 /// 验证码

View File

@ -23,7 +23,7 @@ public class UserItem
/// </summary> /// </summary>
[Column("id")] [Column("id")]
[Required] [Required]
public required string UserId { get; set; } public string UserId { get; set; } = null!;
/// <summary> /// <summary>
/// 密码,值为 SHA256(password+id+salt) /// 密码,值为 SHA256(password+id+salt)
@ -41,7 +41,7 @@ public class UserItem
/// </summary> /// </summary>
[Column("level")] [Column("level")]
[Required] [Required]
public required int Level { get; set; } public int Level { get; set; }
/// <summary> /// <summary>
/// 注册时间 /// 注册时间
@ -49,7 +49,7 @@ public class UserItem
[Column("regdate")] [Column("regdate")]
[Required] [Required]
[JsonPropertyName("registerDate")] [JsonPropertyName("registerDate")]
public required long RegisterDateUnixTime { get; set; } public long RegisterDateUnixTime { get; set; }
/// <summary> /// <summary>
/// 最后变动时间 /// 最后变动时间

View File

@ -0,0 +1,445 @@
// <auto-generated />
using System;
using Blahblah.FlowerStory.Server.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Blahblah.FlowerStory.Server.Migrations
{
[DbContext(typeof(FlowerDatabase))]
[Migration("20230809083422_Add-Comments")]
partial class AddComments
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.9");
modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.CommentItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("cid");
b.Property<int?>("ByUserId")
.HasColumnType("INTEGER")
.HasColumnName("byuid");
b.Property<string>("ByUserName")
.HasColumnType("TEXT")
.HasColumnName("byname");
b.Property<int?>("CommentCategoryId")
.HasColumnType("INTEGER")
.HasColumnName("category");
b.Property<long>("DateUnixTime")
.HasColumnType("INTEGER")
.HasColumnName("date")
.HasAnnotation("Relational:JsonPropertyName", "date");
b.Property<double?>("Latitude")
.HasColumnType("REAL")
.HasColumnName("latitude");
b.Property<double?>("Longitude")
.HasColumnType("REAL")
.HasColumnName("longitude");
b.Property<int>("OwnerId")
.HasColumnType("INTEGER")
.HasColumnName("uid");
b.Property<int>("RecordId")
.HasColumnType("INTEGER")
.HasColumnName("rid");
b.Property<string>("Text")
.HasColumnType("TEXT")
.HasColumnName("text");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.HasIndex("RecordId");
b.ToTable("comments");
});
modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.FlowerItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("fid");
b.Property<int>("CategoryId")
.HasColumnType("INTEGER")
.HasColumnName("categoryid");
b.Property<decimal?>("Cost")
.HasColumnType("real")
.HasColumnName("cost");
b.Property<long>("DateBuyUnixTime")
.HasColumnType("INTEGER")
.HasColumnName("datebuy")
.HasAnnotation("Relational:JsonPropertyName", "dateBuy");
b.Property<double?>("Latitude")
.HasColumnType("REAL")
.HasColumnName("latitude");
b.Property<double?>("Longitude")
.HasColumnType("REAL")
.HasColumnName("longitude");
b.Property<string>("Memo")
.HasColumnType("TEXT")
.HasColumnName("memo");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("name");
b.Property<int>("OwnerId")
.HasColumnType("INTEGER")
.HasColumnName("uid");
b.Property<string>("Purchase")
.HasColumnType("TEXT")
.HasColumnName("purchase");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.ToTable("flowers");
});
modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.PhotoItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("pid");
b.Property<long>("DateUploadUnixTime")
.HasColumnType("INTEGER")
.HasColumnName("dateupload")
.HasAnnotation("Relational:JsonPropertyName", "dateUpload");
b.Property<string>("FileName")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("filename");
b.Property<string>("FileType")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("filetype");
b.Property<int?>("FlowerId")
.HasColumnType("INTEGER")
.HasColumnName("fid");
b.Property<int?>("Height")
.HasColumnType("INTEGER")
.HasColumnName("height");
b.Property<int>("OwnerId")
.HasColumnType("INTEGER")
.HasColumnName("uid");
b.Property<string>("Path")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("path");
b.Property<int?>("RecordId")
.HasColumnType("INTEGER")
.HasColumnName("rid");
b.Property<int?>("Width")
.HasColumnType("INTEGER")
.HasColumnName("width");
b.HasKey("Id");
b.HasIndex("FlowerId");
b.HasIndex("OwnerId");
b.HasIndex("RecordId");
b.ToTable("photos");
});
modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.RecordItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("rid");
b.Property<int?>("ByUserId")
.HasColumnType("INTEGER")
.HasColumnName("byuid");
b.Property<string>("ByUserName")
.HasColumnType("TEXT")
.HasColumnName("byname");
b.Property<int?>("CommentCount")
.HasColumnType("INTEGER")
.HasColumnName("comments");
b.Property<long>("DateUnixTime")
.HasColumnType("INTEGER")
.HasColumnName("date")
.HasAnnotation("Relational:JsonPropertyName", "date");
b.Property<int>("EventId")
.HasColumnType("INTEGER")
.HasColumnName("eid");
b.Property<int?>("FavoriteCount")
.HasColumnType("INTEGER")
.HasColumnName("favs");
b.Property<int>("FlowerId")
.HasColumnType("INTEGER")
.HasColumnName("fid");
b.Property<bool?>("IsHidden")
.HasColumnType("INTEGER")
.HasColumnName("hidden");
b.Property<double?>("Latitude")
.HasColumnType("REAL")
.HasColumnName("latitude");
b.Property<int?>("LikeCount")
.HasColumnType("INTEGER")
.HasColumnName("likes");
b.Property<double?>("Longitude")
.HasColumnType("REAL")
.HasColumnName("longitude");
b.Property<string>("Memo")
.HasColumnType("TEXT")
.HasColumnName("memo");
b.Property<int>("OwnerId")
.HasColumnType("INTEGER")
.HasColumnName("uid");
b.Property<string>("Title")
.HasColumnType("TEXT")
.HasColumnName("title");
b.HasKey("Id");
b.HasIndex("FlowerId");
b.HasIndex("OwnerId");
b.ToTable("records");
});
modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.TokenItem", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT")
.HasColumnName("tid");
b.Property<long>("ActiveDateUnixTime")
.HasColumnType("INTEGER")
.HasColumnName("activedate")
.HasAnnotation("Relational:JsonPropertyName", "activeDate");
b.Property<string>("ClientAgent")
.HasColumnType("TEXT")
.HasColumnName("clientagent");
b.Property<string>("ClientApp")
.HasColumnType("TEXT")
.HasColumnName("clientapp");
b.Property<string>("DeviceId")
.HasColumnType("TEXT")
.HasColumnName("deviceid");
b.Property<long>("ExpireDateUnixTime")
.HasColumnType("INTEGER")
.HasColumnName("expiredate")
.HasAnnotation("Relational:JsonPropertyName", "expireDate");
b.Property<int>("ExpireSeconds")
.HasColumnType("INTEGER")
.HasColumnName("expiresecs");
b.Property<long>("LogonDateUnixTime")
.HasColumnType("INTEGER")
.HasColumnName("logondate")
.HasAnnotation("Relational:JsonPropertyName", "logonDate");
b.Property<int>("UserId")
.HasColumnType("INTEGER")
.HasColumnName("uid");
b.Property<string>("VerifyCode")
.HasColumnType("TEXT")
.HasColumnName("verifycode");
b.HasKey("Id");
b.ToTable("tokens");
});
modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.UserItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("uid");
b.Property<long?>("ActiveDateUnixTime")
.HasColumnType("INTEGER")
.HasColumnName("activedate");
b.Property<byte[]>("Avatar")
.HasColumnType("BLOB")
.HasColumnName("avatar");
b.Property<string>("Email")
.HasColumnType("TEXT")
.HasColumnName("email");
b.Property<int>("Level")
.HasColumnType("INTEGER")
.HasColumnName("level");
b.Property<string>("Mobile")
.HasColumnType("TEXT")
.HasColumnName("mobile");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("name");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("password");
b.Property<long>("RegisterDateUnixTime")
.HasColumnType("INTEGER")
.HasColumnName("regdate")
.HasAnnotation("Relational:JsonPropertyName", "registerDate");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("id");
b.HasKey("Id");
b.ToTable("users");
});
modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.CommentItem", b =>
{
b.HasOne("Blahblah.FlowerStory.Server.Data.Model.UserItem", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Blahblah.FlowerStory.Server.Data.Model.RecordItem", "Record")
.WithMany()
.HasForeignKey("RecordId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Owner");
b.Navigation("Record");
});
modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.FlowerItem", b =>
{
b.HasOne("Blahblah.FlowerStory.Server.Data.Model.UserItem", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Owner");
});
modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.PhotoItem", b =>
{
b.HasOne("Blahblah.FlowerStory.Server.Data.Model.FlowerItem", "Flower")
.WithMany("Photos")
.HasForeignKey("FlowerId");
b.HasOne("Blahblah.FlowerStory.Server.Data.Model.UserItem", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Blahblah.FlowerStory.Server.Data.Model.RecordItem", "Record")
.WithMany("Photos")
.HasForeignKey("RecordId");
b.Navigation("Flower");
b.Navigation("Owner");
b.Navigation("Record");
});
modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.RecordItem", b =>
{
b.HasOne("Blahblah.FlowerStory.Server.Data.Model.FlowerItem", "Flower")
.WithMany()
.HasForeignKey("FlowerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Blahblah.FlowerStory.Server.Data.Model.UserItem", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Flower");
b.Navigation("Owner");
});
modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.FlowerItem", b =>
{
b.Navigation("Photos");
});
modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.RecordItem", b =>
{
b.Navigation("Photos");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,94 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Blahblah.FlowerStory.Server.Migrations
{
/// <inheritdoc />
public partial class AddComments : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "comments",
table: "records",
type: "INTEGER",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "favs",
table: "records",
type: "INTEGER",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "likes",
table: "records",
type: "INTEGER",
nullable: true);
migrationBuilder.CreateTable(
name: "comments",
columns: table => new
{
cid = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
uid = table.Column<int>(type: "INTEGER", nullable: false),
date = table.Column<long>(type: "INTEGER", nullable: false),
rid = table.Column<int>(type: "INTEGER", nullable: false),
category = table.Column<int>(type: "INTEGER", nullable: true),
byuid = table.Column<int>(type: "INTEGER", nullable: true),
byname = table.Column<string>(type: "TEXT", nullable: true),
text = table.Column<string>(type: "TEXT", nullable: true),
latitude = table.Column<double>(type: "REAL", nullable: true),
longitude = table.Column<double>(type: "REAL", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_comments", x => x.cid);
table.ForeignKey(
name: "FK_comments_records_rid",
column: x => x.rid,
principalTable: "records",
principalColumn: "rid",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_comments_users_uid",
column: x => x.uid,
principalTable: "users",
principalColumn: "uid",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_comments_rid",
table: "comments",
column: "rid");
migrationBuilder.CreateIndex(
name: "IX_comments_uid",
table: "comments",
column: "uid");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "comments");
migrationBuilder.DropColumn(
name: "comments",
table: "records");
migrationBuilder.DropColumn(
name: "favs",
table: "records");
migrationBuilder.DropColumn(
name: "likes",
table: "records");
}
}
}

View File

@ -17,6 +17,59 @@ namespace Blahblah.FlowerStory.Server.Migrations
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.9"); modelBuilder.HasAnnotation("ProductVersion", "7.0.9");
modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.CommentItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("cid");
b.Property<int?>("ByUserId")
.HasColumnType("INTEGER")
.HasColumnName("byuid");
b.Property<string>("ByUserName")
.HasColumnType("TEXT")
.HasColumnName("byname");
b.Property<int?>("CommentCategoryId")
.HasColumnType("INTEGER")
.HasColumnName("category");
b.Property<long>("DateUnixTime")
.HasColumnType("INTEGER")
.HasColumnName("date")
.HasAnnotation("Relational:JsonPropertyName", "date");
b.Property<double?>("Latitude")
.HasColumnType("REAL")
.HasColumnName("latitude");
b.Property<double?>("Longitude")
.HasColumnType("REAL")
.HasColumnName("longitude");
b.Property<int>("OwnerId")
.HasColumnType("INTEGER")
.HasColumnName("uid");
b.Property<int>("RecordId")
.HasColumnType("INTEGER")
.HasColumnName("rid");
b.Property<string>("Text")
.HasColumnType("TEXT")
.HasColumnName("text");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.HasIndex("RecordId");
b.ToTable("comments");
});
modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.FlowerItem", b => modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.FlowerItem", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
@ -142,6 +195,10 @@ namespace Blahblah.FlowerStory.Server.Migrations
.HasColumnType("TEXT") .HasColumnType("TEXT")
.HasColumnName("byname"); .HasColumnName("byname");
b.Property<int?>("CommentCount")
.HasColumnType("INTEGER")
.HasColumnName("comments");
b.Property<long>("DateUnixTime") b.Property<long>("DateUnixTime")
.HasColumnType("INTEGER") .HasColumnType("INTEGER")
.HasColumnName("date") .HasColumnName("date")
@ -151,6 +208,10 @@ namespace Blahblah.FlowerStory.Server.Migrations
.HasColumnType("INTEGER") .HasColumnType("INTEGER")
.HasColumnName("eid"); .HasColumnName("eid");
b.Property<int?>("FavoriteCount")
.HasColumnType("INTEGER")
.HasColumnName("favs");
b.Property<int>("FlowerId") b.Property<int>("FlowerId")
.HasColumnType("INTEGER") .HasColumnType("INTEGER")
.HasColumnName("fid"); .HasColumnName("fid");
@ -163,6 +224,10 @@ namespace Blahblah.FlowerStory.Server.Migrations
.HasColumnType("REAL") .HasColumnType("REAL")
.HasColumnName("latitude"); .HasColumnName("latitude");
b.Property<int?>("LikeCount")
.HasColumnType("INTEGER")
.HasColumnName("likes");
b.Property<double?>("Longitude") b.Property<double?>("Longitude")
.HasColumnType("REAL") .HasColumnType("REAL")
.HasColumnName("longitude"); .HasColumnName("longitude");
@ -290,6 +355,25 @@ namespace Blahblah.FlowerStory.Server.Migrations
b.ToTable("users"); b.ToTable("users");
}); });
modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.CommentItem", b =>
{
b.HasOne("Blahblah.FlowerStory.Server.Data.Model.UserItem", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Blahblah.FlowerStory.Server.Data.Model.RecordItem", "Record")
.WithMany()
.HasForeignKey("RecordId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Owner");
b.Navigation("Record");
});
modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.FlowerItem", b => modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.FlowerItem", b =>
{ {
b.HasOne("Blahblah.FlowerStory.Server.Data.Model.UserItem", "Owner") b.HasOne("Blahblah.FlowerStory.Server.Data.Model.UserItem", "Owner")

View File

@ -11,7 +11,7 @@ public class Program
/// <inheritdoc/> /// <inheritdoc/>
public const string ProjectName = "Flower Story"; public const string ProjectName = "Flower Story";
/// <inheritdoc/> /// <inheritdoc/>
public const string Version = "1.1.808"; public const string Version = "1.2.809";
/// <inheritdoc/> /// <inheritdoc/>
public static string DataPath => public static string DataPath =>
#if DEBUG #if DEBUG