complete controllers
This commit is contained in:
parent
3b5bd291f9
commit
50e7297848
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,6 +5,7 @@
|
||||
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
||||
|
||||
flower.db
|
||||
TestCase/
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
|
@ -3,6 +3,7 @@ using Blahblah.FlowerStory.Server.Data.Model;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
@ -44,6 +45,10 @@ public abstract partial class BaseController : ControllerBase
|
||||
/// 管理员用户
|
||||
/// </summary>
|
||||
protected const int UserAdmin = 99;
|
||||
/// <summary>
|
||||
/// 封面事件
|
||||
/// </summary>
|
||||
protected const int EventCover = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 数据库对象
|
||||
@ -130,6 +135,17 @@ public abstract partial class BaseController : ControllerBase
|
||||
return (Unauthorized(), null);
|
||||
}
|
||||
var user = QueryUserItem(token.UserId);
|
||||
//var user = database.Users.Where(u => u.Id == token.UserId).Select(u => new UserItem
|
||||
//{
|
||||
// Id = u.Id,
|
||||
// UserId = u.UserId,
|
||||
// Level = u.Level,
|
||||
// RegisterDateUnixTime = u.RegisterDateUnixTime,
|
||||
// ActiveDateUnixTime = u.ActiveDateUnixTime,
|
||||
// Name = u.Name,
|
||||
// Email = u.Email,
|
||||
// Mobile = u.Mobile
|
||||
//}).SingleOrDefault();
|
||||
if (user == null)
|
||||
{
|
||||
logger?.LogWarning("user not found with id {id}", token.UserId);
|
||||
@ -158,7 +174,7 @@ public abstract partial class BaseController : ControllerBase
|
||||
/// <summary>
|
||||
/// 保存数据库变动并输出日志
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <returns>操作的数据库行数</returns>
|
||||
protected int SaveDatabase()
|
||||
{
|
||||
var count = database.SaveChanges();
|
||||
@ -177,7 +193,7 @@ public abstract partial class BaseController : ControllerBase
|
||||
/// 读取文件到 byte 数组
|
||||
/// </summary>
|
||||
/// <param name="file">来自请求的文件</param>
|
||||
/// <returns></returns>
|
||||
/// <returns>文件结果对象</returns>
|
||||
protected FileResult? WrapFormFile(IFormFile file)
|
||||
{
|
||||
if (file == null)
|
||||
@ -203,16 +219,36 @@ public abstract partial class BaseController : ControllerBase
|
||||
}
|
||||
var data = ms.ToArray();
|
||||
var name = file.FileName;
|
||||
name = $"{Path.GetFileNameWithoutExtension(name)}_{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}.{Path.GetExtension(name)}";
|
||||
var ext = Path.GetExtension(name);
|
||||
var path = $"{WebUtility.UrlEncode(Path.GetFileNameWithoutExtension(name))}_{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}{ext}";
|
||||
|
||||
return new FileResult
|
||||
{
|
||||
Filename = name,
|
||||
FileType = ext,
|
||||
Path = path,
|
||||
Content = data
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入文件到用户的花草目录中
|
||||
/// </summary>
|
||||
/// <param name="uid">用户唯一 id</param>
|
||||
/// <param name="fid">花草唯一 id</param>
|
||||
/// <param name="file">文件对象</param>
|
||||
protected async Task WriteToFile(int uid, int fid, FileResult file)
|
||||
{
|
||||
var directory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "uploads", uid.ToString(), fid.ToString());
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
var path = Path.Combine(directory, file.Path);
|
||||
await System.IO.File.WriteAllBytesAsync(path, file.Content);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -223,7 +259,17 @@ public record FileResult
|
||||
/// <summary>
|
||||
/// 文件名
|
||||
/// </summary>
|
||||
public string? Filename { get; init; }
|
||||
public required string Filename { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件类型
|
||||
/// </summary>
|
||||
public required string FileType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 储存路径
|
||||
/// </summary>
|
||||
public required string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件内容
|
||||
@ -231,3 +277,22 @@ public record FileResult
|
||||
[Required]
|
||||
public required byte[] Content { get; init; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 照片参数
|
||||
/// </summary>
|
||||
public record PhotoParameter
|
||||
{
|
||||
/// <summary>
|
||||
/// 花草 id
|
||||
/// </summary>
|
||||
[Required]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 封面照片
|
||||
/// </summary>
|
||||
[Required]
|
||||
public required IFormFile Photo { get; set; }
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using Blahblah.FlowerStory.Server.Data.Model;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Blahblah.FlowerStory.Server.Controller;
|
||||
|
||||
@ -14,7 +15,7 @@ partial class BaseController
|
||||
protected UserItem? QueryUserItem(int uid)
|
||||
{
|
||||
return database.Users
|
||||
.FromSql($"SELECT \"uid\",\"id\",\"\" AS \"password\",\"level\",\"regdate\",\"activedate\",\"name\",\"email\",\"mobile\",NULL AS \"avatar\" FROM [users] WHERE \"uid\" = {uid} LIMIT 1")
|
||||
.FromSql($"SELECT \"uid\",\"id\",\"\" AS \"password\",\"level\",\"regdate\",\"activedate\",\"name\",\"email\",\"mobile\",NULL AS \"avatar\" FROM \"users\" WHERE \"uid\" = {uid} LIMIT 1")
|
||||
.SingleOrDefault();
|
||||
}
|
||||
|
||||
@ -26,7 +27,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\",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")
|
||||
.SingleOrDefault();
|
||||
}
|
||||
|
||||
@ -49,27 +50,4 @@ partial class BaseController
|
||||
{
|
||||
return database.Database.ExecuteSql($"UPDATE \"users\" SET \"avatar\" = NULL WHERE \"uid\" = {uid}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除用户名下的花草
|
||||
/// </summary>
|
||||
/// <param name="uid">用户唯一 id</param>
|
||||
/// <param name="fid">花草唯一 id</param>
|
||||
/// <returns></returns>
|
||||
protected int RemoveUserFlower(int uid, int fid)
|
||||
{
|
||||
return database.Database.ExecuteSql($"DELETE FROM \"flowers\" WHERE \"uid\" = {uid} AND \"fid\" = {fid}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量移除用户名下的花草
|
||||
/// </summary>
|
||||
/// <param name="uid">用户唯一 id</param>
|
||||
/// <param name="fids">花草唯一 id 的数组</param>
|
||||
/// <returns></returns>
|
||||
protected int RemoveUserFlowers(int uid, int[] fids)
|
||||
{
|
||||
var idfilter = string.Join(", ", fids);
|
||||
return database.Database.ExecuteSql($"DELETE FROM \"flowers\" WHERE \"uid\" = {uid} AND \"fid\" IN ({idfilter})");
|
||||
}
|
||||
}
|
||||
|
452
Server/Controller/EventApiController.cs
Normal file
452
Server/Controller/EventApiController.cs
Normal file
@ -0,0 +1,452 @@
|
||||
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/event")]
|
||||
public class EventApiController : BaseController
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public EventApiController(FlowerDatabase database, ILogger<BaseController>? logger = null) : base(database, logger)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户相关所有符合条件的事件
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// GET /api/event/query
|
||||
/// Authorization: authorization id
|
||||
///
|
||||
/// 参数:
|
||||
///
|
||||
/// eid: int?
|
||||
/// key: string?
|
||||
/// from: long?
|
||||
/// to: long?
|
||||
/// p: bool?
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="eventId">事件类型 id</param>
|
||||
/// <param name="key">查询关键字</param>
|
||||
/// <param name="from">起始日期</param>
|
||||
/// <param name="to">结束日期</param>
|
||||
/// <param name="includePhoto">是否包含图片</param>
|
||||
/// <returns>会话有效则返回符合条件的花草集</returns>
|
||||
/// <response code="200">返回符合条件的花草集</response>
|
||||
/// <response code="401">未找到登录会话或已过期</response>
|
||||
/// <response code="403">用户已禁用</response>
|
||||
/// <response code="404">未找到关联用户</response>
|
||||
[Route("query", Name = "queryEvents")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[HttpGet]
|
||||
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
|
||||
public ActionResult<RecordItem[]> GetRecords(
|
||||
[FromQuery(Name = "eid")] int? eventId,
|
||||
[FromQuery] string? key,
|
||||
[FromQuery] long? from,
|
||||
[FromQuery] long? to,
|
||||
[FromQuery(Name = "p")] bool? includePhoto)
|
||||
{
|
||||
var (result, user) = CheckPermission();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
SaveDatabase();
|
||||
|
||||
var records = database.Records.Where(r => r.OwnerId == user.Id);
|
||||
if (eventId != null)
|
||||
{
|
||||
records = records.Where(r => r.EventId == eventId);
|
||||
}
|
||||
if (key != null)
|
||||
{
|
||||
records = records.Where(r =>
|
||||
r.ByUserName != null && r.ByUserName.ToLower().Contains(key.ToLower()) ||
|
||||
r.Memo != null && r.Memo.ToLower().Contains(key.ToLower()));
|
||||
}
|
||||
if (from != null)
|
||||
{
|
||||
records = records.Where(r => r.DateUnixTime >= from);
|
||||
}
|
||||
if (to != null)
|
||||
{
|
||||
records = records.Where(r => r.DateUnixTime <= to);
|
||||
}
|
||||
|
||||
if (includePhoto == true)
|
||||
{
|
||||
foreach (var r in records)
|
||||
{
|
||||
r.Photos = database.Photos.Where(p => p.RecordId == r.Id).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(records.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除用户的事件
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// DELETE /api/event/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 = "removeEvent")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[HttpDelete]
|
||||
public ActionResult<int> RemoveEvent([FromQuery][Required] int id)
|
||||
{
|
||||
var (result, user) = CheckPermission();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var count = database.Records.Where(r => r.OwnerId == user.Id && r.Id == id).ExecuteDelete();
|
||||
|
||||
SaveDatabase();
|
||||
|
||||
return Ok(count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量移除用户的事件
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// POST /api/event/remove
|
||||
/// 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 = "removeEvents")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[HttpPost]
|
||||
[Consumes("application/json")]
|
||||
public ActionResult<int> RemoveEvents([FromBody] int[] ids)
|
||||
{
|
||||
var (result, user) = CheckPermission();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var count = database.Records.Where(r => r.OwnerId == user.Id && ids.Contains(r.Id)).ExecuteDelete();
|
||||
|
||||
SaveDatabase();
|
||||
|
||||
return Ok(count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用户添加事件
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// POST /api/event/add
|
||||
/// Authorization: authorization id
|
||||
/// {
|
||||
/// "flowerId": 1,
|
||||
/// "eventId": 4, // 浇水
|
||||
/// "byUser": "朋友",
|
||||
/// "memo": "快干死了"
|
||||
/// }
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="event">事件参数</param>
|
||||
/// <returns>添加成功则返回已添加的事件对象</returns>
|
||||
/// <response code="200">返回已添加的事件对象</response>
|
||||
/// <response code="401">未找到登录会话或已过期</response>
|
||||
/// <response code="403">用户已禁用</response>
|
||||
/// <response code="404">未找到关联用户</response>
|
||||
[Route("add", Name = "addEvent")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[HttpPost]
|
||||
[Consumes("application/json")]
|
||||
public ActionResult<RecordItem> AddEvent([FromBody] EventParameter @event)
|
||||
{
|
||||
var (result, user) = CheckPermission();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var item = new RecordItem
|
||||
{
|
||||
OwnerId = user.Id,
|
||||
FlowerId = @event.FlowerId,
|
||||
EventId = @event.EventId,
|
||||
DateUnixTime = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
||||
ByUserId = @event.ByUser == null ? user.Id : null,
|
||||
ByUserName = @event.ByUser,
|
||||
Memo = @event.Memo
|
||||
};
|
||||
database.Records.Add(item);
|
||||
SaveDatabase();
|
||||
|
||||
return Ok(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修改事件
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// PUT /api/event/update
|
||||
/// Authorization: authorization id
|
||||
/// {
|
||||
/// "id": 1,
|
||||
/// "flowerId": 1,
|
||||
/// "eventId": 5, // 施肥
|
||||
/// "byUser": null,
|
||||
/// "memo": "花多多1号"
|
||||
/// }
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="update">修改参数</param>
|
||||
/// <returns>修改成功则返回已修改的事件对象</returns>
|
||||
/// <response code="200">返回已修改的事件对象</response>
|
||||
/// <response code="401">未找到登录会话或已过期</response>
|
||||
/// <response code="403">用户已禁用</response>
|
||||
/// <response code="404">未找到关联用户或者未找到将修改的事件对象</response>
|
||||
[Route("update", Name = "updateEvent")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[HttpPut]
|
||||
[Consumes("application/json")]
|
||||
public ActionResult<RecordItem> Update([FromBody] EventUpdateParameter update)
|
||||
{
|
||||
var (result, user) = CheckPermission();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var record = database.Records.SingleOrDefault(r => r.Id == update.Id && r.OwnerId == user.Id);
|
||||
if (record == null)
|
||||
{
|
||||
SaveDatabase();
|
||||
return NotFound(update.Id);
|
||||
}
|
||||
record.FlowerId = update.FlowerId;
|
||||
record.EventId = update.EventId;
|
||||
if (update.ByUser == null)
|
||||
{
|
||||
record.ByUserId = user.Id;
|
||||
record.ByUserName = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
record.ByUserId = null;
|
||||
record.ByUserName = update.ByUser;
|
||||
}
|
||||
record.Memo = update.Memo;
|
||||
SaveDatabase();
|
||||
|
||||
return Ok(user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加事件关联照片
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// POST /api/event/add_photo
|
||||
/// Authorization: authorization id
|
||||
///
|
||||
/// 参数:
|
||||
///
|
||||
/// id: int
|
||||
/// photo: IFormFile
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="id">事件唯一 id</param>
|
||||
/// <param name="photo">图片</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_photo", Name = "addEventPhoto")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
|
||||
[HttpPost]
|
||||
[Consumes("multipart/form-data")]
|
||||
[RequestSizeLimit(15 * 1024 * 1024)]
|
||||
public async Task<ActionResult> UploadCovers([Required][FromQuery] int id, [Required] IFormFile photo)
|
||||
{
|
||||
var (result, user) = CheckPermission();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var record = database.Records.SingleOrDefault(r => r.Id == id && r.OwnerId == user.Id);
|
||||
if (record == null)
|
||||
{
|
||||
SaveDatabase();
|
||||
return NotFound(id);
|
||||
}
|
||||
if (photo.Length > 0)
|
||||
{
|
||||
var file = WrapFormFile(photo);
|
||||
if (file == null)
|
||||
{
|
||||
SaveDatabase();
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
var p = new PhotoItem
|
||||
{
|
||||
FlowerId = record.FlowerId,
|
||||
RecordId = id,
|
||||
FileType = file.FileType,
|
||||
FileName = file.Filename,
|
||||
Path = file.Path,
|
||||
DateUploadUnixTime = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
|
||||
};
|
||||
database.Photos.Add(p);
|
||||
|
||||
try
|
||||
{
|
||||
await WriteToFile(user.Id, id, file);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SaveDatabase();
|
||||
return Problem(ex.ToString(), "api/event/add_photo");
|
||||
// TODO: Logger
|
||||
}
|
||||
}
|
||||
SaveDatabase();
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取事件关联的照片列表
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// GET /api/event/photos
|
||||
/// 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("photos", Name = "getEventPhotos")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[HttpGet]
|
||||
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
|
||||
public ActionResult<PhotoItem[]> GetPhotos([Required][FromQuery] int id)
|
||||
{
|
||||
var (result, user) = CheckPermission();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
SaveDatabase();
|
||||
|
||||
var photos = database.Photos.Where(p => p.RecordId == id && database.Records.Any(r => r.Id == id && r.OwnerId == user.Id));
|
||||
|
||||
return Ok(photos);
|
||||
}
|
||||
}
|
44
Server/Controller/EventApiController.structs.cs
Normal file
44
Server/Controller/EventApiController.structs.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Blahblah.FlowerStory.Server.Controller;
|
||||
|
||||
/// <summary>
|
||||
/// 事件参数
|
||||
/// </summary>
|
||||
public record EventParameter
|
||||
{
|
||||
/// <summary>
|
||||
/// 花草唯一 id
|
||||
/// </summary>
|
||||
[Required]
|
||||
public int FlowerId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 事件 id
|
||||
/// </summary>
|
||||
[Required]
|
||||
public int EventId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作人姓名
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string? ByUser { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 事件备注
|
||||
/// </summary>
|
||||
public string? Memo { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 事件修改参数
|
||||
/// </summary>
|
||||
public record EventUpdateParameter : EventParameter
|
||||
{
|
||||
/// <summary>
|
||||
/// 事件唯一 id
|
||||
/// </summary>
|
||||
[Required]
|
||||
public int Id { get; set; }
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
using Blahblah.FlowerStory.Server.Data;
|
||||
using Blahblah.FlowerStory.Server.Data.Model;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Blahblah.FlowerStory.Server.Controller;
|
||||
|
||||
/// <summary>
|
||||
/// 事件相关服务
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Produces("application/json")]
|
||||
[Route("api/event")]
|
||||
public class EventController : BaseController
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public EventController(FlowerDatabase database, ILogger<BaseController>? logger = null) : base(database, logger)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户相关所有符合条件的事件
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// GET /api/event/query
|
||||
/// Authorization: authorization id
|
||||
///
|
||||
/// 参数:
|
||||
///
|
||||
/// cid: int?
|
||||
/// key: string?
|
||||
/// from: long?
|
||||
/// to: long?
|
||||
/// cfrom: decimal?
|
||||
/// cto: decimal?
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="eventId">事件类型 id</param>
|
||||
/// <param name="key">查询关键字</param>
|
||||
/// <param name="from">起始日期</param>
|
||||
/// <param name="to">结束日期</param>
|
||||
/// <returns>会话有效则返回符合条件的花草集</returns>
|
||||
/// <response code="200">返回符合条件的花草集</response>
|
||||
/// <response code="401">未找到登录会话或已过期</response>
|
||||
/// <response code="403">用户已禁用</response>
|
||||
/// <response code="404">未找到关联用户</response>
|
||||
[Route("query", Name = "queryEvents")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[HttpGet]
|
||||
public ActionResult<RecordItem[]> GetRecords(
|
||||
[FromQuery(Name = "eid")] int? eventId,
|
||||
[FromQuery] string? key,
|
||||
[FromQuery] long? from,
|
||||
[FromQuery] long? to)
|
||||
{
|
||||
var (result, user) = CheckPermission();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
SaveDatabase();
|
||||
|
||||
var records = database.Records.Where(r => r.OwnerId == user.Id);
|
||||
if (eventId != null)
|
||||
{
|
||||
records = records.Where(r => r.EventId == eventId);
|
||||
}
|
||||
if (key != null)
|
||||
{
|
||||
records = records.Where(r =>
|
||||
r.ByUserName != null && r.ByUserName.ToLower().Contains(key.ToLower())
|
||||
// TODO: notes
|
||||
);
|
||||
}
|
||||
if (from != null)
|
||||
{
|
||||
records = records.Where(r => r.DateUnixTime >= from);
|
||||
}
|
||||
if (to != null)
|
||||
{
|
||||
records = records.Where(r => r.DateUnixTime <= to);
|
||||
}
|
||||
|
||||
return Ok(records.ToArray());
|
||||
}
|
||||
}
|
@ -7,15 +7,15 @@ using System.ComponentModel.DataAnnotations;
|
||||
namespace Blahblah.FlowerStory.Server.Controller;
|
||||
|
||||
/// <summary>
|
||||
/// 花草相关服务
|
||||
/// 花草相关 API 服务
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Produces("application/json")]
|
||||
[Route("api/flower")]
|
||||
public class FlowerController : BaseController
|
||||
public class FlowerApiController : BaseController
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public FlowerController(FlowerDatabase database, ILogger<BaseController>? logger = null) : base(database, logger)
|
||||
public FlowerApiController(FlowerDatabase database, ILogger<BaseController>? logger = null) : base(database, logger)
|
||||
{
|
||||
}
|
||||
|
||||
@ -36,6 +36,7 @@ public class FlowerController : BaseController
|
||||
/// to: long?
|
||||
/// cfrom: decimal?
|
||||
/// cto: decimal?
|
||||
/// p: bool?
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="categoryId">类别 id</param>
|
||||
@ -44,6 +45,7 @@ public class FlowerController : BaseController
|
||||
/// <param name="buyTo">结束购买日期</param>
|
||||
/// <param name="costFrom">开销最小值</param>
|
||||
/// <param name="costTo">开销最大值</param>
|
||||
/// <param name="includePhoto">是否包含封面图片</param>
|
||||
/// <returns>会话有效则返回符合条件的花草集</returns>
|
||||
/// <response code="200">返回符合条件的花草集</response>
|
||||
/// <response code="401">未找到登录会话或已过期</response>
|
||||
@ -55,13 +57,15 @@ public class FlowerController : BaseController
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[HttpGet]
|
||||
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
|
||||
public ActionResult<FlowerItem[]> 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 = "cto")] decimal? costTo,
|
||||
[FromQuery(Name = "p")] bool? includePhoto)
|
||||
{
|
||||
var (result, user) = CheckPermission();
|
||||
if (result != null)
|
||||
@ -104,6 +108,16 @@ public class FlowerController : BaseController
|
||||
flowers = flowers.Where(f => f.Cost != null && f.Cost <= costTo);
|
||||
}
|
||||
|
||||
if (includePhoto == true)
|
||||
{
|
||||
foreach (var f in flowers)
|
||||
{
|
||||
f.Photos = database.Photos.Where(p =>
|
||||
database.Records.Any(r =>
|
||||
r.FlowerId == f.Id && r.EventId == EventCover && r.Id == p.RecordId)).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(flowers.ToArray());
|
||||
}
|
||||
|
||||
@ -145,7 +159,8 @@ public class FlowerController : BaseController
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var count = RemoveUserFlower(user.Id, id);
|
||||
//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();
|
||||
|
||||
@ -190,7 +205,8 @@ public class FlowerController : BaseController
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var count = RemoveUserFlowers(user.Id, ids);
|
||||
//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();
|
||||
|
||||
@ -300,6 +316,7 @@ public class FlowerController : BaseController
|
||||
var flower = database.Flowers.SingleOrDefault(f => f.Id == update.Id && f.OwnerId == user.Id);
|
||||
if (flower == null)
|
||||
{
|
||||
SaveDatabase();
|
||||
return NotFound(update.Id);
|
||||
}
|
||||
flower.CategoryId = update.CategoryId;
|
||||
@ -311,4 +328,156 @@ public class FlowerController : BaseController
|
||||
|
||||
return Ok(user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加花草封面
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// POST /api/flower/add_cover
|
||||
/// Authorization: authorization id
|
||||
///
|
||||
/// 参数:
|
||||
///
|
||||
/// id: int
|
||||
/// photo: IFormFile
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="id">花草唯一 id</param>
|
||||
/// <param name="photo">封面图片</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)]
|
||||
[HttpPost]
|
||||
[Consumes("multipart/form-data")]
|
||||
[RequestSizeLimit(5 * 1024 * 1024)]
|
||||
public async Task<ActionResult> UploadCovers([Required][FromQuery] int id, [Required] IFormFile photo)
|
||||
{
|
||||
var (result, user) = CheckPermission();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var flower = database.Flowers.SingleOrDefault(f => f.Id == id && f.OwnerId == user.Id);
|
||||
if (flower == null)
|
||||
{
|
||||
SaveDatabase();
|
||||
return NotFound(id);
|
||||
}
|
||||
if (photo.Length > 0)
|
||||
{
|
||||
var file = WrapFormFile(photo);
|
||||
if (file == null)
|
||||
{
|
||||
SaveDatabase();
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
var now = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
var record = database.Records.SingleOrDefault(r => r.FlowerId == id && r.EventId == EventCover);
|
||||
if (record == null)
|
||||
{
|
||||
record = new RecordItem
|
||||
{
|
||||
OwnerId = user.Id,
|
||||
FlowerId = id,
|
||||
EventId = EventCover,
|
||||
DateUnixTime = now,
|
||||
ByUserId = user.Id,
|
||||
ByUserName = user.Name
|
||||
//Memo = ""
|
||||
};
|
||||
database.Records.Add(record);
|
||||
}
|
||||
|
||||
var cover = new PhotoItem
|
||||
{
|
||||
FlowerId = id,
|
||||
Record = record,
|
||||
FileType = file.FileType,
|
||||
FileName = file.Filename,
|
||||
Path = file.Path,
|
||||
DateUploadUnixTime = now
|
||||
};
|
||||
database.Photos.Add(cover);
|
||||
|
||||
try
|
||||
{
|
||||
await WriteToFile(user.Id, id, file);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SaveDatabase();
|
||||
return Problem(ex.ToString(), "api/flower/add_cover");
|
||||
// TODO: Logger
|
||||
}
|
||||
}
|
||||
SaveDatabase();
|
||||
|
||||
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,0 为封面</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)]
|
||||
[HttpGet]
|
||||
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
|
||||
public ActionResult<PhotoItem[]> GetCovers([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);
|
||||
}
|
||||
}
|
@ -42,8 +42,8 @@ public record FlowerParameter
|
||||
public record FlowerUpdateParameter : FlowerParameter
|
||||
{
|
||||
/// <summary>
|
||||
/// 花草 id
|
||||
/// 花草唯一 id
|
||||
/// </summary>
|
||||
[Required]
|
||||
public int Id { get; set; }
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using Blahblah.FlowerStory.Server.Data;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Blahblah.FlowerStory.Server.Controller;
|
||||
|
||||
@ -52,4 +53,45 @@ public class ImageController : BaseController
|
||||
}
|
||||
return File(avatar, "image/png");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 请求花草照片
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// GET /photo/flower/{fid}/{name}
|
||||
/// Authorization: authorization id
|
||||
///
|
||||
/// </remarks>
|
||||
/// <returns>认证通过则显示花草照片</returns>
|
||||
/// <response code="200">返回花草照片</response>
|
||||
/// <response code="401">认证失败</response>
|
||||
/// <response code="404">未找到花草照片</response>
|
||||
[Route("flower/{fid}/{name}", Name = "getFlowerPhoto")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[HttpGet]
|
||||
public async Task<ActionResult> GetFlowerPhoto([Required] int fid, [Required] string name)
|
||||
{
|
||||
var (result, token) = CheckToken();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (token == null)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "uploads", token.UserId.ToString(), fid.ToString(), name);
|
||||
if (System.IO.File.Exists(path))
|
||||
{
|
||||
var data = await System.IO.File.ReadAllBytesAsync(path);
|
||||
var ext = Path.GetExtension(path).ToLower();
|
||||
return File(data, ext == ".png" ? "image/png" : "image/jpeg");
|
||||
}
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
410
Server/Controller/UserApiController.cs
Normal file
410
Server/Controller/UserApiController.cs
Normal file
@ -0,0 +1,410 @@
|
||||
using Blahblah.FlowerStory.Server.Data;
|
||||
using Blahblah.FlowerStory.Server.Data.Model;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Blahblah.FlowerStory.Server.Controller;
|
||||
|
||||
/// <summary>
|
||||
/// 用户会话相关 API 服务
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Produces("application/json")]
|
||||
[Route("api/user")]
|
||||
public partial class UserApiController : BaseController
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public UserApiController(FlowerDatabase db, ILogger<UserApiController> logger) : base(db, logger)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用户登录
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// POST /api/user/auth
|
||||
/// {
|
||||
/// "id": "blahblah",
|
||||
/// "password": "pwd123"
|
||||
/// }
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="login">登录参数</param>
|
||||
/// <returns>成功登录则返回自定义认证头</returns>
|
||||
/// <response code="204">返回自定义认证头</response>
|
||||
/// <response code="401">认证失败</response>
|
||||
/// <response code="404">未找到用户</response>
|
||||
[Route("auth", Name = "authenticate")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[HttpPost]
|
||||
[Consumes("application/json")]
|
||||
public ActionResult Authenticate([FromBody] LoginParamter login)
|
||||
{
|
||||
#if DEBUG
|
||||
logger?.LogInformation("user \"{user}\" try to login with password \"{password}\"", login.Id, login.Password);
|
||||
#endif
|
||||
//var user = database.Users.SingleOrDefault(u => u.UserId == login.Id);
|
||||
var user = QueryUserItemForAuthentication(login.Id);
|
||||
if (user == null)
|
||||
{
|
||||
logger?.LogWarning("user \"{user}\" not found", login.Id);
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
// comput password hash with salt
|
||||
string hash = HashPassword(login.Password, login.Id);
|
||||
if (hash != user.Password)
|
||||
{
|
||||
logger?.LogWarning("hash result {hash}, unauthorized with hash {password}", hash, user.Password);
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
// record the session
|
||||
// TODO: singleton token
|
||||
string clientApp;
|
||||
int expires;
|
||||
if (Request.Headers.TryGetValue("X-ClientAgent", out var clientAgent))
|
||||
{
|
||||
clientApp = "app";
|
||||
expires = 30 * 24 * 60 * 60; // 30 days
|
||||
}
|
||||
else
|
||||
{
|
||||
clientApp = "browser";
|
||||
expires = 20 * 60; // 20 mins
|
||||
}
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var token = new TokenItem
|
||||
{
|
||||
Id = Convert.ToBase64String(SHA256.HashData(Guid.NewGuid().ToByteArray())),
|
||||
UserId = user.Id,
|
||||
LogonDateUnixTime = now.ToUnixTimeMilliseconds(),
|
||||
ActiveDateUnixTime = now.ToUnixTimeMilliseconds(),
|
||||
ExpireDateUnixTime = now.AddSeconds(expires).ToUnixTimeMilliseconds(),
|
||||
ExpireSeconds = expires,
|
||||
ClientApp = clientApp,
|
||||
ClientAgent = clientAgent.Count > 0 ? clientAgent : Request.Headers.UserAgent,
|
||||
DeviceId = Request.Headers.TryGetValue("X-DeviceId", out var deviceId) ? deviceId.ToString() : null
|
||||
};
|
||||
database.Tokens.Add(token);
|
||||
|
||||
user.ActiveDateUnixTime = token.ActiveDateUnixTime;
|
||||
SaveDatabase();
|
||||
|
||||
Response.Headers.Add(AuthHeader, token.Id);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注销当前登录会话
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// POST /api/user/logout
|
||||
/// Authorization: authorization id
|
||||
///
|
||||
/// </remarks>
|
||||
/// <returns>注销失败则返回错误内容</returns>
|
||||
/// <response code="204">注销成功</response>
|
||||
/// <response code="401">认证失败</response>
|
||||
[Route("logout", Name = "logout")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[HttpPost]
|
||||
public ActionResult Logout()
|
||||
{
|
||||
var (result, token) = CheckToken();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (token == null)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
database.Tokens.Remove(token);
|
||||
SaveDatabase();
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册用户
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// POST /api/user/register
|
||||
/// {
|
||||
/// "id": "blahblah",
|
||||
/// "password": "pwd123",
|
||||
/// "userName": "Blah blah",
|
||||
/// "email": "blah@example.com",
|
||||
/// "mobile": "18012345678"
|
||||
/// }
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="user">注册参数</param>
|
||||
/// <returns>成功注册则返回已注册的用户对象</returns>
|
||||
/// <response code="200">返回已注册的用户对象</response>
|
||||
/// <response code="500">用户重复或其他服务器错误</response>
|
||||
[Route("register", Name = "register")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
[HttpPost]
|
||||
[Consumes("application/json")]
|
||||
public ActionResult<UserItem> Register([FromBody] UserParameter user)
|
||||
{
|
||||
#if DEBUG
|
||||
logger?.LogInformation("user register, {user}", user);
|
||||
#endif
|
||||
if (database.Users.Any(u => u.UserId == user.Id))
|
||||
{
|
||||
logger?.LogWarning("duplicate user \"{id}\"", user.Id);
|
||||
return Problem("duplicateUser", "api/user/register");
|
||||
}
|
||||
|
||||
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
var item = new UserItem
|
||||
{
|
||||
UserId = user.Id,
|
||||
Password = HashPassword(user.Password, user.Id),
|
||||
Level = UserCommon,
|
||||
RegisterDateUnixTime = now,
|
||||
ActiveDateUnixTime = now,
|
||||
Name = user.UserName,
|
||||
Email = user.Email,
|
||||
Mobile = user.Mobile
|
||||
};
|
||||
database.Users.Add(item);
|
||||
SaveDatabase();
|
||||
|
||||
return Ok(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询当前会话关联的用户
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// GET /api/user/profile
|
||||
/// Authorization: authorization id
|
||||
///
|
||||
/// </remarks>
|
||||
/// <returns>会话有效则返回关联的用户对象</returns>
|
||||
/// <response code="200">返回关联的用户对象</response>
|
||||
/// <response code="401">未找到登录会话或已过期</response>
|
||||
/// <response code="403">用户已禁用</response>
|
||||
/// <response code="404">未找到关联用户</response>
|
||||
[Route("profile", Name = "getProfile")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[HttpGet]
|
||||
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
|
||||
public ActionResult<UserItem> Profile()
|
||||
{
|
||||
var (result, user) = CheckPermission();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
// update last active time
|
||||
SaveDatabase();
|
||||
|
||||
return Ok(user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修改用户
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// PUT /api/user/update
|
||||
/// Authorization: authorization id
|
||||
/// {
|
||||
/// "userName": "Blah blah",
|
||||
/// "email": "blah@example.com",
|
||||
/// "mobile": "18012345678"
|
||||
/// }
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="update">修改参数</param>
|
||||
/// <returns>修改成功则返回已修改的用户对象</returns>
|
||||
/// <response code="200">返回已修改的用户对象</response>
|
||||
/// <response code="401">未找到登录会话或已过期</response>
|
||||
/// <response code="403">用户已禁用</response>
|
||||
/// <response code="404">未找到关联用户</response>
|
||||
/// <response code="413">提交正文过大</response>
|
||||
[Route("update", Name = "updateProfile")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
|
||||
[HttpPut]
|
||||
[Consumes("application/json")]
|
||||
public ActionResult<UserItem> Update([FromBody] UpdateParameter update)
|
||||
{
|
||||
#if DEBUG
|
||||
logger?.LogInformation("user update, {user}", update);
|
||||
#endif
|
||||
var (result, user) = CheckPermission();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
user.Name = update.UserName;
|
||||
user.Email = update.Email;
|
||||
user.Mobile = update.Mobile;
|
||||
SaveDatabase();
|
||||
|
||||
return Ok(user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修改用户头像
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// PUT /api/user/update_avatar
|
||||
/// Authorization: authorization id
|
||||
///
|
||||
/// 参数:
|
||||
///
|
||||
/// avatar: IFormFile
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="avatar">用户头像</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("update_avatar", Name = "updateAvatar")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
|
||||
[HttpPut]
|
||||
[Consumes("multipart/form-data")]
|
||||
[RequestSizeLimit(5 * 1024 * 1024)]
|
||||
public ActionResult<UserItem> UploadAvatar([Required] IFormFile avatar)
|
||||
{
|
||||
#if DEBUG
|
||||
logger?.LogInformation("user update avatar, {avatar}", avatar);
|
||||
#endif
|
||||
var (result, user) = CheckPermission();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (avatar.Length > 0)
|
||||
{
|
||||
var file = WrapFormFile(avatar);
|
||||
if (file == null)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
user.Avatar = file.Content;
|
||||
}
|
||||
SaveDatabase();
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除用户头像
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// DELETE /api/user/remove_avatar
|
||||
/// Authorization: authorization id
|
||||
///
|
||||
/// </remarks>
|
||||
/// <returns>移除成功则返回修改的数据库行数</returns>
|
||||
/// <response code="200">修改的数据库行数</response>
|
||||
/// <response code="401">未找到登录会话或已过期</response>
|
||||
/// <response code="403">用户已禁用</response>
|
||||
/// <response code="404">未找到关联用户</response>
|
||||
[Route("remove_avatar", Name = "removeAvatar")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[HttpDelete]
|
||||
public ActionResult RemoveAvatar()
|
||||
{
|
||||
var (result, user) = CheckPermission();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var count = RemoveUserAvatar(user.Id);
|
||||
SaveDatabase();
|
||||
|
||||
return Ok(count);
|
||||
}
|
||||
|
||||
//#if DEBUG
|
||||
/// <summary>
|
||||
/// #DEBUG 获取所有用户
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Route("debug_list", Name = "debug_list")]
|
||||
[HttpGet]
|
||||
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
|
||||
public ActionResult<UserItem[]> GetUsers()
|
||||
{
|
||||
return Ok(database.Users.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// #DEBUG 获取所有 token
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Route("debug_tokens", Name = "debug_tokens")]
|
||||
[HttpGet]
|
||||
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
|
||||
public ActionResult<TokenItem[]> GetTokens()
|
||||
{
|
||||
return Ok(database.Tokens.ToArray());
|
||||
}
|
||||
//#endif
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Blahblah.FlowerStory.Server.Controller;
|
||||
|
||||
partial class UserController
|
||||
partial class UserApiController
|
||||
{
|
||||
}
|
||||
|
@ -1,407 +0,0 @@
|
||||
using Blahblah.FlowerStory.Server.Data;
|
||||
using Blahblah.FlowerStory.Server.Data.Model;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Blahblah.FlowerStory.Server.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户会话相关服务
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Produces("application/json")]
|
||||
[Route("api/user")]
|
||||
public partial class UserController : BaseController
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public UserController(FlowerDatabase db, ILogger<UserController> logger) : base(db, logger)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用户登录
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// POST /api/user/auth
|
||||
/// {
|
||||
/// "id": "blahblah",
|
||||
/// "password": "pwd123"
|
||||
/// }
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="login">登录参数</param>
|
||||
/// <returns>成功登录则返回自定义认证头</returns>
|
||||
/// <response code="204">返回自定义认证头</response>
|
||||
/// <response code="401">认证失败</response>
|
||||
/// <response code="404">未找到用户</response>
|
||||
[Route("auth", Name = "authenticate")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[HttpPost]
|
||||
[Consumes("application/json")]
|
||||
public ActionResult Authenticate([FromBody] LoginParamter login)
|
||||
{
|
||||
#if DEBUG
|
||||
logger?.LogInformation("user \"{user}\" try to login with password \"{password}\"", login.Id, login.Password);
|
||||
#endif
|
||||
//var user = database.Users.SingleOrDefault(u => u.UserId == login.Id);
|
||||
var user = QueryUserItemForAuthentication(login.Id);
|
||||
if (user == null)
|
||||
{
|
||||
logger?.LogWarning("user \"{user}\" not found", login.Id);
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
// comput password hash with salt
|
||||
string hash = HashPassword(login.Password, login.Id);
|
||||
if (hash != user.Password)
|
||||
{
|
||||
logger?.LogWarning("hash result {hash}, unauthorized with hash {password}", hash, user.Password);
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
// record the session
|
||||
// TODO: singleton token
|
||||
string clientApp;
|
||||
int expires;
|
||||
if (Request.Headers.TryGetValue("X-ClientAgent", out var clientAgent))
|
||||
{
|
||||
clientApp = "app";
|
||||
expires = 30 * 24 * 60 * 60; // 30 days
|
||||
}
|
||||
else
|
||||
{
|
||||
clientApp = "browser";
|
||||
expires = 20 * 60; // 20 mins
|
||||
}
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var token = new TokenItem
|
||||
{
|
||||
Id = Convert.ToBase64String(SHA256.HashData(Guid.NewGuid().ToByteArray())),
|
||||
UserId = user.Id,
|
||||
LogonDateUnixTime = now.ToUnixTimeMilliseconds(),
|
||||
ActiveDateUnixTime = now.ToUnixTimeMilliseconds(),
|
||||
ExpireDateUnixTime = now.AddSeconds(expires).ToUnixTimeMilliseconds(),
|
||||
ExpireSeconds = expires,
|
||||
ClientApp = clientApp,
|
||||
ClientAgent = clientAgent.Count > 0 ? clientAgent : Request.Headers.UserAgent,
|
||||
DeviceId = Request.Headers.TryGetValue("X-DeviceId", out var deviceId) ? deviceId.ToString() : null
|
||||
};
|
||||
database.Tokens.Add(token);
|
||||
|
||||
user.ActiveDateUnixTime = token.ActiveDateUnixTime;
|
||||
SaveDatabase();
|
||||
|
||||
Response.Headers.Add(AuthHeader, token.Id);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注销当前登录会话
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// POST /api/user/logout
|
||||
/// Authorization: authorization id
|
||||
///
|
||||
/// </remarks>
|
||||
/// <returns>注销失败则返回错误内容</returns>
|
||||
/// <response code="204">注销成功</response>
|
||||
/// <response code="401">认证失败</response>
|
||||
[Route("logout", Name = "logout")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[HttpPost]
|
||||
public ActionResult Logout()
|
||||
{
|
||||
var (result, token) = CheckToken();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (token == null)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
database.Tokens.Remove(token);
|
||||
SaveDatabase();
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册用户
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// POST /api/user/register
|
||||
/// {
|
||||
/// "id": "blahblah",
|
||||
/// "password": "pwd123",
|
||||
/// "userName": "Blah blah",
|
||||
/// "email": "blah@example.com",
|
||||
/// "mobile": "18012345678"
|
||||
/// }
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="user">注册参数</param>
|
||||
/// <returns>成功注册则返回已注册的用户对象</returns>
|
||||
/// <response code="200">返回已注册的用户对象</response>
|
||||
/// <response code="500">用户重复或其他服务器错误</response>
|
||||
[Route("register", Name = "register")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
[HttpPost]
|
||||
[Consumes("application/json")]
|
||||
public ActionResult<UserItem> Register([FromBody] UserParameter user)
|
||||
{
|
||||
#if DEBUG
|
||||
logger?.LogInformation("user register, {user}", user);
|
||||
#endif
|
||||
if (database.Users.Any(u => u.UserId == user.Id))
|
||||
{
|
||||
logger?.LogWarning("duplicate user \"{id}\"", user.Id);
|
||||
return Problem("duplicateUser", "user/register", 500);
|
||||
}
|
||||
|
||||
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
var item = new UserItem
|
||||
{
|
||||
UserId = user.Id,
|
||||
Password = HashPassword(user.Password, user.Id),
|
||||
Level = UserCommon,
|
||||
RegisterDateUnixTime = now,
|
||||
ActiveDateUnixTime = now,
|
||||
Name = user.UserName,
|
||||
Email = user.Email,
|
||||
Mobile = user.Mobile
|
||||
};
|
||||
database.Users.Add(item);
|
||||
SaveDatabase();
|
||||
|
||||
return Ok(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询当前会话关联的用户
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// GET /api/user/profile
|
||||
/// Authorization: authorization id
|
||||
///
|
||||
/// </remarks>
|
||||
/// <returns>会话有效则返回关联的用户对象</returns>
|
||||
/// <response code="200">返回关联的用户对象</response>
|
||||
/// <response code="401">未找到登录会话或已过期</response>
|
||||
/// <response code="403">用户已禁用</response>
|
||||
/// <response code="404">未找到关联用户</response>
|
||||
[Route("profile", Name = "getProfile")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[HttpGet]
|
||||
public ActionResult<UserItem> Profile()
|
||||
{
|
||||
var (result, user) = CheckPermission();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
// update last active time
|
||||
SaveDatabase();
|
||||
|
||||
return Ok(user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修改用户
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// PUT /api/user/update
|
||||
/// Authorization: authorization id
|
||||
/// {
|
||||
/// "userName": "Blah blah",
|
||||
/// "email": "blah@example.com",
|
||||
/// "mobile": "18012345678"
|
||||
/// }
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="update">修改参数</param>
|
||||
/// <returns>修改成功则返回已修改的用户对象</returns>
|
||||
/// <response code="200">返回已修改的用户对象</response>
|
||||
/// <response code="401">未找到登录会话或已过期</response>
|
||||
/// <response code="403">用户已禁用</response>
|
||||
/// <response code="404">未找到关联用户</response>
|
||||
/// <response code="413">提交正文过大</response>
|
||||
[Route("update", Name = "updateProfile")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
|
||||
[HttpPut]
|
||||
[Consumes("application/json")]
|
||||
public ActionResult<UserItem> Update([FromBody] UpdateParameter update)
|
||||
{
|
||||
#if DEBUG
|
||||
logger?.LogInformation("user update, {user}", update);
|
||||
#endif
|
||||
var (result, user) = CheckPermission();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
user.Name = update.UserName;
|
||||
user.Email = update.Email;
|
||||
user.Mobile = update.Mobile;
|
||||
SaveDatabase();
|
||||
|
||||
return Ok(user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修改用户头像
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// PUT /api/user/update_avatar
|
||||
/// Authorization: authorization id
|
||||
///
|
||||
/// 参数:
|
||||
///
|
||||
/// avatar: IFormFile?
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="avatar">用户头像</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("update_avatar", Name = "updateAvatar")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
|
||||
[HttpPut]
|
||||
[Consumes("multipart/form-data")]
|
||||
[RequestSizeLimit(5 * 1024 * 1024)]
|
||||
public ActionResult<UserItem> UploadAvatar(IFormFile? avatar)
|
||||
{
|
||||
#if DEBUG
|
||||
logger?.LogInformation("user update avatar, {avatar}", avatar);
|
||||
#endif
|
||||
var (result, user) = CheckPermission();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (avatar?.Length > 0)
|
||||
{
|
||||
var file = WrapFormFile(avatar);
|
||||
if (file == null)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
user.Avatar = file.Content;
|
||||
}
|
||||
SaveDatabase();
|
||||
|
||||
return Ok(user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除用户头像
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 请求示例:
|
||||
///
|
||||
/// DELETE /api/user/remove_avatar
|
||||
/// Authorization: authorization id
|
||||
///
|
||||
/// </remarks>
|
||||
/// <returns>移除成功则返回修改的数据库行数</returns>
|
||||
/// <response code="200">修改的数据库行数</response>
|
||||
/// <response code="401">未找到登录会话或已过期</response>
|
||||
/// <response code="403">用户已禁用</response>
|
||||
/// <response code="404">未找到关联用户</response>
|
||||
[Route("remove_avatar", Name = "removeAvatar")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[HttpDelete]
|
||||
public ActionResult RemoveAvatar()
|
||||
{
|
||||
var (result, user) = CheckPermission();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var count = RemoveUserAvatar(user.Id);
|
||||
SaveDatabase();
|
||||
|
||||
return Ok(count);
|
||||
}
|
||||
|
||||
//#if DEBUG
|
||||
/// <summary>
|
||||
/// #DEBUG 获取所有用户
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Route("debug_list", Name = "debug_list")]
|
||||
[HttpGet]
|
||||
public ActionResult<UserItem[]> GetUsers()
|
||||
{
|
||||
return Ok(database.Users.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// #DEBUG 获取所有 token
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Route("debug_tokens", Name = "debug_tokens")]
|
||||
[HttpGet]
|
||||
public ActionResult<TokenItem[]> GetTokens()
|
||||
{
|
||||
return Ok(database.Tokens.ToArray());
|
||||
}
|
||||
//#endif
|
||||
}
|
||||
}
|
@ -36,4 +36,9 @@ public class FlowerDatabase : DbContext
|
||||
/// 会话令牌集
|
||||
/// </summary>
|
||||
public DbSet<TokenItem> Tokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 照片集
|
||||
/// </summary>
|
||||
public DbSet<PhotoItem> Photos { get; set; }
|
||||
}
|
||||
|
@ -19,12 +19,19 @@ public class FlowerItem
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所有人 uid
|
||||
/// 所有人 id
|
||||
/// </summary>
|
||||
[Column("uid")]
|
||||
[ForeignKey(nameof(Owner))]
|
||||
[Required]
|
||||
public int OwnerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所有人
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public UserItem? Owner { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 类别 id
|
||||
/// </summary>
|
||||
@ -59,12 +66,6 @@ public class FlowerItem
|
||||
[Column("purchase")]
|
||||
public string? Purchase { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 购买照片
|
||||
/// </summary>
|
||||
[Column("photo")]
|
||||
public byte[]? Photo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
@ -77,4 +78,9 @@ public class FlowerItem
|
||||
[NotMapped]
|
||||
[JsonIgnore]
|
||||
public DateTimeOffset DateBuy => DateTimeOffset.FromUnixTimeMilliseconds(DateBuyUnixTime);
|
||||
|
||||
/// <summary>
|
||||
/// 封面相关照片
|
||||
/// </summary>
|
||||
public ICollection<PhotoItem>? Photos { get; set; }
|
||||
}
|
||||
|
84
Server/Data/Model/PhotoItem.cs
Normal file
84
Server/Data/Model/PhotoItem.cs
Normal file
@ -0,0 +1,84 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Blahblah.FlowerStory.Server.Data.Model;
|
||||
|
||||
/// <summary>
|
||||
/// 照片对象
|
||||
/// </summary>
|
||||
[Table("photos")]
|
||||
public class PhotoItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 自增 id,主键
|
||||
/// </summary>
|
||||
[Column("pid")]
|
||||
[Key]
|
||||
[Required]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 关联花草 id
|
||||
/// </summary>
|
||||
[Column("fid")]
|
||||
[ForeignKey(nameof(Flower))]
|
||||
[Required]
|
||||
public int FlowerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 关联花草
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public FlowerItem? Flower { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 关联的事件 id
|
||||
/// </summary>
|
||||
[Column("rid")]
|
||||
[ForeignKey(nameof(Record))]
|
||||
[Required]
|
||||
public int RecordId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 关联的事件
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public RecordItem? Record { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图片类型
|
||||
/// </summary>
|
||||
[Column("filetype")]
|
||||
[Required]
|
||||
public required string FileType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件名
|
||||
/// </summary>
|
||||
[Column("filename")]
|
||||
[Required]
|
||||
public required string FileName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件路径
|
||||
/// </summary>
|
||||
[Column("path")]
|
||||
[Required]
|
||||
public required string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 上传时间
|
||||
/// </summary>
|
||||
[Column("dateupload")]
|
||||
[Required]
|
||||
[JsonPropertyName("dateUpload")]
|
||||
public long DateUploadUnixTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 上传时间
|
||||
/// </summary>
|
||||
[NotMapped]
|
||||
[JsonIgnore]
|
||||
public DateTimeOffset DateUpload => DateTimeOffset.FromUnixTimeMilliseconds(DateUploadUnixTime);
|
||||
}
|
@ -19,12 +19,33 @@ public class RecordItem
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 关联人 uid
|
||||
/// 关联人 id
|
||||
/// </summary>
|
||||
[Column("uid")]
|
||||
[ForeignKey(nameof(Owner))]
|
||||
[Required]
|
||||
public int OwnerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 关联人
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public UserItem? Owner { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 关联花草 id
|
||||
/// </summary>
|
||||
[Column("fid")]
|
||||
[ForeignKey(nameof(Flower))]
|
||||
[Required]
|
||||
public int FlowerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 关联花草
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public FlowerItem? Flower { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 事件类型
|
||||
/// </summary>
|
||||
@ -52,12 +73,6 @@ public class RecordItem
|
||||
[Column("byname")]
|
||||
public string? ByUserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 事件关联照片
|
||||
/// </summary>
|
||||
[Column("photo")]
|
||||
public byte[]? Photo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
@ -70,4 +85,9 @@ public class RecordItem
|
||||
[NotMapped]
|
||||
[JsonIgnore]
|
||||
public DateTimeOffset Date => DateTimeOffset.FromUnixTimeMilliseconds(DateUnixTime);
|
||||
|
||||
/// <summary>
|
||||
/// 事件关联照片
|
||||
/// </summary>
|
||||
public ICollection<PhotoItem>? Photos { get; set; }
|
||||
}
|
||||
|
300
Server/Migrations/20230525082941_AddPhotos.Designer.cs
generated
Normal file
300
Server/Migrations/20230525082941_AddPhotos.Designer.cs
generated
Normal file
@ -0,0 +1,300 @@
|
||||
// <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("20230525082941_AddPhotos")]
|
||||
partial class AddPhotos
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "7.0.5");
|
||||
|
||||
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<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<string>("Path")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("path");
|
||||
|
||||
b.Property<int>("RecordId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("rid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
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<long>("DateUnixTime")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("date")
|
||||
.HasAnnotation("Relational:JsonPropertyName", "date");
|
||||
|
||||
b.Property<int>("EventId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("eid");
|
||||
|
||||
b.Property<int>("FlowerId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("fid");
|
||||
|
||||
b.Property<string>("Memo")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("memo");
|
||||
|
||||
b.Property<int>("OwnerId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("uid");
|
||||
|
||||
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.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.RecordItem", "Record")
|
||||
.WithMany("Photo")
|
||||
.HasForeignKey("RecordId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
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.RecordItem", b =>
|
||||
{
|
||||
b.Navigation("Photo");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
143
Server/Migrations/20230525082941_AddPhotos.cs
Normal file
143
Server/Migrations/20230525082941_AddPhotos.cs
Normal file
@ -0,0 +1,143 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Blahblah.FlowerStory.Server.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddPhotos : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "photo",
|
||||
table: "records");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "photo",
|
||||
table: "flowers");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "fid",
|
||||
table: "records",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "photos",
|
||||
columns: table => new
|
||||
{
|
||||
pid = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
rid = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
filetype = table.Column<string>(type: "TEXT", nullable: false),
|
||||
filename = table.Column<string>(type: "TEXT", nullable: false),
|
||||
path = table.Column<string>(type: "TEXT", nullable: false),
|
||||
dateupload = table.Column<long>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_photos", x => x.pid);
|
||||
table.ForeignKey(
|
||||
name: "FK_photos_records_rid",
|
||||
column: x => x.rid,
|
||||
principalTable: "records",
|
||||
principalColumn: "rid",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_records_fid",
|
||||
table: "records",
|
||||
column: "fid");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_records_uid",
|
||||
table: "records",
|
||||
column: "uid");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_flowers_uid",
|
||||
table: "flowers",
|
||||
column: "uid");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_photos_rid",
|
||||
table: "photos",
|
||||
column: "rid");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_flowers_users_uid",
|
||||
table: "flowers",
|
||||
column: "uid",
|
||||
principalTable: "users",
|
||||
principalColumn: "uid",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_records_flowers_fid",
|
||||
table: "records",
|
||||
column: "fid",
|
||||
principalTable: "flowers",
|
||||
principalColumn: "fid",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_records_users_uid",
|
||||
table: "records",
|
||||
column: "uid",
|
||||
principalTable: "users",
|
||||
principalColumn: "uid",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_flowers_users_uid",
|
||||
table: "flowers");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_records_flowers_fid",
|
||||
table: "records");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_records_users_uid",
|
||||
table: "records");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "photos");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_records_fid",
|
||||
table: "records");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_records_uid",
|
||||
table: "records");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_flowers_uid",
|
||||
table: "flowers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "fid",
|
||||
table: "records");
|
||||
|
||||
migrationBuilder.AddColumn<byte[]>(
|
||||
name: "photo",
|
||||
table: "records",
|
||||
type: "BLOB",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<byte[]>(
|
||||
name: "photo",
|
||||
table: "flowers",
|
||||
type: "BLOB",
|
||||
nullable: true);
|
||||
}
|
||||
}
|
||||
}
|
319
Server/Migrations/20230525091254_AddPhotosFlowerForeignKey.Designer.cs
generated
Normal file
319
Server/Migrations/20230525091254_AddPhotosFlowerForeignKey.Designer.cs
generated
Normal file
@ -0,0 +1,319 @@
|
||||
// <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("20230525091254_AddPhotosFlowerForeignKey")]
|
||||
partial class AddPhotosFlowerForeignKey
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "7.0.5");
|
||||
|
||||
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<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<string>("Path")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("path");
|
||||
|
||||
b.Property<int>("RecordId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("rid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("FlowerId");
|
||||
|
||||
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<long>("DateUnixTime")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("date")
|
||||
.HasAnnotation("Relational:JsonPropertyName", "date");
|
||||
|
||||
b.Property<int>("EventId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("eid");
|
||||
|
||||
b.Property<int>("FlowerId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("fid");
|
||||
|
||||
b.Property<string>("Memo")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("memo");
|
||||
|
||||
b.Property<int>("OwnerId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("uid");
|
||||
|
||||
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.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")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Blahblah.FlowerStory.Server.Data.Model.RecordItem", "Record")
|
||||
.WithMany("Photos")
|
||||
.HasForeignKey("RecordId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Flower");
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Blahblah.FlowerStory.Server.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddPhotosFlowerForeignKey : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "fid",
|
||||
table: "photos",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_photos_fid",
|
||||
table: "photos",
|
||||
column: "fid");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_photos_flowers_fid",
|
||||
table: "photos",
|
||||
column: "fid",
|
||||
principalTable: "flowers",
|
||||
principalColumn: "fid",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_photos_flowers_fid",
|
||||
table: "photos");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_photos_fid",
|
||||
table: "photos");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "fid",
|
||||
table: "photos");
|
||||
}
|
||||
}
|
||||
}
|
@ -50,17 +50,59 @@ namespace Blahblah.FlowerStory.Server.Migrations
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("uid");
|
||||
|
||||
b.Property<byte[]>("Photo")
|
||||
.HasColumnType("BLOB")
|
||||
.HasColumnName("photo");
|
||||
|
||||
b.Property<string>("Purchase")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("purchase");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("flowers");
|
||||
b.HasIndex("OwnerId");
|
||||
|
||||
b.ToTable("flowers", (string)null);
|
||||
});
|
||||
|
||||
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<string>("Path")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("path");
|
||||
|
||||
b.Property<int>("RecordId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("rid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("FlowerId");
|
||||
|
||||
b.HasIndex("RecordId");
|
||||
|
||||
b.ToTable("photos", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.RecordItem", b =>
|
||||
@ -87,6 +129,10 @@ namespace Blahblah.FlowerStory.Server.Migrations
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("eid");
|
||||
|
||||
b.Property<int>("FlowerId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("fid");
|
||||
|
||||
b.Property<string>("Memo")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("memo");
|
||||
@ -95,13 +141,13 @@ namespace Blahblah.FlowerStory.Server.Migrations
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("uid");
|
||||
|
||||
b.Property<byte[]>("Photo")
|
||||
.HasColumnType("BLOB")
|
||||
.HasColumnName("photo");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("records");
|
||||
b.HasIndex("FlowerId");
|
||||
|
||||
b.HasIndex("OwnerId");
|
||||
|
||||
b.ToTable("records", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.TokenItem", b =>
|
||||
@ -151,7 +197,7 @@ namespace Blahblah.FlowerStory.Server.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("tokens");
|
||||
b.ToTable("tokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.UserItem", b =>
|
||||
@ -203,7 +249,66 @@ namespace Blahblah.FlowerStory.Server.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("users");
|
||||
b.ToTable("users", (string)null);
|
||||
});
|
||||
|
||||
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")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Blahblah.FlowerStory.Server.Data.Model.RecordItem", "Record")
|
||||
.WithMany("Photos")
|
||||
.HasForeignKey("RecordId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Flower");
|
||||
|
||||
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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user