flower-story/Server/Controller/FlowerApiController.cs
2024-10-12 14:34:11 +08:00

930 lines
31 KiB
C#

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/flower")]
public class FlowerApiController(FlowerDatabase database, ILogger<FlowerApiController>? logger = null) : BaseController<FlowerApiController>(database, logger)
{
/// <summary>
/// 获取用户名下所有符合条件的花草
/// </summary>
/// <remarks>
/// 请求示例:
///
/// GET /api/flower/query
/// Authorization: authorization id
///
/// 参数:
///
/// cid: int?
/// key: string?
/// from: long?
/// to: long?
/// cfrom: decimal?
/// cto: decimal?
/// photo: bool?
/// lon: double?
/// lat: double?
/// distance: int?
/// p: int?
/// size: int?
///
/// </remarks>
/// <param name="categoryId">类别 id</param>
/// <param name="key">查询关键字</param>
/// <param name="buyFrom">起始购买日期</param>
/// <param name="buyTo">结束购买日期</param>
/// <param name="costFrom">开销最小值</param>
/// <param name="costTo">开销最大值</param>
/// <param name="includePhoto">是否包含封面图片</param>
/// <param name="latitude">纬度</param>
/// <param name="longitude">经度</param>
/// <param name="distance">距离(米)</param>
/// <param name="page">页数</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 = "queryFlowers")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesErrorResponseType(typeof(ErrorResponse))]
[HttpGet]
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
public ActionResult<FlowerResult> GetFlowers(
[FromQuery(Name = "cid")] int? categoryId,
[FromQuery] string? key,
[FromQuery(Name = "from")] long? buyFrom,
[FromQuery(Name = "to")] long? buyTo,
[FromQuery(Name = "cfrom")] decimal? costFrom,
[FromQuery(Name = "cto")] decimal? costTo,
[FromQuery(Name = "photo")] bool? includePhoto,
[FromQuery(Name = "lat")] double? latitude,
[FromQuery(Name = "lon")] double? longitude,
[FromQuery] int? distance,
[FromQuery(Name = "p")] int? page = 0,
[FromQuery(Name = "size")] int? pageSize = 20)
{
var (result, user) = CheckPermission();
if (result != null)
{
return result;
}
if (user == null)
{
return NotFound();
}
SaveDatabase();
var flowers = GetFlowerResult(user.Id, null, categoryId, key, buyFrom, buyTo, costFrom, costTo, includePhoto, latitude, longitude, distance, page, pageSize);
return Ok(flowers);
}
/// <summary>
/// 获取最新、最近的花草
/// </summary>
/// <remarks>
/// 请求示例:
///
/// GET /api/flower/latest
///
/// 参数:
///
/// cid: int?
/// key: string?
/// from: long?
/// to: long?
/// cfrom: decimal?
/// cto: decimal?
/// photo: bool?
/// lon: double?
/// lat: double?
/// distance: int?
/// p: int?
/// size: int?
///
/// </remarks>
/// <param name="categoryId">类别 id</param>
/// <param name="key">查询关键字</param>
/// <param name="buyFrom">起始购买日期</param>
/// <param name="buyTo">结束购买日期</param>
/// <param name="costFrom">开销最小值</param>
/// <param name="costTo">开销最大值</param>
/// <param name="includePhoto">是否包含封面图片</param>
/// <param name="latitude">纬度</param>
/// <param name="longitude">经度</param>
/// <param name="distance">距离(米)</param>
/// <param name="page">页数</param>
/// <param name="pageSize">分页大小</param>
/// <returns>会话有效则返回符合条件的花草集</returns>
/// <response code="200">返回符合条件的花草集</response>
/// <response code="401">未找到登录会话或已过期</response>
/// <response code="403">用户已禁用</response>
/// <response code="404">未找到关联用户</response>
[Route("latest", Name = "queryLatestFlowers")]
[ProducesResponseType(StatusCodes.Status200OK)]
[HttpGet]
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
public ActionResult<FlowerResult> GetLatestFlowers(
[FromQuery(Name = "cid")] int? categoryId,
[FromQuery] string? key,
[FromQuery(Name = "from")] long? buyFrom,
[FromQuery(Name = "to")] long? buyTo,
[FromQuery(Name = "cfrom")] decimal? costFrom,
[FromQuery(Name = "cto")] decimal? costTo,
[FromQuery(Name = "photo")] bool? includePhoto,
[FromQuery(Name = "lat")] double? latitude,
[FromQuery(Name = "lon")] double? longitude,
[FromQuery] int? distance,
[FromQuery(Name = "p")] int? page = 0,
[FromQuery(Name = "size")] int? pageSize = 20)
{
var result = GetFlowerResult(null, (int)EventTypes.Share, categoryId, key, buyFrom, buyTo, costFrom, costTo, includePhoto, latitude, longitude, distance, page, pageSize);
return Ok(result);
}
private FlowerResult GetFlowerResult(int? userId, int? eventId, int? categoryId, string? key,
long? buyFrom, long? buyTo, decimal? costFrom, decimal? costTo,
bool? includePhoto, double? latitude, double? longitude, int? distance,
int? page = 0, int? pageSize = 20)
{
IQueryable<FlowerItem> flowers;
if (userId != null)
{
flowers = database.Flowers.Where(f => f.OwnerId == userId);
}
else
{
flowers = database.Flowers;
}
if (categoryId != null)
{
flowers = flowers.Where(f => f.CategoryId == categoryId);
}
if (key != null)
{
flowers = flowers.Where(f =>
f.Name.Contains(key, StringComparison.CurrentCultureIgnoreCase) ||
f.Purchase != null &&
f.Purchase.Contains(key, StringComparison.CurrentCultureIgnoreCase));
}
if (buyFrom != null)
{
flowers = flowers.Where(f => f.DateBuyUnixTime >= buyFrom);
}
if (buyTo != null)
{
flowers = flowers.Where(f => f.DateBuyUnixTime <= buyTo);
}
if (costFrom != null)
{
flowers = flowers.Where(f => f.Cost != null && f.Cost >= costFrom);
}
if (costTo != null)
{
flowers = flowers.Where(f => f.Cost != null && f.Cost <= costTo);
}
if (eventId != null)
{
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)
{
items = flowers.Where(f => f.Latitude != null && f.Longitude != null)
.AsEnumerable()
.Where(f =>
{
var d = GetDistance(latitude.Value, longitude.Value, f.Latitude ?? 0, f.Longitude ?? 0);
f.Distance = (int)d;
return d <= distance;
});
}
else
{
items = flowers;
}
int count = items.Count();
var size = pageSize ?? 20;
var p = page ?? 0;
items = items.Skip(p * size).Take(size);
if (includePhoto == true)
{
foreach (var f in items)
{
//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";
}
}
}
return new()
{
Flowers = items.ToArray(),
Count = count
};
}
/// <summary>
/// 查询用户的花草
/// </summary>
/// <remarks>
/// 请求示例:
///
/// GET /api/flower/get
/// Authorization: authorization id
///
/// 参数:
///
/// id: int
/// photo: bool?
///
/// </remarks>
/// <param name="id">花草唯一 id</param>
/// <param name="includePhoto">是否包含封面图片</param>
/// <returns>会话有效则返回查询到的花草对象</returns>
/// <response code="200">返回查询到的花草对象</response>
/// <response code="401">未找到登录会话或已过期</response>
/// <response code="403">用户已禁用</response>
/// <response code="404">未找到关联用户或者未找到花草</response>
[Route("get", Name = "getFlower")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesErrorResponseType(typeof(ErrorResponse))]
[HttpGet]
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
public ActionResult<FlowerItem> GetFlower(
[FromQuery][Required] int id,
[FromQuery(Name = "photo")] bool? includePhoto)
{
var (result, user) = CheckPermission();
if (result != null)
{
return result;
}
if (user == null)
{
return NotFound();
}
SaveDatabase();
var item = database.Flowers.Find(id);
if (item == null)
{
return NotFound($"Flower id {id} not found");
}
var loc = database.Records
.OrderByDescending(r => r.DateUnixTime)
.SingleOrDefault(r => r.FlowerId == id && (r.EventId == (int)EventTypes.Move || r.EventId == (int)EventTypes.Born));
if (loc != null)
{
item.Location = loc.Memo;
}
if (includePhoto == true)
{
item.Photos = [.. database.Photos.Where(p => p.FlowerId == item.Id && p.RecordId == null)];
foreach (var photo in item.Photos)
{
photo.Url = $"photo/flower/{item.Id}/{photo.Path}";
}
}
return Ok(item);
}
/// <summary>
/// 移除用户的花草
/// </summary>
/// <remarks>
/// 请求示例:
///
/// DELETE /api/flower/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 = "removeFlower")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesErrorResponseType(typeof(ErrorResponse))]
[HttpDelete]
public ActionResult<int> RemoveFlower([FromQuery][Required] int id)
{
var (result, user) = CheckPermission();
if (result != null)
{
return result;
}
if (user == null)
{
return NotFound();
}
//database.Records.Where(r => r.OwnerId == user.Id && r.FlowerId == id).ExecuteDelete();
var count = database.Flowers.Where(f => f.OwnerId == user.Id && f.Id == id).ExecuteDelete();
SaveDatabase();
return Ok(count);
}
/// <summary>
/// 批量移除用户的花草
/// </summary>
/// <remarks>
/// 请求示例:
///
/// POST /api/flower/removeany
/// Authorization: authorization id
/// [
/// 2, 4, 5, 11
/// ]
///
/// </remarks>
/// <param name="ids">要移除的花草唯一 id 的数组</param>
/// <returns>会话有效则返回操作影响的数据库行数</returns>
/// <response code="200">返回操作影响的数据库行数</response>
/// <response code="401">未找到登录会话或已过期</response>
/// <response code="403">用户已禁用</response>
/// <response code="404">未找到关联用户</response>
[Route("removeany", Name = "removeFlowers")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesErrorResponseType(typeof(ErrorResponse))]
[HttpPost]
[Consumes("application/json")]
public ActionResult<int> RemoveFlowers([FromBody] int[] ids)
{
var (result, user) = CheckPermission();
if (result != null)
{
return result;
}
if (user == null)
{
return NotFound();
}
//database.Records.Where(r => r.OwnerId == user.Id && ids.Contains(r.FlowerId)).ExecuteDelete();
var count = database.Flowers.Where(f => f.OwnerId == user.Id && ids.Contains(f.Id)).ExecuteDelete();
SaveDatabase();
return Ok(count);
}
/// <summary>
/// 用户添加花草
/// </summary>
/// <remarks>
/// 请求示例:
///
/// POST /api/flower/add
/// Authorization: authorization id
///
/// 参数:
///
/// categoryId: 0
/// name: "玛格丽特"
/// dateBuy: 1684919954743
/// cost: 5.00
/// purchase: "花鸟市场"
/// memo: "备注信息"
/// lon: 29.5462794
/// lat: 106.5380034
/// cid: 1
/// cover: &lt;photo&gt;
///
/// </remarks>
/// <param name="flower">花草参数</param>
/// <returns>添加成功则返回已添加的花草对象</returns>
/// <response code="200">返回已添加的花草对象</response>
/// <response code="400">提交的内容不合法</response>
/// <response code="401">未找到登录会话或已过期</response>
/// <response code="403">用户已禁用</response>
/// <response code="404">未找到关联用户</response>
/// <response code="413">提交正文过大</response>
[Route("add", Name = "addFlower")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
[ProducesErrorResponseType(typeof(ErrorResponse))]
[HttpPost]
[Consumes("multipart/form-data")]
[RequestSizeLimit(5 * 1024 * 1024)]
public async Task<ActionResult<FlowerItem>> AddFlower([FromForm] FlowerParameter flower)
{
var (result, user) = CheckPermission();
if (result != null)
{
return result;
}
if (user == null)
{
return NotFound();
}
FileResult? file;
if (flower.Cover?.Length > 0)
{
file = WrapFormFile(flower.Cover);
if (file == null)
{
return BadRequest();
}
}
else
{
file = null;
}
var item = new FlowerItem
{
OwnerId = user.Id,
CategoryId = flower.CategoryId,
Name = flower.Name,
DateBuyUnixTime = flower.DateBuy,
Cost = flower.Cost,
Purchase = flower.Purchase,
Memo = flower.Memo,
Latitude = flower.Latitude,
Longitude = flower.Longitude
};
database.Flowers.Add(item);
SaveDatabase();
var now = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
if (flower.Location != null)
{
var record = new RecordItem
{
FlowerId = item.Id,
EventId = (int)EventTypes.Born,
DateUnixTime = now,
OwnerId = user.Id,
Latitude = flower.Latitude,
Longitude = flower.Longitude,
Memo = flower.Location
};
database.Records.Add(record);
SaveDatabase();
}
if (file != null)
{
try
{
await ExecuteTransaction(async token =>
{
var cover = new PhotoItem
{
OwnerId = user.Id,
FlowerId = item.Id,
FileType = file.FileType,
FileName = file.Filename,
Path = file.Path,
DateUploadUnixTime = now,
Width = file.Width,
Height = file.Height
};
AddPhotoItem(cover);
await WriteToFile(item.Id, file, token: token);
});
}
catch (Exception ex)
{
return Problem(ex.ToString(), "api/flower/add#WriteToFile");
// TODO: Logger
}
}
else if (flower.CoverId is int coverId)
{
var photo = database.Photos.SingleOrDefault(p => p.Id == coverId && p.OwnerId == user.Id);
if (photo != null)
{
photo.FlowerId = item.Id;
SaveDatabase();
try
{
MoveTempFileToFlower(item.Id, photo.Path);
}
catch (Exception ex)
{
return Problem(ex.ToString(), "api/flower/add#MoveTempFileToFlower");
// TODO: Logger
}
}
}
return Ok(item);
}
/// <summary>
/// 用户上传封面
/// </summary>
/// <remarks>
/// 请求示例:
///
/// POST /api/flower/cover_upload
/// Authorization: authorization id
///
/// 参数:
///
/// cover: &lt;photo&gt;
///
/// </remarks>
/// <param name="p">封面参数</param>
/// <returns>添加成功则返回封面 id</returns>
/// <response code="200">返回已添加的封面 id</response>
/// <response code="400">提交的内容不合法</response>
/// <response code="401">未找到登录会话或已过期</response>
/// <response code="403">用户已禁用</response>
/// <response code="404">未找到关联用户</response>
/// <response code="413">提交正文过大</response>
[Route("cover_upload", Name = "uploadCover")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
[ProducesErrorResponseType(typeof(ErrorResponse))]
[HttpPost]
[Consumes("multipart/form-data")]
[RequestSizeLimit(5 * 1024 * 1024)]
public async Task<ActionResult<int>> UploadCover([FromForm] CoverParameter p)
{
var (result, user) = CheckPermission();
if (result != null)
{
return result;
}
if (user == null)
{
return NotFound();
}
if (p.Cover?.Length > 0)
{
var file = WrapFormFile(p.Cover);
if (file == null)
{
return BadRequest();
}
var now = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
try
{
await WriteToFile(TempId, file);
var item = new PhotoItem
{
OwnerId = user.Id,
FileType = file.FileType,
FileName = file.Filename,
Path = file.Path,
DateUploadUnixTime = now,
Width = file.Width,
Height = file.Height
};
database.Photos.Add(item);
SaveDatabase();
return Ok(item.Id);
}
catch (Exception ex)
{
return Problem(ex.ToString(), "api/flower/cover_upload");
// TODO: Logger
}
}
return BadRequest();
}
/// <summary>
/// 修改花草
/// </summary>
/// <remarks>
/// 请求示例:
///
/// PUT /api/flower/update
/// Authorization: authorization id
///
/// 参数:
///
/// id: 0
/// categoryId: 1
/// name: "姬小菊"
/// dateBuy: 1684935276117
/// cost: 15.40
/// purchase: null
/// memo: "备注"
/// lon: 29.5462794
/// lat: 106.5380034
/// cover: &lt;photo&gt;
///
/// </remarks>
/// <param name="update">修改参数</param>
/// <returns>修改成功则返回已修改的花草对象</returns>
/// <response code="200">返回已修改的花草对象</response>
/// <response code="400">提交的内容不合法</response>
/// <response code="401">未找到登录会话或已过期</response>
/// <response code="403">用户已禁用</response>
/// <response code="404">未找到关联用户或者未找到将修改的花草对象</response>
/// <response code="413">提交正文过大</response>
[Route("update", Name = "updateFlower")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
[ProducesErrorResponseType(typeof(ErrorResponse))]
[HttpPut]
[Consumes("multipart/form-data")]
[RequestSizeLimit(5 * 1024 * 1024)]
public async Task<ActionResult<FlowerItem>> Update([FromForm] FlowerUpdateParameter update)
{
var (result, user) = CheckPermission();
if (result != null)
{
return result;
}
if (user == null)
{
return NotFound();
}
var flower = database.Flowers.SingleOrDefault(f => f.Id == update.Id && f.OwnerId == user.Id);
if (flower == null)
{
return NotFound($"Flower id {update.Id} not found");
}
flower.CategoryId = update.CategoryId;
flower.Name = update.Name;
flower.DateBuyUnixTime = update.DateBuy;
flower.Cost = update.Cost;
flower.Purchase = update.Purchase;
flower.Memo = update.Memo;
flower.Latitude = update.Latitude;
flower.Longitude = update.Longitude;
if (update.Cover?.Length > 0)
{
var file = WrapFormFile(update.Cover);
if (file == null)
{
return BadRequest();
}
var now = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var photos = database.Photos.Where(p => p.FlowerId == update.Id && p.RecordId == null).ToList();
if (photos.Count > 0)
{
database.Photos.Where(p => p.RecordId == null).ExecuteDelete();
foreach (var photo in photos)
{
DeleteFile(update.Id, photo.Path);
}
}
SaveDatabase();
try
{
await ExecuteTransaction(async token =>
{
var cover = new PhotoItem
{
OwnerId = user.Id,
FlowerId = update.Id,
FileType = file.FileType,
FileName = file.Filename,
Path = file.Path,
DateUploadUnixTime = now,
Width = file.Width,
Height = file.Height
};
AddPhotoItem(cover);
await WriteToFile(update.Id, file, token: token);
});
}
catch (Exception ex)
{
return Problem(ex.ToString(), "api/flower/update");
// TODO: Logger
}
}
else
{
SaveDatabase();
}
return Ok(flower);
}
/// <summary>
/// 添加花草封面
/// </summary>
/// <remarks>
/// 请求示例:
///
/// POST /api/flower/add_cover
/// Authorization: authorization id
///
/// 参数:
///
/// id: int
/// lon: double?
/// lat: double?
/// photo: IFormFile
///
/// </remarks>
/// <param name="param">封面修改参数</param>
/// <returns>修改成功则返回 HTTP 204</returns>
/// <response code="204">修改成功</response>
/// <response code="400">照片格式非法</response>
/// <response code="401">未找到登录会话或已过期</response>
/// <response code="403">用户已禁用</response>
/// <response code="404">未找到关联用户或者关联的花草</response>
/// <response code="413">提交正文过大</response>
[Route("add_cover", Name = "addFlowerCover")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
[ProducesErrorResponseType(typeof(ErrorResponse))]
[HttpPost]
[Consumes("multipart/form-data")]
[RequestSizeLimit(5 * 1024 * 1024)]
public async Task<ActionResult> UploadCovers([FromForm] FlowerCoverParameter param)
{
var (result, user) = CheckPermission();
if (result != null)
{
return result;
}
if (user == null)
{
return NotFound();
}
var flower = database.Flowers.SingleOrDefault(f => f.Id == param.Id && f.OwnerId == user.Id);
if (flower == null)
{
return NotFound($"Flower id {param.Id} not found");
}
if (param.Cover?.Length > 0)
{
var file = WrapFormFile(param.Cover);
if (file == null)
{
return BadRequest();
}
var now = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
flower.Latitude = param.Latitude;
flower.Longitude = param.Longitude;
SaveDatabase();
try
{
await ExecuteTransaction(async token =>
{
var cover = new PhotoItem
{
OwnerId = user.Id,
FlowerId = param.Id,
FileType = file.FileType,
FileName = file.Filename,
Path = file.Path,
DateUploadUnixTime = now,
Width = file.Width,
Height = file.Height
};
AddPhotoItem(cover);
await WriteToFile(param.Id, file, token: token);
});
}
catch (Exception ex)
{
return Problem(ex.ToString(), "api/flower/add_cover");
// TODO: Logger
}
}
return NoContent();
}
/// <summary>
/// 获取花草特定类型事件的照片列表
/// </summary>
/// <remarks>
/// 请求示例:
///
/// GET /api/flower/photos
/// Authorization: authorization id
///
/// 参数:
///
/// id: int
/// eid: int?
///
/// </remarks>
/// <param name="id">花草唯一 id</param>
/// <param name="eventId">事件类型 id</param>
/// <returns>验证通过则返回花草特定类型事件的照片列表</returns>
/// <response code="200">返回花草特定类型事件的照片列表</response>
/// <response code="401">未找到登录会话或已过期</response>
/// <response code="403">用户已禁用</response>
/// <response code="404">未找到关联用户或者未找到花草对象</response>
[Route("photos", Name = "getFlowerPhotos")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesErrorResponseType(typeof(ErrorResponse))]
[HttpGet]
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
public ActionResult<PhotoItem[]> GetPhotos([Required][FromQuery] int id, [FromQuery(Name = "eid")] int? eventId = 0)
{
var (result, user) = CheckPermission();
if (result != null)
{
return result;
}
if (user == null)
{
return NotFound();
}
SaveDatabase();
var photos = database.Photos.Where(p => database.Records.Any(r => r.FlowerId == id && r.EventId == eventId && r.OwnerId == user.Id));
return Ok(photos);
}
}