comment system
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
|
265
Server/Controller/CommentApiController.cs
Normal file
265
Server/Controller/CommentApiController.cs
Normal 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);
|
||||
}
|
||||
}
|
44
Server/Controller/CommentApiController.structs.cs
Normal file
44
Server/Controller/CommentApiController.structs.cs
Normal 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; }
|
||||
}
|
@@ -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
|
||||
|
@@ -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
|
||||
};
|
||||
}
|
||||
|
Reference in New Issue
Block a user