comment system

This commit is contained in:
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;
}
using var bitmap = SKBitmap.Decode(data);
if (maxWidth >= bitmap.Width)
using var image = SKImage.FromEncodedData(data);
if (maxWidth >= image.Width)
{
using var enc = bitmap.Encode(SKEncodedImageFormat.Jpeg, 80);
using var enc = image.Encode(SKEncodedImageFormat.Jpeg, 80);
return enc.ToArray();
}
var height = maxWidth * bitmap.Height / bitmap.Width;
using var image = bitmap.Resize(new SKImageInfo(maxWidth, height), quality);
using var encode = image.Encode(SKEncodedImageFormat.Jpeg, 80);
var height = maxWidth * image.Height / image.Width;
using var bitmap = SKBitmap.FromImage(image);
using var resized = bitmap.Resize(new SKImageInfo(maxWidth, height), quality);
using var encode = resized.Encode(SKEncodedImageFormat.Jpeg, 80);
return encode.ToArray();
}

View File

@@ -47,7 +47,7 @@ partial class BaseController
protected UserItem? QueryUserItemForAuthentication(string id)
{
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();
}

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>
/// <remarks>
/// 请求示例:
@@ -30,11 +30,13 @@ public class EventApiController : BaseController
///
/// 参数:
///
/// fid: int?
/// eid: int?
/// key: string?
/// from: long?
/// to: long?
/// p: bool?
/// size: int?
///
/// </remarks>
/// <param name="flowerId">花草唯一 id</param>
@@ -43,8 +45,9 @@ public class EventApiController : BaseController
/// <param name="from">起始日期</param>
/// <param name="to">结束日期</param>
/// <param name="includePhoto">是否包含图片</param>
/// <returns>会话有效则返回符合条件的花草集</returns>
/// <response code="200">返回符合条件的花草集</response>
/// <param name="pageSize">分页大小</param>
/// <returns>会话有效则返回符合条件的事件集</returns>
/// <response code="200">返回符合条件的事件集</response>
/// <response code="401">未找到登录会话或已过期</response>
/// <response code="403">用户已禁用</response>
/// <response code="404">未找到关联用户</response>
@@ -62,7 +65,8 @@ public class EventApiController : BaseController
[FromQuery] string? key,
[FromQuery] long? from,
[FromQuery] long? to,
[FromQuery(Name = "p")] bool? includePhoto)
[FromQuery(Name = "p")] bool? includePhoto,
[FromQuery(Name = "size")] int? pageSize = 20)
{
var (result, user) = CheckPermission();
if (result != null)
@@ -76,21 +80,8 @@ public class EventApiController : BaseController
SaveDatabase();
var records = database.Records.Where(r => r.OwnerId == user.Id).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
});
var records = database.Records.Where(r => r.IsHidden != true);
if (flowerId != null)
{
records = records.Where(r => r.FlowerId == flowerId);
@@ -116,23 +107,48 @@ public class EventApiController : BaseController
if (includePhoto == true)
{
foreach (var r in records)
{
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.Include(r => r.Photos);
}
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();
foreach (var r in array)
{
if (string.IsNullOrEmpty(r.ByUserName))
{
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);
@@ -306,7 +322,7 @@ public class EventApiController : BaseController
/// 参数:
///
/// flowerId: 1
/// eventId": 4 // 浇水
/// eventId: 4 // 浇水
/// byUser: "朋友"
/// memo: "快干死了"
/// lon: 29.5462794

View File

@@ -168,7 +168,7 @@ public class FlowerApiController : BaseController
bool? includePhoto, double? latitude, double? longitude, int? distance,
int? page = 0, int? pageSize = 20)
{
IEnumerable<FlowerItem> flowers;
IQueryable<FlowerItem> flowers;
if (userId != null)
{
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));
}
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)
{
flowers = flowers.Where(f => f.Latitude != null && f.Longitude != null)
items = flowers.Where(f => f.Latitude != null && f.Longitude != null)
.AsEnumerable()
.Where(f =>
{
@@ -221,18 +229,26 @@ public class FlowerApiController : BaseController
return d <= distance;
});
}
else
{
items = flowers;
}
int count = flowers.Count();
int count = items.Count();
var size = pageSize ?? 20;
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)
{
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)
{
photo.Url = $"photo/flower/{f.Id}/{photo.Path}?thumb=1";
@@ -242,7 +258,7 @@ public class FlowerApiController : BaseController
return new()
{
Flowers = flowers.ToArray(),
Flowers = items.ToArray(),
Count = count
};
}