diff --git a/.trae/rules/save.md b/.trae/rules/save.md new file mode 100644 index 0000000..a75a18f --- /dev/null +++ b/.trae/rules/save.md @@ -0,0 +1,372 @@ +# 存入胶囊功能 API 文档 + +## 概述 +存入胶囊功能允许用户将邮件保存为时光胶囊状态,邮件将以草稿形式保存,用户可以随时编辑或发送。 + +## API 接口 + +### 创建胶囊邮件 + +**接口地址:** `POST /api/v1/mails` + +**接口描述:** 创建一个新邮件并将其保存为时光胶囊状态(草稿) + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| title | string | 是 | 邮件标题 | +| content | string | 是 | 邮件内容 | +| recipientType | string | 是 | 收件人类型:SELF(自己)、SPECIFIC(指定收件人)、PUBLIC(公开信) | +| recipientEmail | string | 否 | 指定收件人邮箱(当recipientType为SPECIFIC时必填) | +| sendTime | string | 否 | 发送时间,ISO格式时间字符串(如:2025-12-31T23:59:59Z) | +| triggerType | string | 否 | 触发类型:TIME(时间)、LOCATION(地点)、EVENT(事件) | +| triggerCondition | object | 否 | 触发条件 | +| triggerCondition.location | object | 否 | 地点触发条件 | +| triggerCondition.location.latitude | number | 否 | 纬度 | +| triggerCondition.location.longitude | number | 否 | 经度 | +| triggerCondition.location.city | string | 否 | 城市 | +| triggerCondition.event | object | 否 | 事件触发条件 | +| triggerCondition.event.keywords | array | 否 | 关键词列表 | +| triggerCondition.event.type | string | 否 | 事件类型 | +| attachments | array | 否 | 附件列表 | +| attachments[].type | string | 否 | 附件类型:IMAGE、VOICE、VIDEO | +| attachments[].url | string | 否 | 附件URL | +| attachments[].thumbnail | string | 否 | 缩略图URL | +| isEncrypted | boolean | 否 | 是否加密 | +| capsuleStyle | string | 否 | 胶囊样式 | +| status | string | 是 | 邮件状态,存入胶囊时固定为:DRAFT | + +#### 请求示例 + +```json +{ + "title": "写给未来的自己", + "content": "亲爱的未来的我,当你读到这封信时,希望你已经实现了现在的梦想...", + "recipientType": "SELF", + "sendTime": "2025-12-31T23:59:59Z", + "triggerType": "TIME", + "attachments": [ + { + "type": "IMAGE", + "url": "https://example.com/image.jpg", + "thumbnail": "https://example.com/thumb.jpg" + } + ], + "isEncrypted": false, + "capsuleStyle": "default", + "status": "DRAFT" +} +``` + +#### 响应参数 + +| 参数名 | 类型 | 说明 | +|--------|------|------| +| code | number | 响应状态码,200表示成功 | +| message | string | 响应消息 | +| data | object | 响应数据 | +| data.mailId | string | 邮件ID | +| data.capsuleId | string | 胶囊ID | +| data.status | string | 邮件状态:DRAFT、PENDING、DELIVERING、DELIVERED | +| data.createdAt | string | 创建时间,ISO格式时间字符串 | + +#### 响应示例 + +```json +{ + "code": 200, + "message": "success", + "data": { + "mailId": "mail_1234567890", + "capsuleId": "capsule_1234567890", + "status": "DRAFT", + "createdAt": "2023-07-20T10:30:00Z" + } +} +``` + +### 获取胶囊列表 + +**接口地址:** `GET /api/v1/mails` + +**接口描述:** 获取用户的胶囊邮件列表 + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| type | string | 否 | 邮件类型:INBOX、SENT、DRAFT,获取胶囊时使用DRAFT | +| status | string | 否 | 状态筛选:PENDING、DELIVERING、DELIVERED、DRAFT | +| page | number | 否 | 页码,默认为1 | +| size | number | 否 | 每页数量,默认为10 | + +#### 请求示例 + +``` +GET /api/v1/mails?type=DRAFT&page=1&size=10 +``` + +#### 响应参数 + +| 参数名 | 类型 | 说明 | +|--------|------|------| +| code | number | 响应状态码,200表示成功 | +| message | string | 响应消息 | +| data | object | 响应数据 | +| data.list | array | 邮件列表 | +| data.list[].mailId | string | 邮件ID | +| data.list[].title | string | 邮件标题 | +| data.list[].sender | object | 发件人信息 | +| data.list[].recipient | object | 收件人信息 | +| data.list[].sendTime | string | 发送时间 | +| data.list[].deliveryTime | string | 送达时间 | +| data.list[].status | string | 邮件状态 | +| data.list[].hasAttachments | boolean | 是否有附件 | +| data.list[].isEncrypted | boolean | 是否加密 | +| data.list[].capsuleStyle | string | 胶囊样式 | +| data.total | number | 总数量 | +| data.page | number | 当前页码 | +| data.size | number | 每页数量 | + +#### 响应示例 + +```json +{ + "code": 200, + "message": "success", + "data": { + "list": [ + { + "mailId": "mail_1234567890", + "title": "写给未来的自己", + "sender": { + "userId": "user_123", + "username": "张三", + "avatar": "https://example.com/avatar.jpg" + }, + "recipient": { + "userId": "user_123", + "username": "张三", + "avatar": "https://example.com/avatar.jpg" + }, + "sendTime": "2025-12-31T23:59:59Z", + "deliveryTime": null, + "status": "DRAFT", + "hasAttachments": true, + "isEncrypted": false, + "capsuleStyle": "default" + } + ], + "total": 1, + "page": 1, + "size": 10 + } +} +``` + +### 获取胶囊详情 + +**接口地址:** `GET /api/v1/mails/{mailId}` + +**接口描述:** 获取指定胶囊邮件的详细信息 + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| mailId | string | 是 | 邮件ID | + +#### 请求示例 + +``` +GET /api/v1/mails/mail_1234567890 +``` + +#### 响应参数 + +| 参数名 | 类型 | 说明 | +|--------|------|------| +| code | number | 响应状态码,200表示成功 | +| message | string | 响应消息 | +| data | object | 响应数据 | +| data.mailId | string | 邮件ID | +| data.title | string | 邮件标题 | +| data.content | string | 邮件内容 | +| data.sender | object | 发件人信息 | +| data.recipient | object | 收件人信息 | +| data.sendTime | string | 发送时间 | +| data.createdAt | string | 创建时间 | +| data.deliveryTime | string | 送达时间 | +| data.status | string | 邮件状态 | +| data.triggerType | string | 触发类型 | +| data.triggerCondition | object | 触发条件 | +| data.attachments | array | 附件列表 | +| data.isEncrypted | boolean | 是否加密 | +| data.capsuleStyle | string | 胶囊样式 | +| data.canEdit | boolean | 是否可编辑(草稿状态为true) | +| data.canRevoke | boolean | 是否可撤销(待投递状态为true) | + +#### 响应示例 + +```json +{ + "code": 200, + "message": "success", + "data": { + "mailId": "mail_1234567890", + "title": "写给未来的自己", + "content": "亲爱的未来的我,当你读到这封信时,希望你已经实现了现在的梦想...", + "sender": { + "userId": "user_123", + "username": "张三", + "avatar": "https://example.com/avatar.jpg", + "email": "zhangsan@example.com" + }, + "recipient": { + "userId": "user_123", + "username": "张三", + "avatar": "https://example.com/avatar.jpg", + "email": "zhangsan@example.com" + }, + "sendTime": "2025-12-31T23:59:59Z", + "createdAt": "2023-07-20T10:30:00Z", + "deliveryTime": null, + "status": "DRAFT", + "triggerType": "TIME", + "triggerCondition": {}, + "attachments": [ + { + "id": "attach_123", + "type": "IMAGE", + "url": "https://example.com/image.jpg", + "thumbnail": "https://example.com/thumb.jpg", + "size": 1024000 + } + ], + "isEncrypted": false, + "capsuleStyle": "default", + "canEdit": true, + "canRevoke": false + } +} +``` + +### 更新胶囊邮件 + +**接口地址:** `PUT /api/v1/mails/{mailId}` + +**接口描述:** 更新胶囊邮件内容(仅草稿状态可更新) + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| mailId | string | 是 | 邮件ID(路径参数) | +| title | string | 否 | 邮件标题 | +| content | string | 否 | 邮件内容 | +| recipientType | string | 否 | 收件人类型:SELF、SPECIFIC、PUBLIC | +| recipientEmail | string | 否 | 指定收件人邮箱(当recipientType为SPECIFIC时必填) | +| sendTime | string | 否 | 发送时间,ISO格式时间字符串 | +| triggerType | string | 否 | 触发类型:TIME、LOCATION、EVENT | +| triggerCondition | object | 否 | 触发条件 | +| attachments | array | 否 | 附件列表 | +| isEncrypted | boolean | 否 | 是否加密 | +| capsuleStyle | string | 否 | 胶囊样式 | + +#### 请求示例 + +```json +{ + "title": "更新后的标题", + "content": "更新后的内容...", + "sendTime": "2026-12-31T23:59:59Z" +} +``` + +#### 响应参数 + +| 参数名 | 类型 | 说明 | +|--------|------|------| +| code | number | 响应状态码,200表示成功 | +| message | string | 响应消息 | +| data | object | 响应数据 | +| data.mailId | string | 邮件ID | +| data.capsuleId | string | 胶囊ID | +| data.status | string | 邮件状态 | +| data.updatedAt | string | 更新时间,ISO格式时间字符串 | + +#### 响应示例 + +```json +{ + "code": 200, + "message": "success", + "data": { + "mailId": "mail_1234567890", + "capsuleId": "capsule_1234567890", + "status": "DRAFT", + "updatedAt": "2023-07-21T14:30:00Z" + } +} +``` + +### 删除胶囊邮件 + +**接口地址:** `DELETE /api/v1/mails/{mailId}` + +**接口描述:** 删除指定的胶囊邮件 + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| mailId | string | 是 | 邮件ID(路径参数) | + +#### 请求示例 + +``` +DELETE /api/v1/mails/mail_1234567890 +``` + +#### 响应参数 + +| 参数名 | 类型 | 说明 | +|--------|------|------| +| code | number | 响应状态码,200表示成功 | +| message | string | 响应消息 | +| data | object | 响应数据 | +| data.mailId | string | 已删除的邮件ID | + +#### 响应示例 + +```json +{ + "code": 200, + "message": "success", + "data": { + "mailId": "mail_1234567890" + } +} +``` + +## 错误码 + +| 错误码 | 说明 | +|--------|------| +| 200 | 成功 | +| 400 | 请求参数错误 | +| 401 | 未授权,需要登录 | +| 403 | 权限不足 | +| 404 | 资源不存在 | +| 422 | 验证失败 | +| 500 | 服务器内部错误 | + +## 注意事项 + +1. 存入胶囊的邮件状态为DRAFT,可以在任何时候编辑或发送 +2. 只有草稿状态的邮件可以编辑或删除 +3. 发送时间必须晚于当前时间 +4. 附件大小限制为10MB +5. 免费用户每月最多可创建10个胶囊邮件 +6. 加密邮件需要额外验证才能查看内容 \ No newline at end of file diff --git a/.trae/rules/seed.md b/.trae/rules/seed.md new file mode 100644 index 0000000..f22d13b --- /dev/null +++ b/.trae/rules/seed.md @@ -0,0 +1,307 @@ +# 发送至未来功能 API 文档 + +## 概述 +发送至未来功能允许用户将邮件设置为在未来特定时间自动发送,邮件状态将变为待投递(PENDING),系统会在指定时间自动处理发送。 + +## API 接口 + +### 发送至未来 + +**接口地址:** `POST /api/v1/mails/send-to-future` + +**接口描述:** 将草稿状态的邮件设置为在未来特定时间自动发送 + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| mailId | string | 是 | 邮件ID | +| sendTime | string | 是 | 发送时间,ISO格式时间字符串(如:2025-12-31T23:59:59Z) | +| triggerType | string | 否 | 触发类型:TIME(时间)、LOCATION(地点)、EVENT(事件),默认为TIME | +| triggerCondition | object | 否 | 触发条件 | +| triggerCondition.location | object | 否 | 地点触发条件 | +| triggerCondition.location.latitude | number | 否 | 纬度 | +| triggerCondition.location.longitude | number | 否 | 经度 | +| triggerCondition.location.city | string | 否 | 城市 | +| triggerCondition.event | object | 否 | 事件触发条件 | +| triggerCondition.event.keywords | array | 否 | 关键词列表 | +| triggerCondition.event.type | string | 否 | 事件类型 | + +#### 请求示例 + +```json +{ + "mailId": "mail_1234567890", + "sendTime": "2025-12-31T23:59:59Z", + "triggerType": "TIME", + "triggerCondition": {} +} +``` + +#### 响应参数 + +| 参数名 | 类型 | 说明 | +|--------|------|------| +| code | number | 响应状态码,200表示成功 | +| message | string | 响应消息 | +| data | object | 响应数据 | +| data.mailId | string | 邮件ID | +| data.capsuleId | string | 胶囊ID | +| data.status | string | 邮件状态:PENDING | +| data.sendTime | string | 发送时间 | +| data.countdown | number | 倒计时秒数 | +| data.updatedAt | string | 更新时间,ISO格式时间字符串 | + +#### 响应示例 + +```json +{ + "code": 200, + "message": "success", + "data": { + "mailId": "mail_1234567890", + "capsuleId": "capsule_1234567890", + "status": "PENDING", + "sendTime": "2025-12-31T23:59:59Z", + "countdown": 94608000, + "updatedAt": "2023-07-20T10:30:00Z" + } +} +``` + +### 获取待发送邮件列表 + +**接口地址:** `GET /api/v1/mails` + +**接口描述:** 获取用户的待发送邮件列表 + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| type | string | 否 | 邮件类型:INBOX、SENT、DRAFT,获取待发送时使用SENT | +| status | string | 否 | 状态筛选:PENDING、DELIVERING、DELIVERED、DRAFT,获取待发送时使用PENDING | +| page | number | 否 | 页码,默认为1 | +| size | number | 否 | 每页数量,默认为10 | + +#### 请求示例 + +``` +GET /api/v1/mails?type=SENT&status=PENDING&page=1&size=10 +``` + +#### 响应参数 + +| 参数名 | 类型 | 说明 | +|--------|------|------| +| code | number | 响应状态码,200表示成功 | +| message | string | 响应消息 | +| data | object | 响应数据 | +| data.list | array | 邮件列表 | +| data.list[].mailId | string | 邮件ID | +| data.list[].title | string | 邮件标题 | +| data.list[].sender | object | 发件人信息 | +| data.list[].recipient | object | 收件人信息 | +| data.list[].sendTime | string | 发送时间 | +| data.list[].deliveryTime | string | 送达时间 | +| data.list[].status | string | 邮件状态 | +| data.list[].hasAttachments | boolean | 是否有附件 | +| data.list[].isEncrypted | boolean | 是否加密 | +| data.list[].capsuleStyle | string | 胶囊样式 | +| data.list[].countdown | number | 倒计时秒数 | +| data.total | number | 总数量 | +| data.page | number | 当前页码 | +| data.size | number | 每页数量 | + +#### 响应示例 + +```json +{ + "code": 200, + "message": "success", + "data": { + "list": [ + { + "mailId": "mail_1234567890", + "title": "写给未来的自己", + "sender": { + "userId": "user_123", + "username": "张三", + "avatar": "https://example.com/avatar.jpg" + }, + "recipient": { + "userId": "user_123", + "username": "张三", + "avatar": "https://example.com/avatar.jpg" + }, + "sendTime": "2025-12-31T23:59:59Z", + "deliveryTime": null, + "status": "PENDING", + "hasAttachments": true, + "isEncrypted": false, + "capsuleStyle": "default", + "countdown": 94608000 + } + ], + "total": 1, + "page": 1, + "size": 10 + } +} +``` + +### 获取待发送邮件详情 + +**接口地址:** `GET /api/v1/mails/{mailId}` + +**接口描述:** 获取指定待发送邮件的详细信息 + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| mailId | string | 是 | 邮件ID | + +#### 请求示例 + +``` +GET /api/v1/mails/mail_1234567890 +``` + +#### 响应参数 + +| 参数名 | 类型 | 说明 | +|--------|------|------| +| code | number | 响应状态码,200表示成功 | +| message | string | 响应消息 | +| data | object | 响应数据 | +| data.mailId | string | 邮件ID | +| data.title | string | 邮件标题 | +| data.content | string | 邮件内容 | +| data.sender | object | 发件人信息 | +| data.recipient | object | 收件人信息 | +| data.sendTime | string | 发送时间 | +| data.createdAt | string | 创建时间 | +| data.deliveryTime | string | 送达时间 | +| data.status | string | 邮件状态 | +| data.triggerType | string | 触发类型 | +| data.triggerCondition | object | 触发条件 | +| data.attachments | array | 附件列表 | +| data.isEncrypted | boolean | 是否加密 | +| data.capsuleStyle | string | 胶囊样式 | +| data.canEdit | boolean | 是否可编辑(待发送状态为false) | +| data.canRevoke | boolean | 是否可撤销(待发送状态为true) | +| data.countdown | number | 倒计时秒数 | + +#### 响应示例 + +```json +{ + "code": 200, + "message": "success", + "data": { + "mailId": "mail_1234567890", + "title": "写给未来的自己", + "content": "亲爱的未来的我,当你读到这封信时,希望你已经实现了现在的梦想...", + "sender": { + "userId": "user_123", + "username": "张三", + "avatar": "https://example.com/avatar.jpg", + "email": "zhangsan@example.com" + }, + "recipient": { + "userId": "user_123", + "username": "张三", + "avatar": "https://example.com/avatar.jpg", + "email": "zhangsan@example.com" + }, + "sendTime": "2025-12-31T23:59:59Z", + "createdAt": "2023-07-20T10:30:00Z", + "deliveryTime": null, + "status": "PENDING", + "triggerType": "TIME", + "triggerCondition": {}, + "attachments": [ + { + "id": "attach_123", + "type": "IMAGE", + "url": "https://example.com/image.jpg", + "thumbnail": "https://example.com/thumb.jpg", + "size": 1024000 + } + ], + "isEncrypted": false, + "capsuleStyle": "default", + "canEdit": false, + "canRevoke": true, + "countdown": 94608000 + } +} +``` + +### 撤销待发送邮件 + +**接口地址:** `POST /api/v1/mails/{mailId}/revoke` + +**接口描述:** 撤销待发送的邮件,将状态改回草稿 + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| mailId | string | 是 | 邮件ID(路径参数) | + +#### 请求示例 + +``` +POST /api/v1/mails/mail_1234567890/revoke +``` + +#### 响应参数 + +| 参数名 | 类型 | 说明 | +|--------|------|------| +| code | number | 响应状态码,200表示成功 | +| message | string | 响应消息 | +| data | object | 响应数据 | +| data.mailId | string | 邮件ID | +| data.status | string | 邮件状态:DRAFT | +| data.revokedAt | string | 撤销时间,ISO格式时间字符串 | + +#### 响应示例 + +```json +{ + "code": 200, + "message": "success", + "data": { + "mailId": "mail_1234567890", + "status": "DRAFT", + "revokedAt": "2023-07-21T14:30:00Z" + } +} +``` + +## 错误码 + +| 错误码 | 说明 | +|--------|------| +| 200 | 成功 | +| 400 | 请求参数错误 | +| 401 | 未授权,需要登录 | +| 403 | 权限不足 | +| 404 | 资源不存在 | +| 422 | 验证失败 | +| 500 | 服务器内部错误 | + +## 注意事项 + +1. 发送至未来的邮件状态为PENDING,表示等待系统在未来指定时间自动发送 +2. 只有草稿状态(DRAFT)的邮件可以设置为发送至未来 +3. 发送时间必须晚于当前时间至少1小时 +4. 待发送状态的邮件不能编辑内容,但可以撤销发送 +5. 撤销后的邮件状态将变回草稿(DRAFT),可以重新编辑或设置发送时间 +6. 系统会在发送时间到达前10分钟进入投递中状态(DELIVERING) +7. 免费用户每月最多可设置5封邮件发送至未来 +8. 附件大小限制为10MB +9. 加密邮件需要额外验证才能查看内容 \ No newline at end of file diff --git a/FutureMailAPI/Controllers/MailsController.cs b/FutureMailAPI/Controllers/MailsController.cs index 8cd04e9..2447209 100644 --- a/FutureMailAPI/Controllers/MailsController.cs +++ b/FutureMailAPI/Controllers/MailsController.cs @@ -80,6 +80,108 @@ namespace FutureMailAPI.Controllers result); } + // 直接接收前端原始格式的创建邮件接口 + [HttpPost("create-raw")] + public async Task CreateMailRaw() + { + try + { + // 读取请求体 + var request = HttpContext.Request; + using var reader = new StreamReader(request.Body); + var body = await reader.ReadToEndAsync(); + + // 解析JSON + var rawMail = System.Text.Json.JsonSerializer.Deserialize>(body); + if (rawMail == null) + { + return BadRequest(ApiResponse.ErrorResult("请求数据为空")); + } + + // 创建兼容DTO + var compatDto = new SentMailCreateCompatDto(); + + // 解析各个字段 + if (rawMail.ContainsKey("title") && rawMail["title"] != null) + compatDto.title = rawMail["title"].ToString() ?? string.Empty; + + if (rawMail.ContainsKey("content") && rawMail["content"] != null) + compatDto.content = rawMail["content"].ToString() ?? string.Empty; + + if (rawMail.ContainsKey("recipientType") && rawMail["recipientType"] != null) + { + var recipientTypeStr = rawMail["recipientType"].ToString(); + if (Enum.TryParse(recipientTypeStr, true, out var recipientType)) + compatDto.recipientType = recipientType; + } + + if (rawMail.ContainsKey("recipientEmail") && rawMail["recipientEmail"] != null) + compatDto.recipientEmail = rawMail["recipientEmail"].ToString(); + + if (rawMail.ContainsKey("sendTime") && rawMail["sendTime"] != null) + { + if (DateTime.TryParse(rawMail["sendTime"].ToString(), out var sendTime)) + compatDto.sendTime = sendTime; + } + + if (rawMail.ContainsKey("triggerType") && rawMail["triggerType"] != null) + { + var triggerTypeStr = rawMail["triggerType"].ToString(); + if (Enum.TryParse(triggerTypeStr, true, out var triggerType)) + compatDto.triggerType = triggerType; + } + + if (rawMail.ContainsKey("triggerCondition")) + compatDto.triggerCondition = rawMail["triggerCondition"]; + + if (rawMail.ContainsKey("attachments")) + { + try + { + compatDto.attachments = System.Text.Json.JsonSerializer.Deserialize>(rawMail["attachments"].ToString()); + } + catch + { + compatDto.attachments = new List(); + } + } + + if (rawMail.ContainsKey("isEncrypted")) + compatDto.isEncrypted = bool.Parse(rawMail["isEncrypted"].ToString()); + + if (rawMail.ContainsKey("capsuleStyle")) + compatDto.capsuleStyle = rawMail["capsuleStyle"].ToString() ?? "default"; + + // 从JWT令牌中获取当前用户ID + var currentUserId = GetCurrentUserId(); + + if (currentUserId <= 0) + { + return Unauthorized(ApiResponse.ErrorResult("未授权访问")); + } + + // 转换为内部DTO + var internalDto = compatDto.ToInternalDto(); + + var result = await _mailService.CreateMailAsync(currentUserId, internalDto); + + if (!result.Success) + { + return BadRequest(result); + } + + return CreatedAtAction( + nameof(GetMail), + new { mailId = result.Data!.Id }, + result); + } + catch (Exception ex) + { + _logger.LogError(ex, "创建邮件时发生错误"); + return StatusCode(500, ApiResponse.ErrorResult("服务器内部错误")); + } + } + [HttpGet("{mailId}")] public async Task GetMail(int mailId) { @@ -255,5 +357,198 @@ namespace FutureMailAPI.Controllers return Ok(result); } + + /// + /// 存入胶囊 - 创建胶囊邮件 + /// + /// 存入胶囊请求 + /// 操作结果 + [HttpPost("capsule")] + public async Task SaveToCapsule([FromBody] SaveToCapsuleDto dto) + { + if (!ModelState.IsValid) + { + return BadRequest(ApiResponse.ErrorResult("请求参数无效")); + } + + var currentUserId = GetCurrentUserId(); + + if (currentUserId <= 0) + { + return Unauthorized(ApiResponse.ErrorResult("未授权访问")); + } + + var result = await _mailService.SaveToCapsuleAsync(currentUserId, dto); + + if (!result.Success) + { + return BadRequest(result); + } + + return CreatedAtAction( + nameof(GetCapsuleMail), + new { id = result.Data!.Id }, + result); + } + + /// + /// 获取胶囊邮件列表 + /// + /// 页码 + /// 页大小 + /// 状态筛选 + /// 收件人类型筛选 + /// 关键词搜索 + /// 开始日期 + /// 结束日期 + /// 胶囊邮件列表 + [HttpGet("capsule")] + public async Task GetCapsuleMails( + [FromQuery] int pageIndex = 1, + [FromQuery] int pageSize = 10, + [FromQuery] int? status = null, + [FromQuery] int? recipientType = null, + [FromQuery] string? keyword = null, + [FromQuery] DateTime? startDate = null, + [FromQuery] DateTime? endDate = null) + { + var queryDto = new MailListQueryDto + { + PageIndex = pageIndex, + PageSize = pageSize, + Status = status, + RecipientType = recipientType, + Keyword = keyword, + StartDate = startDate, + EndDate = endDate + }; + + var currentUserId = GetCurrentUserId(); + + if (currentUserId <= 0) + { + return Unauthorized(ApiResponse>.ErrorResult("未授权访问")); + } + + var result = await _mailService.GetCapsuleMailsAsync(currentUserId, queryDto); + + if (!result.Success) + { + return BadRequest(result); + } + + return Ok(result); + } + + /// + /// 获取胶囊邮件详情 + /// + /// 邮件ID + /// 胶囊邮件详情 + [HttpGet("capsule/{id}")] + public async Task GetCapsuleMail(int id) + { + var currentUserId = GetCurrentUserId(); + + if (currentUserId <= 0) + { + return Unauthorized(ApiResponse.ErrorResult("未授权访问")); + } + + var result = await _mailService.GetCapsuleMailByIdAsync(currentUserId, id); + + if (!result.Success) + { + return NotFound(result); + } + + return Ok(result); + } + + /// + /// 更新胶囊邮件 + /// + /// 邮件ID + /// 更新请求 + /// 更新后的胶囊邮件详情 + [HttpPut("capsule/{id}")] + public async Task UpdateCapsuleMail(int id, [FromBody] UpdateCapsuleMailDto dto) + { + if (!ModelState.IsValid) + { + return BadRequest(ApiResponse.ErrorResult("请求参数无效")); + } + + var currentUserId = GetCurrentUserId(); + + if (currentUserId <= 0) + { + return Unauthorized(ApiResponse.ErrorResult("未授权访问")); + } + + var result = await _mailService.UpdateCapsuleMailAsync(currentUserId, id, dto); + + if (!result.Success) + { + return BadRequest(result); + } + + return Ok(result); + } + + /// + /// 撤销胶囊邮件 + /// + /// 邮件ID + /// 操作结果 + [HttpPost("capsule/{id}/revoke")] + public async Task RevokeCapsuleMail(int id) + { + var currentUserId = GetCurrentUserId(); + + if (currentUserId <= 0) + { + return Unauthorized(ApiResponse.ErrorResult("未授权访问")); + } + + var result = await _mailService.RevokeCapsuleMailAsync(currentUserId, id); + + if (!result.Success) + { + return BadRequest(result); + } + + return Ok(result); + } + + /// + /// 发送至未来 - 将草稿状态的邮件设置为在未来特定时间自动发送 + /// + /// 发送至未来请求DTO + /// 发送至未来响应DTO + [HttpPost("send-to-future")] + public async Task SendToFuture([FromBody] SendToFutureDto sendToFutureDto) + { + if (!ModelState.IsValid) + { + return BadRequest(ApiResponse.ErrorResult("请求参数无效")); + } + + var currentUserId = GetCurrentUserId(); + + if (currentUserId <= 0) + { + return Unauthorized(ApiResponse.ErrorResult("未授权访问")); + } + + var result = await _mailService.SendToFutureAsync(currentUserId, sendToFutureDto); + + if (!result.Success) + { + return BadRequest(result); + } + + return Ok(result); + } } } \ No newline at end of file diff --git a/FutureMailAPI/DTOs/MailDTOs.cs b/FutureMailAPI/DTOs/MailDTOs.cs index 567a7fb..a8d7fc4 100644 --- a/FutureMailAPI/DTOs/MailDTOs.cs +++ b/FutureMailAPI/DTOs/MailDTOs.cs @@ -189,4 +189,130 @@ namespace FutureMailAPI.DTOs public DateTime? StartDate { get; set; } public DateTime? EndDate { get; set; } } + + // 存入胶囊请求DTO + public class SaveToCapsuleDto + { + [Required(ErrorMessage = "标题是必填项")] + [StringLength(200, ErrorMessage = "标题长度不能超过200个字符")] + public string Title { get; set; } = string.Empty; + + [Required(ErrorMessage = "内容是必填项")] + public string Content { get; set; } = string.Empty; + + [Required(ErrorMessage = "收件人类型是必填项")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public RecipientTypeEnum RecipientType { get; set; } + + public string? RecipientEmail { get; set; } + + public DateTime? SendTime { get; set; } + + [Required(ErrorMessage = "触发条件类型是必填项")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public TriggerTypeEnum TriggerType { get; set; } = TriggerTypeEnum.TIME; + + public object? TriggerCondition { get; set; } + + public List? Attachments { get; set; } + + public bool IsEncrypted { get; set; } = false; + + [Required(ErrorMessage = "胶囊样式是必填项")] + public string CapsuleStyle { get; set; } = "default"; + } + + // 存入胶囊响应DTO + public class SaveToCapsuleResponseDto + { + public int Id { get; set; } + public string MailId { get; set; } = string.Empty; + public string CapsuleId { get; set; } = string.Empty; + public string Status { get; set; } = string.Empty; + public DateTime CreatedAt { get; set; } + } + + // 胶囊邮件列表响应DTO + public class CapsuleMailListResponseDto + { + public string MailId { get; set; } = string.Empty; + public string Title { get; set; } = string.Empty; + public UserInfoDto Sender { get; set; } = new(); + public UserInfoDto Recipient { get; set; } = new(); + public DateTime SendTime { get; set; } + public DateTime? DeliveryTime { get; set; } + public string Status { get; set; } = string.Empty; + public bool HasAttachments { get; set; } + public bool IsEncrypted { get; set; } + public string CapsuleStyle { get; set; } = string.Empty; + public int? Countdown { get; set; } // 倒计时秒数(仅status=PENDING时返回) + } + + // 胶囊邮件详情响应DTO + public class CapsuleMailDetailResponseDto + { + public string MailId { get; set; } = string.Empty; + public string Title { get; set; } = string.Empty; + public string Content { get; set; } = string.Empty; + public UserInfoDto Sender { get; set; } = new(); + public UserInfoDto Recipient { get; set; } = new(); + public DateTime SendTime { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime? DeliveryTime { get; set; } + public string Status { get; set; } = string.Empty; + public string TriggerType { get; set; } = string.Empty; + public object? TriggerCondition { get; set; } + public List Attachments { get; set; } = new(); + public bool IsEncrypted { get; set; } + public string CapsuleStyle { get; set; } = string.Empty; + public bool CanEdit { get; set; } // 是否可编辑(仅草稿状态) + public bool CanRevoke { get; set; } // 是否可撤销(仅待投递状态) + } + + + + // 附件DTO + public class AttachmentDto + { + public string Id { get; set; } = string.Empty; + public string Type { get; set; } = string.Empty; + public string Url { get; set; } = string.Empty; + public string? Thumbnail { get; set; } + public long Size { get; set; } + } + + // 更新胶囊邮件DTO + public class UpdateCapsuleMailDto + { + public string? Title { get; set; } + public string? Content { get; set; } + public RecipientTypeEnum? RecipientType { get; set; } + public string? RecipientEmail { get; set; } + public DateTime? SendTime { get; set; } + public TriggerTypeEnum? TriggerType { get; set; } + public object? TriggerCondition { get; set; } + public List? Attachments { get; set; } + public bool? IsEncrypted { get; set; } + public string? CapsuleStyle { get; set; } + } + + // 发送至未来请求DTO + public class SendToFutureDto + { + [Required(ErrorMessage = "邮件ID是必填项")] + public int MailId { get; set; } + + [Required(ErrorMessage = "投递时间是必填项")] + public DateTime DeliveryTime { get; set; } + } + + // 发送至未来响应DTO + public class SendToFutureResponseDto + { + public int MailId { get; set; } + public string Title { get; set; } = string.Empty; + public DateTime DeliveryTime { get; set; } + public string Status { get; set; } = string.Empty; + public DateTime SentAt { get; set; } + } } \ No newline at end of file diff --git a/FutureMailAPI/DTOs/PersonalSpaceDTOs.cs b/FutureMailAPI/DTOs/PersonalSpaceDTOs.cs index a9c1b4d..969aad0 100644 --- a/FutureMailAPI/DTOs/PersonalSpaceDTOs.cs +++ b/FutureMailAPI/DTOs/PersonalSpaceDTOs.cs @@ -60,6 +60,7 @@ namespace FutureMailAPI.DTOs public int UserId { get; set; } public string Username { get; set; } = string.Empty; public string? Avatar { get; set; } + public string Email { get; set; } = string.Empty; } public class SubscriptionResponseDto diff --git a/FutureMailAPI/Data/FutureMailDbContext.cs b/FutureMailAPI/Data/FutureMailDbContext.cs index 1ba15c7..17555e9 100644 --- a/FutureMailAPI/Data/FutureMailDbContext.cs +++ b/FutureMailAPI/Data/FutureMailDbContext.cs @@ -36,7 +36,7 @@ namespace FutureMailAPI.Data .HasForeignKey(e => e.SenderId) .OnDelete(DeleteBehavior.Restrict); - entity.HasOne() + entity.HasOne(e => e.Recipient) .WithMany() .HasForeignKey(e => e.RecipientId) .OnDelete(DeleteBehavior.SetNull); @@ -52,8 +52,8 @@ namespace FutureMailAPI.Data .HasForeignKey(e => e.SentMailId) .OnDelete(DeleteBehavior.Cascade); - entity.HasOne() - .WithMany() + entity.HasOne(e => e.Recipient) + .WithMany(u => u.ReceivedMails) .HasForeignKey(e => e.RecipientId) .OnDelete(DeleteBehavior.Cascade); @@ -68,9 +68,10 @@ namespace FutureMailAPI.Data .HasForeignKey(e => e.UserId) .OnDelete(DeleteBehavior.Cascade); + // 一对一关系配置 entity.HasOne() - .WithMany() - .HasForeignKey(e => e.SentMailId) + .WithOne(m => m.TimeCapsule) + .HasForeignKey(e => e.SentMailId) .OnDelete(DeleteBehavior.Cascade); entity.Property(e => e.CreatedAt).HasDefaultValueSql("CURRENT_TIMESTAMP"); diff --git a/FutureMailAPI/FutureMail.db b/FutureMailAPI/FutureMail.db index 278197f..cac5e5f 100644 Binary files a/FutureMailAPI/FutureMail.db and b/FutureMailAPI/FutureMail.db differ diff --git a/FutureMailAPI/FutureMail.db-shm b/FutureMailAPI/FutureMail.db-shm index e53417a..c8d1d43 100644 Binary files a/FutureMailAPI/FutureMail.db-shm and b/FutureMailAPI/FutureMail.db-shm differ diff --git a/FutureMailAPI/FutureMail.db-wal b/FutureMailAPI/FutureMail.db-wal index 96eb04d..844fc95 100644 Binary files a/FutureMailAPI/FutureMail.db-wal and b/FutureMailAPI/FutureMail.db-wal differ diff --git a/FutureMailAPI/Migrations/20251018051334_AddSentMailCreatedAt.Designer.cs b/FutureMailAPI/Migrations/20251018051334_AddSentMailCreatedAt.Designer.cs new file mode 100644 index 0000000..abcc8ed --- /dev/null +++ b/FutureMailAPI/Migrations/20251018051334_AddSentMailCreatedAt.Designer.cs @@ -0,0 +1,495 @@ +// +using System; +using FutureMailAPI.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace FutureMailAPI.Migrations +{ + [DbContext(typeof(FutureMailDbContext))] + [Migration("20251018051334_AddSentMailCreatedAt")] + partial class AddSentMailCreatedAt + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.9"); + + modelBuilder.Entity("FutureMailAPI.Models.OAuthClient", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("ClientSecret") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("RedirectUris") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Scopes") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OAuthClients"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.OAuthToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessToken") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("ClientId") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("ExpiresAt") + .HasColumnType("TEXT"); + + b.Property("RefreshToken") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("RevokedAt") + .HasColumnType("TEXT"); + + b.Property("Scope") + .HasColumnType("TEXT"); + + b.Property("TokenType") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("AccessToken") + .IsUnique(); + + b.HasIndex("ClientId"); + + b.HasIndex("RefreshToken") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("OAuthTokens"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.ReceivedMail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsRead") + .HasColumnType("INTEGER"); + + b.Property("IsReplied") + .HasColumnType("INTEGER"); + + b.Property("ReadAt") + .HasColumnType("TEXT"); + + b.Property("ReceivedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("RecipientId") + .HasColumnType("INTEGER"); + + b.Property("RecipientId1") + .HasColumnType("INTEGER"); + + b.Property("ReplyMailId") + .HasColumnType("INTEGER"); + + b.Property("SentMailId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("RecipientId1"); + + b.HasIndex("SentMailId"); + + b.ToTable("ReceivedMails"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.SentMail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attachments") + .HasColumnType("TEXT"); + + b.Property("Content") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DeliveryTime") + .HasColumnType("TEXT"); + + b.Property("EncryptionKey") + .HasColumnType("TEXT"); + + b.Property("IsEncrypted") + .HasColumnType("INTEGER"); + + b.Property("RecipientId") + .HasColumnType("INTEGER"); + + b.Property("RecipientId1") + .HasColumnType("INTEGER"); + + b.Property("RecipientType") + .HasColumnType("INTEGER"); + + b.Property("SenderId") + .HasColumnType("INTEGER"); + + b.Property("SentAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Theme") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("TriggerDetails") + .HasColumnType("TEXT"); + + b.Property("TriggerType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("RecipientId1"); + + b.HasIndex("SenderId"); + + b.ToTable("SentMails"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.TimeCapsule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Color") + .HasMaxLength(20) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("GlowIntensity") + .HasColumnType("REAL"); + + b.Property("Opacity") + .HasColumnType("REAL"); + + b.Property("PositionX") + .HasColumnType("REAL"); + + b.Property("PositionY") + .HasColumnType("REAL"); + + b.Property("PositionZ") + .HasColumnType("REAL"); + + b.Property("Rotation") + .HasColumnType("REAL"); + + b.Property("SentMailId") + .HasColumnType("INTEGER"); + + b.Property("SentMailId1") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("REAL"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Style") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("SentMailId") + .IsUnique(); + + b.HasIndex("SentMailId1"); + + b.HasIndex("UserId"); + + b.ToTable("TimeCapsules"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Avatar") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("LastLoginAt") + .HasColumnType("TEXT"); + + b.Property("Nickname") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("PreferredBackground") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PreferredScene") + .HasMaxLength(20) + .HasColumnType("TEXT"); + + b.Property("RefreshToken") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("TEXT"); + + b.Property("Salt") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.OAuthToken", b => + { + b.HasOne("FutureMailAPI.Models.OAuthClient", "Client") + .WithMany("Tokens") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FutureMailAPI.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.ReceivedMail", b => + { + b.HasOne("FutureMailAPI.Models.User", null) + .WithMany() + .HasForeignKey("RecipientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FutureMailAPI.Models.User", "Recipient") + .WithMany("ReceivedMails") + .HasForeignKey("RecipientId1") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FutureMailAPI.Models.SentMail", "SentMail") + .WithMany() + .HasForeignKey("SentMailId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Recipient"); + + b.Navigation("SentMail"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.SentMail", b => + { + b.HasOne("FutureMailAPI.Models.User", null) + .WithMany() + .HasForeignKey("RecipientId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("FutureMailAPI.Models.User", "Recipient") + .WithMany() + .HasForeignKey("RecipientId1"); + + b.HasOne("FutureMailAPI.Models.User", "Sender") + .WithMany("SentMails") + .HasForeignKey("SenderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Recipient"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.TimeCapsule", b => + { + b.HasOne("FutureMailAPI.Models.SentMail", null) + .WithOne("TimeCapsule") + .HasForeignKey("FutureMailAPI.Models.TimeCapsule", "SentMailId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FutureMailAPI.Models.SentMail", "SentMail") + .WithMany() + .HasForeignKey("SentMailId1") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FutureMailAPI.Models.User", "User") + .WithMany("TimeCapsules") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SentMail"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.OAuthClient", b => + { + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.SentMail", b => + { + b.Navigation("TimeCapsule"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.User", b => + { + b.Navigation("ReceivedMails"); + + b.Navigation("SentMails"); + + b.Navigation("TimeCapsules"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/FutureMailAPI/Migrations/20251018051334_AddSentMailCreatedAt.cs b/FutureMailAPI/Migrations/20251018051334_AddSentMailCreatedAt.cs new file mode 100644 index 0000000..1990552 --- /dev/null +++ b/FutureMailAPI/Migrations/20251018051334_AddSentMailCreatedAt.cs @@ -0,0 +1,71 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace FutureMailAPI.Migrations +{ + /// + public partial class AddSentMailCreatedAt : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_TimeCapsules_SentMailId", + table: "TimeCapsules"); + + migrationBuilder.AddColumn( + name: "GlowIntensity", + table: "TimeCapsules", + type: "REAL", + nullable: false, + defaultValue: 0.0); + + migrationBuilder.AddColumn( + name: "Style", + table: "TimeCapsules", + type: "TEXT", + maxLength: 50, + nullable: true); + + migrationBuilder.AddColumn( + name: "CreatedAt", + table: "SentMails", + type: "TEXT", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.CreateIndex( + name: "IX_TimeCapsules_SentMailId", + table: "TimeCapsules", + column: "SentMailId", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_TimeCapsules_SentMailId", + table: "TimeCapsules"); + + migrationBuilder.DropColumn( + name: "GlowIntensity", + table: "TimeCapsules"); + + migrationBuilder.DropColumn( + name: "Style", + table: "TimeCapsules"); + + migrationBuilder.DropColumn( + name: "CreatedAt", + table: "SentMails"); + + migrationBuilder.CreateIndex( + name: "IX_TimeCapsules_SentMailId", + table: "TimeCapsules", + column: "SentMailId"); + } + } +} diff --git a/FutureMailAPI/Migrations/20251018071917_FixDuplicateForeignKeys.Designer.cs b/FutureMailAPI/Migrations/20251018071917_FixDuplicateForeignKeys.Designer.cs new file mode 100644 index 0000000..22a6028 --- /dev/null +++ b/FutureMailAPI/Migrations/20251018071917_FixDuplicateForeignKeys.Designer.cs @@ -0,0 +1,475 @@ +// +using System; +using FutureMailAPI.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace FutureMailAPI.Migrations +{ + [DbContext(typeof(FutureMailDbContext))] + [Migration("20251018071917_FixDuplicateForeignKeys")] + partial class FixDuplicateForeignKeys + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.9"); + + modelBuilder.Entity("FutureMailAPI.Models.OAuthClient", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("ClientSecret") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("RedirectUris") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Scopes") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OAuthClients"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.OAuthToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessToken") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("ClientId") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("ExpiresAt") + .HasColumnType("TEXT"); + + b.Property("RefreshToken") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("RevokedAt") + .HasColumnType("TEXT"); + + b.Property("Scope") + .HasColumnType("TEXT"); + + b.Property("TokenType") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("AccessToken") + .IsUnique(); + + b.HasIndex("ClientId"); + + b.HasIndex("RefreshToken") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("OAuthTokens"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.ReceivedMail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsRead") + .HasColumnType("INTEGER"); + + b.Property("IsReplied") + .HasColumnType("INTEGER"); + + b.Property("ReadAt") + .HasColumnType("TEXT"); + + b.Property("ReceivedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("RecipientId") + .HasColumnType("INTEGER"); + + b.Property("ReplyMailId") + .HasColumnType("INTEGER"); + + b.Property("SentMailId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SentMailId"); + + b.ToTable("ReceivedMails"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.SentMail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attachments") + .HasColumnType("TEXT"); + + b.Property("Content") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DeliveryTime") + .HasColumnType("TEXT"); + + b.Property("EncryptionKey") + .HasColumnType("TEXT"); + + b.Property("IsEncrypted") + .HasColumnType("INTEGER"); + + b.Property("RecipientId") + .HasColumnType("INTEGER"); + + b.Property("RecipientType") + .HasColumnType("INTEGER"); + + b.Property("SenderId") + .HasColumnType("INTEGER"); + + b.Property("SentAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Theme") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("TriggerDetails") + .HasColumnType("TEXT"); + + b.Property("TriggerType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SenderId"); + + b.ToTable("SentMails"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.TimeCapsule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Color") + .HasMaxLength(20) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("GlowIntensity") + .HasColumnType("REAL"); + + b.Property("Opacity") + .HasColumnType("REAL"); + + b.Property("PositionX") + .HasColumnType("REAL"); + + b.Property("PositionY") + .HasColumnType("REAL"); + + b.Property("PositionZ") + .HasColumnType("REAL"); + + b.Property("Rotation") + .HasColumnType("REAL"); + + b.Property("SentMailId") + .HasColumnType("INTEGER"); + + b.Property("SentMailId1") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("REAL"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Style") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("SentMailId") + .IsUnique(); + + b.HasIndex("SentMailId1"); + + b.HasIndex("UserId"); + + b.ToTable("TimeCapsules"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Avatar") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("LastLoginAt") + .HasColumnType("TEXT"); + + b.Property("Nickname") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("PreferredBackground") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PreferredScene") + .HasMaxLength(20) + .HasColumnType("TEXT"); + + b.Property("RefreshToken") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("TEXT"); + + b.Property("Salt") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.OAuthToken", b => + { + b.HasOne("FutureMailAPI.Models.OAuthClient", "Client") + .WithMany("Tokens") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FutureMailAPI.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.ReceivedMail", b => + { + b.HasOne("FutureMailAPI.Models.User", "Recipient") + .WithMany("ReceivedMails") + .HasForeignKey("RecipientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FutureMailAPI.Models.SentMail", "SentMail") + .WithMany() + .HasForeignKey("SentMailId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Recipient"); + + b.Navigation("SentMail"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.SentMail", b => + { + b.HasOne("FutureMailAPI.Models.User", "Recipient") + .WithMany() + .HasForeignKey("RecipientId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("FutureMailAPI.Models.User", "Sender") + .WithMany("SentMails") + .HasForeignKey("SenderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Recipient"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.TimeCapsule", b => + { + b.HasOne("FutureMailAPI.Models.SentMail", null) + .WithOne("TimeCapsule") + .HasForeignKey("FutureMailAPI.Models.TimeCapsule", "SentMailId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FutureMailAPI.Models.SentMail", "SentMail") + .WithMany() + .HasForeignKey("SentMailId1") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FutureMailAPI.Models.User", "User") + .WithMany("TimeCapsules") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SentMail"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.OAuthClient", b => + { + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.SentMail", b => + { + b.Navigation("TimeCapsule"); + }); + + modelBuilder.Entity("FutureMailAPI.Models.User", b => + { + b.Navigation("ReceivedMails"); + + b.Navigation("SentMails"); + + b.Navigation("TimeCapsules"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/FutureMailAPI/Migrations/20251018071917_FixDuplicateForeignKeys.cs b/FutureMailAPI/Migrations/20251018071917_FixDuplicateForeignKeys.cs new file mode 100644 index 0000000..5095492 --- /dev/null +++ b/FutureMailAPI/Migrations/20251018071917_FixDuplicateForeignKeys.cs @@ -0,0 +1,80 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace FutureMailAPI.Migrations +{ + /// + public partial class FixDuplicateForeignKeys : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ReceivedMails_Users_RecipientId1", + table: "ReceivedMails"); + + migrationBuilder.DropForeignKey( + name: "FK_SentMails_Users_RecipientId1", + table: "SentMails"); + + migrationBuilder.DropIndex( + name: "IX_SentMails_RecipientId1", + table: "SentMails"); + + migrationBuilder.DropIndex( + name: "IX_ReceivedMails_RecipientId1", + table: "ReceivedMails"); + + migrationBuilder.DropColumn( + name: "RecipientId1", + table: "SentMails"); + + migrationBuilder.DropColumn( + name: "RecipientId1", + table: "ReceivedMails"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "RecipientId1", + table: "SentMails", + type: "INTEGER", + nullable: true); + + migrationBuilder.AddColumn( + name: "RecipientId1", + table: "ReceivedMails", + type: "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateIndex( + name: "IX_SentMails_RecipientId1", + table: "SentMails", + column: "RecipientId1"); + + migrationBuilder.CreateIndex( + name: "IX_ReceivedMails_RecipientId1", + table: "ReceivedMails", + column: "RecipientId1"); + + migrationBuilder.AddForeignKey( + name: "FK_ReceivedMails_Users_RecipientId1", + table: "ReceivedMails", + column: "RecipientId1", + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_SentMails_Users_RecipientId1", + table: "SentMails", + column: "RecipientId1", + principalTable: "Users", + principalColumn: "Id"); + } + } +} diff --git a/FutureMailAPI/Migrations/FutureMailDbContextModelSnapshot.cs b/FutureMailAPI/Migrations/FutureMailDbContextModelSnapshot.cs index 7afc3e9..236658a 100644 --- a/FutureMailAPI/Migrations/FutureMailDbContextModelSnapshot.cs +++ b/FutureMailAPI/Migrations/FutureMailDbContextModelSnapshot.cs @@ -148,9 +148,6 @@ namespace FutureMailAPI.Migrations b.Property("RecipientId") .HasColumnType("INTEGER"); - b.Property("RecipientId1") - .HasColumnType("INTEGER"); - b.Property("ReplyMailId") .HasColumnType("INTEGER"); @@ -161,8 +158,6 @@ namespace FutureMailAPI.Migrations b.HasIndex("RecipientId"); - b.HasIndex("RecipientId1"); - b.HasIndex("SentMailId"); b.ToTable("ReceivedMails"); @@ -181,6 +176,9 @@ namespace FutureMailAPI.Migrations .IsRequired() .HasColumnType("TEXT"); + b.Property("CreatedAt") + .HasColumnType("TEXT"); + b.Property("DeliveryTime") .HasColumnType("TEXT"); @@ -193,9 +191,6 @@ namespace FutureMailAPI.Migrations b.Property("RecipientId") .HasColumnType("INTEGER"); - b.Property("RecipientId1") - .HasColumnType("INTEGER"); - b.Property("RecipientType") .HasColumnType("INTEGER"); @@ -229,8 +224,6 @@ namespace FutureMailAPI.Migrations b.HasIndex("RecipientId"); - b.HasIndex("RecipientId1"); - b.HasIndex("SenderId"); b.ToTable("SentMails"); @@ -251,6 +244,9 @@ namespace FutureMailAPI.Migrations .HasColumnType("TEXT") .HasDefaultValueSql("CURRENT_TIMESTAMP"); + b.Property("GlowIntensity") + .HasColumnType("REAL"); + b.Property("Opacity") .HasColumnType("REAL"); @@ -278,6 +274,10 @@ namespace FutureMailAPI.Migrations b.Property("Status") .HasColumnType("INTEGER"); + b.Property("Style") + .HasMaxLength(50) + .HasColumnType("TEXT"); + b.Property("Type") .HasColumnType("INTEGER"); @@ -286,7 +286,8 @@ namespace FutureMailAPI.Migrations b.HasKey("Id"); - b.HasIndex("SentMailId"); + b.HasIndex("SentMailId") + .IsUnique(); b.HasIndex("SentMailId1"); @@ -387,15 +388,9 @@ namespace FutureMailAPI.Migrations modelBuilder.Entity("FutureMailAPI.Models.ReceivedMail", b => { - b.HasOne("FutureMailAPI.Models.User", null) - .WithMany() - .HasForeignKey("RecipientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - b.HasOne("FutureMailAPI.Models.User", "Recipient") .WithMany("ReceivedMails") - .HasForeignKey("RecipientId1") + .HasForeignKey("RecipientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); @@ -412,15 +407,11 @@ namespace FutureMailAPI.Migrations modelBuilder.Entity("FutureMailAPI.Models.SentMail", b => { - b.HasOne("FutureMailAPI.Models.User", null) + b.HasOne("FutureMailAPI.Models.User", "Recipient") .WithMany() .HasForeignKey("RecipientId") .OnDelete(DeleteBehavior.SetNull); - b.HasOne("FutureMailAPI.Models.User", "Recipient") - .WithMany() - .HasForeignKey("RecipientId1"); - b.HasOne("FutureMailAPI.Models.User", "Sender") .WithMany("SentMails") .HasForeignKey("SenderId") @@ -435,8 +426,8 @@ namespace FutureMailAPI.Migrations modelBuilder.Entity("FutureMailAPI.Models.TimeCapsule", b => { b.HasOne("FutureMailAPI.Models.SentMail", null) - .WithMany() - .HasForeignKey("SentMailId") + .WithOne("TimeCapsule") + .HasForeignKey("FutureMailAPI.Models.TimeCapsule", "SentMailId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); @@ -462,6 +453,11 @@ namespace FutureMailAPI.Migrations b.Navigation("Tokens"); }); + modelBuilder.Entity("FutureMailAPI.Models.SentMail", b => + { + b.Navigation("TimeCapsule"); + }); + modelBuilder.Entity("FutureMailAPI.Models.User", b => { b.Navigation("ReceivedMails"); diff --git a/FutureMailAPI/Models/SentMail.cs b/FutureMailAPI/Models/SentMail.cs index 0d6484e..164282a 100644 --- a/FutureMailAPI/Models/SentMail.cs +++ b/FutureMailAPI/Models/SentMail.cs @@ -28,6 +28,9 @@ namespace FutureMailAPI.Models // 发送时间 public DateTime SentAt { get; set; } = DateTime.UtcNow; + // 创建时间 + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + // 投递时间 [Required] public DateTime DeliveryTime { get; set; } @@ -61,5 +64,7 @@ namespace FutureMailAPI.Models public virtual User Sender { get; set; } = null!; public virtual User? Recipient { get; set; } + + public virtual TimeCapsule? TimeCapsule { get; set; } } } \ No newline at end of file diff --git a/FutureMailAPI/Models/TimeCapsule.cs b/FutureMailAPI/Models/TimeCapsule.cs index 76da37c..6e353cb 100644 --- a/FutureMailAPI/Models/TimeCapsule.cs +++ b/FutureMailAPI/Models/TimeCapsule.cs @@ -32,6 +32,13 @@ namespace FutureMailAPI.Models // 胶囊旋转角度 public double Rotation { get; set; } = 0; + // 胶囊样式/皮肤 + [MaxLength(50)] + public string? Style { get; set; } + + // 发光强度 + public double GlowIntensity { get; set; } = 0.8; + // 胶囊状态: 0-未激活, 1-漂浮中, 2-即将到达, 3-已开启 [Required] public int Status { get; set; } = 0; diff --git a/FutureMailAPI/Services/IMailService.cs b/FutureMailAPI/Services/IMailService.cs index bd21764..a157c2e 100644 --- a/FutureMailAPI/Services/IMailService.cs +++ b/FutureMailAPI/Services/IMailService.cs @@ -17,5 +17,15 @@ namespace FutureMailAPI.Services Task> MarkReceivedMailAsReadAsync(int userId, int mailId); Task> MarkAsReadAsync(int userId, int mailId); Task> RevokeMailAsync(int userId, int mailId); + + // 存入胶囊相关方法 + Task> SaveToCapsuleAsync(int userId, SaveToCapsuleDto saveToCapsuleDto); + Task>> GetCapsuleMailsAsync(int userId, MailListQueryDto queryDto); + Task> GetCapsuleMailByIdAsync(int userId, int mailId); + Task> UpdateCapsuleMailAsync(int userId, int mailId, UpdateCapsuleMailDto updateDto); + Task> RevokeCapsuleMailAsync(int userId, int mailId); + + // 发送至未来功能 + Task> SendToFutureAsync(int userId, SendToFutureDto sendToFutureDto); } } \ No newline at end of file diff --git a/FutureMailAPI/Services/MailService.cs b/FutureMailAPI/Services/MailService.cs index 2d89678..5e60c6e 100644 --- a/FutureMailAPI/Services/MailService.cs +++ b/FutureMailAPI/Services/MailService.cs @@ -2,6 +2,7 @@ using Microsoft.EntityFrameworkCore; using FutureMailAPI.Data; using FutureMailAPI.Models; using FutureMailAPI.DTOs; +using System.Text.Json; namespace FutureMailAPI.Services { @@ -382,6 +383,393 @@ namespace FutureMailAPI.Services return ApiResponse.SuccessResult(true, "邮件已撤销"); } + // 存入胶囊相关方法实现 + public async Task> SaveToCapsuleAsync(int userId, SaveToCapsuleDto saveToCapsuleDto) + { + // 验证收件人类型 + if (saveToCapsuleDto.RecipientType == RecipientTypeEnum.SPECIFIC && string.IsNullOrEmpty(saveToCapsuleDto.RecipientEmail)) + { + return ApiResponse.ErrorResult("指定收件人时,收件人邮箱是必填项"); + } + + // 验证发送时间 + if (saveToCapsuleDto.SendTime.HasValue && saveToCapsuleDto.SendTime.Value <= DateTime.UtcNow) + { + return ApiResponse.ErrorResult("发送时间必须是未来时间"); + } + + // 创建邮件 + var mail = new SentMail + { + Title = saveToCapsuleDto.Title, + Content = saveToCapsuleDto.Content, + SenderId = userId, + RecipientType = (int)saveToCapsuleDto.RecipientType, + SentAt = DateTime.UtcNow, + CreatedAt = DateTime.UtcNow, + DeliveryTime = saveToCapsuleDto.SendTime ?? DateTime.UtcNow.AddDays(1), // 默认一天后发送 + Status = 0, // 草稿状态 + TriggerType = (int)saveToCapsuleDto.TriggerType, + TriggerDetails = saveToCapsuleDto.TriggerCondition != null ? JsonSerializer.Serialize(saveToCapsuleDto.TriggerCondition) : null, + Attachments = saveToCapsuleDto.Attachments != null ? JsonSerializer.Serialize(saveToCapsuleDto.Attachments) : null, + IsEncrypted = saveToCapsuleDto.IsEncrypted, + Theme = saveToCapsuleDto.CapsuleStyle + }; + + // 如果是指定收件人,查找收件人ID + if (saveToCapsuleDto.RecipientType == RecipientTypeEnum.SPECIFIC && !string.IsNullOrEmpty(saveToCapsuleDto.RecipientEmail)) + { + var recipient = await _context.Users + .FirstOrDefaultAsync(u => u.Email == saveToCapsuleDto.RecipientEmail); + + if (recipient == null) + { + return ApiResponse.ErrorResult("收件人不存在"); + } + + mail.RecipientId = recipient.Id; + } + + _context.SentMails.Add(mail); + await _context.SaveChangesAsync(); + + // 创建时间胶囊 + var timeCapsule = new TimeCapsule + { + UserId = userId, + SentMailId = mail.Id, + Status = 0, // 草稿状态 + CreatedAt = DateTime.UtcNow, + PositionX = 0.5f, // 默认位置 + PositionY = 0.5f, + PositionZ = 0.5f, + Style = saveToCapsuleDto.CapsuleStyle, + GlowIntensity = 0.8f // 默认发光强度 + }; + + _context.TimeCapsules.Add(timeCapsule); + await _context.SaveChangesAsync(); + + var response = new SaveToCapsuleResponseDto + { + Id = mail.Id, + MailId = mail.Id.ToString(), + CapsuleId = timeCapsule.Id.ToString(), + Status = "DRAFT", + CreatedAt = mail.CreatedAt + }; + + return ApiResponse.SuccessResult(response, "邮件已存入胶囊"); + } + + public async Task>> GetCapsuleMailsAsync(int userId, MailListQueryDto queryDto) + { + var query = _context.SentMails + .Where(m => m.SenderId == userId && m.Status == 0) // 只查询草稿状态的邮件 + .Include(m => m.Sender) + .Include(m => m.Recipient) + .Include(m => m.TimeCapsule) + .AsQueryable(); + + // 应用筛选条件 + if (queryDto.Status.HasValue) + { + query = query.Where(m => m.Status == queryDto.Status.Value); + } + + if (queryDto.RecipientType.HasValue) + { + query = query.Where(m => m.RecipientType == queryDto.RecipientType.Value); + } + + if (!string.IsNullOrEmpty(queryDto.Keyword)) + { + query = query.Where(m => m.Title.Contains(queryDto.Keyword) || m.Content.Contains(queryDto.Keyword)); + } + + if (queryDto.StartDate.HasValue) + { + query = query.Where(m => m.SentAt >= queryDto.StartDate.Value); + } + + if (queryDto.EndDate.HasValue) + { + query = query.Where(m => m.SentAt <= queryDto.EndDate.Value); + } + + // 排序 + query = query.OrderByDescending(m => m.SentAt); + + // 分页 + var totalCount = await query.CountAsync(); + var mails = await query + .Skip((queryDto.PageIndex - 1) * queryDto.PageSize) + .Take(queryDto.PageSize) + .ToListAsync(); + + var mailDtos = mails.Select(MapToCapsuleMailListResponseDto).ToList(); + + var pagedResponse = new PagedResponse( + mailDtos, queryDto.PageIndex, queryDto.PageSize, totalCount); + + return ApiResponse>.SuccessResult(pagedResponse); + } + + public async Task> GetCapsuleMailByIdAsync(int userId, int mailId) + { + var mail = await _context.SentMails + .Include(m => m.Sender) + .Include(m => m.Recipient) + .Include(m => m.TimeCapsule) + .FirstOrDefaultAsync(m => m.Id == mailId && m.SenderId == userId); + + if (mail == null) + { + return ApiResponse.ErrorResult("邮件不存在"); + } + + var mailDto = MapToCapsuleMailDetailResponseDto(mail); + + return ApiResponse.SuccessResult(mailDto); + } + + public async Task> UpdateCapsuleMailAsync(int userId, int mailId, UpdateCapsuleMailDto updateDto) + { + var mail = await _context.SentMails + .Include(m => m.Sender) + .Include(m => m.Recipient) + .Include(m => m.TimeCapsule) + .FirstOrDefaultAsync(m => m.Id == mailId && m.SenderId == userId); + + if (mail == null) + { + return ApiResponse.ErrorResult("邮件不存在"); + } + + // 检查邮件状态,只有草稿状态的邮件才能编辑 + if (mail.Status != 0) + { + return ApiResponse.ErrorResult("只有草稿状态的邮件才能编辑"); + } + + // 更新邮件信息 + if (!string.IsNullOrEmpty(updateDto.Title)) + { + mail.Title = updateDto.Title; + } + + if (!string.IsNullOrEmpty(updateDto.Content)) + { + mail.Content = updateDto.Content; + } + + if (updateDto.RecipientType.HasValue) + { + mail.RecipientType = (int)updateDto.RecipientType.Value; + + // 如果是指定收件人,查找收件人ID + if (updateDto.RecipientType.Value == RecipientTypeEnum.SPECIFIC && !string.IsNullOrEmpty(updateDto.RecipientEmail)) + { + var recipient = await _context.Users + .FirstOrDefaultAsync(u => u.Email == updateDto.RecipientEmail); + + if (recipient == null) + { + return ApiResponse.ErrorResult("收件人不存在"); + } + + mail.RecipientId = recipient.Id; + } + } + + if (updateDto.SendTime.HasValue) + { + if (updateDto.SendTime.Value <= DateTime.UtcNow) + { + return ApiResponse.ErrorResult("发送时间必须是未来时间"); + } + + mail.DeliveryTime = updateDto.SendTime.Value; + } + + if (updateDto.TriggerType.HasValue) + { + mail.TriggerType = (int)updateDto.TriggerType.Value; + } + + if (updateDto.TriggerCondition != null) + { + mail.TriggerDetails = JsonSerializer.Serialize(updateDto.TriggerCondition); + } + + if (updateDto.Attachments != null) + { + mail.Attachments = JsonSerializer.Serialize(updateDto.Attachments); + } + + if (updateDto.IsEncrypted.HasValue) + { + mail.IsEncrypted = updateDto.IsEncrypted.Value; + } + + if (!string.IsNullOrEmpty(updateDto.CapsuleStyle)) + { + mail.Theme = updateDto.CapsuleStyle; + + // 更新时间胶囊样式 + if (mail.TimeCapsule != null) + { + mail.TimeCapsule.Style = updateDto.CapsuleStyle; + } + } + + await _context.SaveChangesAsync(); + + var mailResponse = MapToCapsuleMailDetailResponseDto(mail); + + return ApiResponse.SuccessResult(mailResponse, "胶囊邮件更新成功"); + } + + public async Task> RevokeCapsuleMailAsync(int userId, int mailId) + { + var mail = await _context.SentMails + .Include(m => m.TimeCapsule) + .FirstOrDefaultAsync(m => m.Id == mailId && m.SenderId == userId); + + if (mail == null) + { + return ApiResponse.ErrorResult("邮件不存在"); + } + + // 检查邮件状态,只有草稿或待投递状态的邮件才能撤销 + if (mail.Status > 1) + { + return ApiResponse.ErrorResult("已投递的邮件不能撤销"); + } + + // 更新邮件状态为已撤销 + mail.Status = 4; // 4-已撤销 + await _context.SaveChangesAsync(); + + // 更新相关的时间胶囊状态 + if (mail.TimeCapsule != null) + { + mail.TimeCapsule.Status = 3; // 3-已撤销 + await _context.SaveChangesAsync(); + } + + return ApiResponse.SuccessResult(true, "胶囊邮件已撤销"); + } + + private static CapsuleMailListResponseDto MapToCapsuleMailListResponseDto(SentMail mail) + { + return new CapsuleMailListResponseDto + { + MailId = mail.Id.ToString(), + Title = mail.Title, + Sender = MapToUserInfoDto(mail.Sender), + Recipient = mail.Recipient != null ? MapToUserInfoDto(mail.Recipient) : new UserInfoDto(), + SendTime = mail.SentAt, + DeliveryTime = mail.DeliveryTime, + Status = mail.Status switch + { + 0 => "DRAFT", + 1 => "PENDING", + 2 => "DELIVERING", + 3 => "DELIVERED", + _ => "UNKNOWN" + }, + HasAttachments = !string.IsNullOrEmpty(mail.Attachments), + IsEncrypted = mail.IsEncrypted, + CapsuleStyle = mail.Theme ?? "default", + Countdown = mail.Status == 1 ? (int)(mail.DeliveryTime - DateTime.UtcNow).TotalSeconds : null + }; + } + + private static CapsuleMailDetailResponseDto MapToCapsuleMailDetailResponseDto(SentMail mail) + { + List attachments = new List(); + + if (!string.IsNullOrEmpty(mail.Attachments)) + { + try + { + var attachmentList = JsonSerializer.Deserialize>(mail.Attachments); + if (attachmentList != null) + { + attachments = attachmentList.Select(a => new AttachmentDto + { + Id = Guid.NewGuid().ToString(), + Type = "IMAGE", // 默认类型,实际应根据数据解析 + Url = a.ToString() ?? "", + Size = 0 // 默认大小,实际应根据数据解析 + }).ToList(); + } + } + catch + { + // 解析失败时忽略附件 + } + } + + object? triggerCondition = null; + if (!string.IsNullOrEmpty(mail.TriggerDetails)) + { + try + { + triggerCondition = JsonSerializer.Deserialize(mail.TriggerDetails); + } + catch + { + // 解析失败时忽略触发条件 + } + } + + return new CapsuleMailDetailResponseDto + { + MailId = mail.Id.ToString(), + Title = mail.Title, + Content = mail.Content, + Sender = MapToUserInfoDto(mail.Sender), + Recipient = mail.Recipient != null ? MapToUserInfoDto(mail.Recipient) : new UserInfoDto(), + SendTime = mail.SentAt, + CreatedAt = mail.CreatedAt, + DeliveryTime = mail.DeliveryTime, + Status = mail.Status switch + { + 0 => "DRAFT", + 1 => "PENDING", + 2 => "DELIVERING", + 3 => "DELIVERED", + _ => "UNKNOWN" + }, + TriggerType = mail.TriggerType switch + { + 0 => "TIME", + 1 => "LOCATION", + 2 => "EVENT", + _ => "UNKNOWN" + }, + TriggerCondition = triggerCondition, + Attachments = attachments, + IsEncrypted = mail.IsEncrypted, + CapsuleStyle = mail.Theme ?? "default", + CanEdit = mail.Status == 0, // 只有草稿状态可编辑 + CanRevoke = mail.Status <= 1 // 草稿或待投递状态可撤销 + }; + } + + private static UserInfoDto MapToUserInfoDto(User user) + { + return new UserInfoDto + { + UserId = user.Id, + Username = user.Username, + Avatar = user.Avatar ?? "", + Email = user.Email + }; + } + private async Task GetSentMailWithDetailsAsync(int mailId) { var mail = await _context.SentMails @@ -471,5 +859,70 @@ namespace FutureMailAPI.Services _ => "未知" }; } + + /// + /// 发送至未来 - 将草稿状态的邮件设置为在未来特定时间自动发送 + /// + /// 用户ID + /// 发送至未来请求DTO + /// 发送至未来响应DTO + public async Task> SendToFutureAsync(int userId, SendToFutureDto sendToFutureDto) + { + // 检查投递时间是否在未来 + if (sendToFutureDto.DeliveryTime <= DateTime.UtcNow) + { + return ApiResponse.ErrorResult("投递时间必须是未来时间"); + } + + // 查找邮件 + var mail = await _context.SentMails + .Include(m => m.Sender) + .FirstOrDefaultAsync(m => m.Id == sendToFutureDto.MailId && m.SenderId == userId); + + if (mail == null) + { + return ApiResponse.ErrorResult("邮件不存在"); + } + + // 检查邮件是否为草稿状态 + if (mail.Status != 0) + { + return ApiResponse.ErrorResult("只有草稿状态的邮件可以设置为发送至未来"); + } + + // 更新邮件状态和投递时间 + mail.DeliveryTime = sendToFutureDto.DeliveryTime; + mail.Status = 1; // 设置为已发送(待投递)状态 + mail.SentAt = DateTime.UtcNow; // 设置发送时间为当前时间 + + await _context.SaveChangesAsync(); + + // 创建时间胶囊 + var timeCapsule = new TimeCapsule + { + UserId = userId, + SentMailId = mail.Id, + PositionX = 0, + PositionY = 0, + PositionZ = 0, + Status = 1, // 漂浮中 + Type = 0 // 普通 + }; + + _context.TimeCapsules.Add(timeCapsule); + await _context.SaveChangesAsync(); + + // 构建响应 + var response = new SendToFutureResponseDto + { + MailId = mail.Id, + Title = mail.Title, + DeliveryTime = mail.DeliveryTime, + Status = GetStatusText(mail.Status), + SentAt = mail.SentAt + }; + + return ApiResponse.SuccessResult(response, "邮件已设置为发送至未来"); + } } } \ No newline at end of file diff --git a/FutureMailAPI/bin/Debug/net9.0/FutureMailAPI.dll b/FutureMailAPI/bin/Debug/net9.0/FutureMailAPI.dll index d19d246..65998eb 100644 Binary files a/FutureMailAPI/bin/Debug/net9.0/FutureMailAPI.dll and b/FutureMailAPI/bin/Debug/net9.0/FutureMailAPI.dll differ diff --git a/FutureMailAPI/bin/Debug/net9.0/FutureMailAPI.pdb b/FutureMailAPI/bin/Debug/net9.0/FutureMailAPI.pdb index 6f2dee7..6af68bb 100644 Binary files a/FutureMailAPI/bin/Debug/net9.0/FutureMailAPI.pdb and b/FutureMailAPI/bin/Debug/net9.0/FutureMailAPI.pdb differ diff --git a/FutureMailAPI/bin/Debug/net9.0/FutureMailAPI.xml b/FutureMailAPI/bin/Debug/net9.0/FutureMailAPI.xml index ca28ba7..0fd5667 100644 --- a/FutureMailAPI/bin/Debug/net9.0/FutureMailAPI.xml +++ b/FutureMailAPI/bin/Debug/net9.0/FutureMailAPI.xml @@ -83,6 +83,55 @@ 文件ID 文件信息 + + + 存入胶囊 - 创建胶囊邮件 + + 存入胶囊请求 + 操作结果 + + + + 获取胶囊邮件列表 + + 页码 + 页大小 + 状态筛选 + 收件人类型筛选 + 关键词搜索 + 开始日期 + 结束日期 + 胶囊邮件列表 + + + + 获取胶囊邮件详情 + + 邮件ID + 胶囊邮件详情 + + + + 更新胶囊邮件 + + 邮件ID + 更新请求 + 更新后的胶囊邮件详情 + + + + 撤销胶囊邮件 + + 邮件ID + 操作结果 + + + + 发送至未来 - 将草稿状态的邮件设置为在未来特定时间自动发送 + + 发送至未来请求DTO + 发送至未来响应DTO + 注册设备 @@ -207,5 +256,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + 发送至未来 - 将草稿状态的邮件设置为在未来特定时间自动发送 + + 用户ID + 发送至未来请求DTO + 发送至未来响应DTO + diff --git a/FutureMailAPI/obj/Debug/net9.0/FutureMailAPI.csproj.CoreCompileInputs.cache b/FutureMailAPI/obj/Debug/net9.0/FutureMailAPI.csproj.CoreCompileInputs.cache index b3e5961..ce82859 100644 --- a/FutureMailAPI/obj/Debug/net9.0/FutureMailAPI.csproj.CoreCompileInputs.cache +++ b/FutureMailAPI/obj/Debug/net9.0/FutureMailAPI.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -51372bde626c0ba3aa5386f92d0ea465adcc4b558852e21737182a326708e608 +96b95197304e11878aba7d6a0ad52cc38a6b9883e011012e3da5e389d5e93831 diff --git a/FutureMailAPI/obj/Debug/net9.0/FutureMailAPI.dll b/FutureMailAPI/obj/Debug/net9.0/FutureMailAPI.dll index d19d246..65998eb 100644 Binary files a/FutureMailAPI/obj/Debug/net9.0/FutureMailAPI.dll and b/FutureMailAPI/obj/Debug/net9.0/FutureMailAPI.dll differ diff --git a/FutureMailAPI/obj/Debug/net9.0/FutureMailAPI.pdb b/FutureMailAPI/obj/Debug/net9.0/FutureMailAPI.pdb index 6f2dee7..6af68bb 100644 Binary files a/FutureMailAPI/obj/Debug/net9.0/FutureMailAPI.pdb and b/FutureMailAPI/obj/Debug/net9.0/FutureMailAPI.pdb differ diff --git a/FutureMailAPI/obj/Debug/net9.0/FutureMailAPI.xml b/FutureMailAPI/obj/Debug/net9.0/FutureMailAPI.xml index ca28ba7..0fd5667 100644 --- a/FutureMailAPI/obj/Debug/net9.0/FutureMailAPI.xml +++ b/FutureMailAPI/obj/Debug/net9.0/FutureMailAPI.xml @@ -83,6 +83,55 @@ 文件ID 文件信息 + + + 存入胶囊 - 创建胶囊邮件 + + 存入胶囊请求 + 操作结果 + + + + 获取胶囊邮件列表 + + 页码 + 页大小 + 状态筛选 + 收件人类型筛选 + 关键词搜索 + 开始日期 + 结束日期 + 胶囊邮件列表 + + + + 获取胶囊邮件详情 + + 邮件ID + 胶囊邮件详情 + + + + 更新胶囊邮件 + + 邮件ID + 更新请求 + 更新后的胶囊邮件详情 + + + + 撤销胶囊邮件 + + 邮件ID + 操作结果 + + + + 发送至未来 - 将草稿状态的邮件设置为在未来特定时间自动发送 + + 发送至未来请求DTO + 发送至未来响应DTO + 注册设备 @@ -207,5 +256,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + 发送至未来 - 将草稿状态的邮件设置为在未来特定时间自动发送 + + 用户ID + 发送至未来请求DTO + 发送至未来响应DTO + diff --git a/FutureMailAPI/obj/Debug/net9.0/project.razor.json b/FutureMailAPI/obj/Debug/net9.0/project.razor.json index aaa6b90..5af61fc 100644 --- a/FutureMailAPI/obj/Debug/net9.0/project.razor.json +++ b/FutureMailAPI/obj/Debug/net9.0/project.razor.json @@ -13,7 +13,7 @@ "ProjectWorkspaceState": { "TagHelpers": [ { - "HashCode": -535743526, + "HashCode": -2008518270, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Authorization.AuthorizeRouteView", "AssemblyName": "Microsoft.AspNetCore.Components.Authorization", @@ -97,7 +97,7 @@ } }, { - "HashCode": -2053641828, + "HashCode": -2080177151, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Authorization.AuthorizeRouteView", "AssemblyName": "Microsoft.AspNetCore.Components.Authorization", @@ -182,7 +182,7 @@ } }, { - "HashCode": 1995491017, + "HashCode": -1169906769, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Authorization.AuthorizeRouteView.NotAuthorized", "AssemblyName": "Microsoft.AspNetCore.Components.Authorization", @@ -215,7 +215,7 @@ } }, { - "HashCode": 824436316, + "HashCode": -2001170475, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Authorization.AuthorizeRouteView.NotAuthorized", "AssemblyName": "Microsoft.AspNetCore.Components.Authorization", @@ -249,7 +249,7 @@ } }, { - "HashCode": 575089357, + "HashCode": -779394766, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Authorization.AuthorizeRouteView.Authorizing", "AssemblyName": "Microsoft.AspNetCore.Components.Authorization", @@ -270,7 +270,7 @@ } }, { - "HashCode": -471166931, + "HashCode": 116733599, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Authorization.AuthorizeRouteView.Authorizing", "AssemblyName": "Microsoft.AspNetCore.Components.Authorization", @@ -292,7 +292,7 @@ } }, { - "HashCode": -455675492, + "HashCode": -919568059, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Authorization.AuthorizeView", "AssemblyName": "Microsoft.AspNetCore.Components.Authorization", @@ -397,7 +397,7 @@ } }, { - "HashCode": -2055802383, + "HashCode": -19316343, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Authorization.AuthorizeView", "AssemblyName": "Microsoft.AspNetCore.Components.Authorization", @@ -503,7 +503,7 @@ } }, { - "HashCode": 240693311, + "HashCode": -1501530547, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Authorization.AuthorizeView.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Authorization", @@ -536,7 +536,7 @@ } }, { - "HashCode": 1666336969, + "HashCode": 916168727, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Authorization.AuthorizeView.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Authorization", @@ -570,7 +570,7 @@ } }, { - "HashCode": -211101841, + "HashCode": -834689023, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Authorization.AuthorizeView.NotAuthorized", "AssemblyName": "Microsoft.AspNetCore.Components.Authorization", @@ -603,7 +603,7 @@ } }, { - "HashCode": -1389780228, + "HashCode": -1778137722, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Authorization.AuthorizeView.NotAuthorized", "AssemblyName": "Microsoft.AspNetCore.Components.Authorization", @@ -637,7 +637,7 @@ } }, { - "HashCode": 1559496138, + "HashCode": -516968054, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Authorization.AuthorizeView.Authorized", "AssemblyName": "Microsoft.AspNetCore.Components.Authorization", @@ -670,7 +670,7 @@ } }, { - "HashCode": -10824910, + "HashCode": -1495449499, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Authorization.AuthorizeView.Authorized", "AssemblyName": "Microsoft.AspNetCore.Components.Authorization", @@ -704,7 +704,7 @@ } }, { - "HashCode": 2109093318, + "HashCode": -1712077588, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Authorization.AuthorizeView.Authorizing", "AssemblyName": "Microsoft.AspNetCore.Components.Authorization", @@ -725,7 +725,7 @@ } }, { - "HashCode": -1783783973, + "HashCode": -1824859729, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Authorization.AuthorizeView.Authorizing", "AssemblyName": "Microsoft.AspNetCore.Components.Authorization", @@ -747,7 +747,7 @@ } }, { - "HashCode": 763526120, + "HashCode": 719343309, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Authorization.CascadingAuthenticationState", "AssemblyName": "Microsoft.AspNetCore.Components.Authorization", @@ -778,7 +778,7 @@ } }, { - "HashCode": -510489334, + "HashCode": 1692982579, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Authorization.CascadingAuthenticationState", "AssemblyName": "Microsoft.AspNetCore.Components.Authorization", @@ -810,7 +810,7 @@ } }, { - "HashCode": 1751011274, + "HashCode": 1444629023, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Authorization.CascadingAuthenticationState.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Authorization", @@ -831,7 +831,7 @@ } }, { - "HashCode": -1835126596, + "HashCode": -810805864, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Authorization.CascadingAuthenticationState.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Authorization", @@ -853,7 +853,7 @@ } }, { - "HashCode": 1595338345, + "HashCode": -1392450232, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.CascadingValue", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -928,7 +928,7 @@ } }, { - "HashCode": 462883957, + "HashCode": 1786302393, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.CascadingValue", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1004,7 +1004,7 @@ } }, { - "HashCode": 434524209, + "HashCode": 2025235800, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.CascadingValue.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1025,7 +1025,7 @@ } }, { - "HashCode": 2006371137, + "HashCode": 1409827993, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.CascadingValue.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1047,7 +1047,7 @@ } }, { - "HashCode": 948788777, + "HashCode": -1134472505, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.DynamicComponent", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1089,7 +1089,7 @@ } }, { - "HashCode": -1817797339, + "HashCode": -819539094, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.DynamicComponent", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1132,7 +1132,7 @@ } }, { - "HashCode": 214778228, + "HashCode": -544215047, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.LayoutView", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1174,7 +1174,7 @@ } }, { - "HashCode": -525875967, + "HashCode": -1250420404, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.LayoutView", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1217,7 +1217,7 @@ } }, { - "HashCode": 1115232672, + "HashCode": -39304146, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.LayoutView.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1238,7 +1238,7 @@ } }, { - "HashCode": 1344553508, + "HashCode": 463830583, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.LayoutView.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1260,7 +1260,7 @@ } }, { - "HashCode": 1931006130, + "HashCode": -151987294, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.RouteView", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1302,7 +1302,7 @@ } }, { - "HashCode": 138229283, + "HashCode": 168847753, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.RouteView", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1345,7 +1345,7 @@ } }, { - "HashCode": -644207253, + "HashCode": 1331975060, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Routing.Router", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1452,7 +1452,7 @@ } }, { - "HashCode": 1290880080, + "HashCode": 1774539907, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Routing.Router", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1560,7 +1560,7 @@ } }, { - "HashCode": -703861115, + "HashCode": -1425290208, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Routing.Router.NotFound", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1581,7 +1581,7 @@ } }, { - "HashCode": -1435889852, + "HashCode": 1064077723, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Routing.Router.NotFound", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1603,7 +1603,7 @@ } }, { - "HashCode": 1521356165, + "HashCode": 1924688483, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Routing.Router.Found", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1636,7 +1636,7 @@ } }, { - "HashCode": 1915464676, + "HashCode": -612931338, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Routing.Router.Found", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1670,7 +1670,7 @@ } }, { - "HashCode": 1140911377, + "HashCode": 1762995158, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Routing.Router.Navigating", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1691,7 +1691,7 @@ } }, { - "HashCode": 1239820588, + "HashCode": -885091426, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Routing.Router.Navigating", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1713,7 +1713,7 @@ } }, { - "HashCode": -659294383, + "HashCode": -48982662, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Sections.SectionContent", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1765,7 +1765,7 @@ } }, { - "HashCode": 1108873164, + "HashCode": 1673549004, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Sections.SectionContent", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1818,7 +1818,7 @@ } }, { - "HashCode": 1410207907, + "HashCode": -767554750, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Sections.SectionContent.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1839,7 +1839,7 @@ } }, { - "HashCode": -814870635, + "HashCode": -1629635387, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Sections.SectionContent.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1861,7 +1861,7 @@ } }, { - "HashCode": 1378750101, + "HashCode": -515569600, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Sections.SectionOutlet", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1902,7 +1902,7 @@ } }, { - "HashCode": 578792013, + "HashCode": -809435024, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Sections.SectionOutlet", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -1944,7 +1944,7 @@ } }, { - "HashCode": 1855638710, + "HashCode": -1565215515, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.ImportMap", "AssemblyName": "Microsoft.AspNetCore.Components.Endpoints", @@ -1985,7 +1985,7 @@ } }, { - "HashCode": -510000842, + "HashCode": 1118529982, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.ImportMap", "AssemblyName": "Microsoft.AspNetCore.Components.Endpoints", @@ -2027,7 +2027,7 @@ } }, { - "HashCode": -1452564887, + "HashCode": 1994953870, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator", "AssemblyName": "Microsoft.AspNetCore.Components.Forms", @@ -2046,7 +2046,7 @@ } }, { - "HashCode": -1933158778, + "HashCode": 1585771789, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator", "AssemblyName": "Microsoft.AspNetCore.Components.Forms", @@ -2066,7 +2066,7 @@ } }, { - "HashCode": -651131425, + "HashCode": 1579896487, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.AntiforgeryToken", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -2085,7 +2085,7 @@ } }, { - "HashCode": -86070291, + "HashCode": 2039425838, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.AntiforgeryToken", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -2105,7 +2105,7 @@ } }, { - "HashCode": 351903230, + "HashCode": -1706442436, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.EditForm", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -2230,7 +2230,7 @@ } }, { - "HashCode": -1634675555, + "HashCode": 641413287, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.EditForm", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -2356,7 +2356,7 @@ } }, { - "HashCode": 1142212010, + "HashCode": -422589414, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Forms.EditForm.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -2389,7 +2389,7 @@ } }, { - "HashCode": -1145246252, + "HashCode": 715537313, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Forms.EditForm.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -2423,7 +2423,7 @@ } }, { - "HashCode": 71829261, + "HashCode": -1291145746, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.InputCheckbox", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -2495,7 +2495,7 @@ } }, { - "HashCode": -623263909, + "HashCode": -1981033493, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.InputCheckbox", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -2568,7 +2568,7 @@ } }, { - "HashCode": -386542291, + "HashCode": 1691528901, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.InputDate", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -2676,7 +2676,7 @@ } }, { - "HashCode": 379746601, + "HashCode": 2073889618, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.InputDate", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -2785,7 +2785,7 @@ } }, { - "HashCode": -2048786862, + "HashCode": -1253479575, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.InputFile", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -2827,7 +2827,7 @@ } }, { - "HashCode": 333792889, + "HashCode": -623730573, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.InputFile", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -2870,7 +2870,7 @@ } }, { - "HashCode": 1067658955, + "HashCode": 1457148497, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.InputNumber", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -2967,7 +2967,7 @@ } }, { - "HashCode": 30573778, + "HashCode": 572044294, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.InputNumber", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -3065,7 +3065,7 @@ } }, { - "HashCode": 117109617, + "HashCode": -434923145, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.InputRadio", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -3129,7 +3129,7 @@ } }, { - "HashCode": -2090575767, + "HashCode": 2127023144, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.InputRadio", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -3194,7 +3194,7 @@ } }, { - "HashCode": -1817436131, + "HashCode": -715060453, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.InputRadioGroup", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -3302,7 +3302,7 @@ } }, { - "HashCode": -1269941825, + "HashCode": 1217342851, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.InputRadioGroup", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -3411,7 +3411,7 @@ } }, { - "HashCode": -1540884318, + "HashCode": 64205391, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Forms.InputRadioGroup.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -3432,7 +3432,7 @@ } }, { - "HashCode": 797931372, + "HashCode": -1953046159, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Forms.InputRadioGroup.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -3454,7 +3454,7 @@ } }, { - "HashCode": -1901655829, + "HashCode": -1001569107, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.InputSelect", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -3552,7 +3552,7 @@ } }, { - "HashCode": 208645667, + "HashCode": -238352000, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.InputSelect", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -3651,7 +3651,7 @@ } }, { - "HashCode": -1143051998, + "HashCode": -825103021, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Forms.InputSelect.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -3672,7 +3672,7 @@ } }, { - "HashCode": -1024052616, + "HashCode": -146570736, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Forms.InputSelect.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -3694,7 +3694,7 @@ } }, { - "HashCode": -474528774, + "HashCode": 1413950623, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.InputText", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -3766,7 +3766,7 @@ } }, { - "HashCode": 681412170, + "HashCode": -1635420024, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.InputText", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -3839,7 +3839,7 @@ } }, { - "HashCode": -445042939, + "HashCode": 1167860647, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.InputTextArea", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -3911,7 +3911,7 @@ } }, { - "HashCode": -1754959733, + "HashCode": 2059077734, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.InputTextArea", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -3984,7 +3984,7 @@ } }, { - "HashCode": 865286843, + "HashCode": 353826115, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.FormMappingScope", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4037,7 +4037,7 @@ } }, { - "HashCode": 1277655936, + "HashCode": 1861959859, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.FormMappingScope", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4091,7 +4091,7 @@ } }, { - "HashCode": 352649100, + "HashCode": 962641497, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Forms.FormMappingScope.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4124,7 +4124,7 @@ } }, { - "HashCode": 1809291258, + "HashCode": -938044133, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Forms.FormMappingScope.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4158,7 +4158,7 @@ } }, { - "HashCode": -1174430424, + "HashCode": 212293166, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.ValidationMessage", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4212,7 +4212,7 @@ } }, { - "HashCode": -1257018116, + "HashCode": -1650896460, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.ValidationMessage", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4267,7 +4267,7 @@ } }, { - "HashCode": -145627213, + "HashCode": 63188944, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.ValidationSummary", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4308,7 +4308,7 @@ } }, { - "HashCode": 1561677985, + "HashCode": -730782437, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Forms.ValidationSummary", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4350,7 +4350,7 @@ } }, { - "HashCode": 1205601277, + "HashCode": 2010080825, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Routing.FocusOnNavigate", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4391,7 +4391,7 @@ } }, { - "HashCode": 822299117, + "HashCode": -1178369024, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Routing.FocusOnNavigate", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4433,7 +4433,7 @@ } }, { - "HashCode": 2075331134, + "HashCode": -1599833069, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Routing.NavigationLock", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4475,7 +4475,7 @@ } }, { - "HashCode": -338137067, + "HashCode": -1152020112, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Routing.NavigationLock", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4518,7 +4518,7 @@ } }, { - "HashCode": 352352792, + "HashCode": -1151337508, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Routing.NavLink", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4581,7 +4581,7 @@ } }, { - "HashCode": 599401892, + "HashCode": -808963839, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Routing.NavLink", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4645,7 +4645,7 @@ } }, { - "HashCode": -626416421, + "HashCode": 1498574520, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Routing.NavLink.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4666,7 +4666,7 @@ } }, { - "HashCode": 544557895, + "HashCode": 1248702838, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Routing.NavLink.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4688,7 +4688,7 @@ } }, { - "HashCode": -203166847, + "HashCode": -1397940782, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Web.HeadContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4720,7 +4720,7 @@ } }, { - "HashCode": 1723525126, + "HashCode": -1156610723, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Web.HeadContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4753,7 +4753,7 @@ } }, { - "HashCode": -458584282, + "HashCode": -1511817818, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Web.HeadContent.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4774,7 +4774,7 @@ } }, { - "HashCode": 1918429842, + "HashCode": -1462938457, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Web.HeadContent.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4796,7 +4796,7 @@ } }, { - "HashCode": -3163690, + "HashCode": -2144706792, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Web.HeadOutlet", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4815,7 +4815,7 @@ } }, { - "HashCode": -190563990, + "HashCode": -738964256, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Web.HeadOutlet", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4835,7 +4835,7 @@ } }, { - "HashCode": 779297140, + "HashCode": 1862934174, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Web.PageTitle", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4867,7 +4867,7 @@ } }, { - "HashCode": 1994733122, + "HashCode": -556460013, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Web.PageTitle", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4900,7 +4900,7 @@ } }, { - "HashCode": 1510717595, + "HashCode": 1195770574, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Web.PageTitle.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4921,7 +4921,7 @@ } }, { - "HashCode": 1976624225, + "HashCode": 577444299, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Web.PageTitle.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -4943,7 +4943,7 @@ } }, { - "HashCode": -1680556720, + "HashCode": -1443887965, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Web.ErrorBoundary", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -5006,7 +5006,7 @@ } }, { - "HashCode": -1580264464, + "HashCode": -1210296141, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Web.ErrorBoundary", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -5070,7 +5070,7 @@ } }, { - "HashCode": -1906819079, + "HashCode": 337061497, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Web.ErrorBoundary.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -5091,7 +5091,7 @@ } }, { - "HashCode": -1107322918, + "HashCode": 1183431742, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Web.ErrorBoundary.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -5113,7 +5113,7 @@ } }, { - "HashCode": 1559679303, + "HashCode": 739114230, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Web.ErrorBoundary.ErrorContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -5146,7 +5146,7 @@ } }, { - "HashCode": 795321109, + "HashCode": 71662915, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Web.ErrorBoundary.ErrorContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -5180,7 +5180,7 @@ } }, { - "HashCode": -467077226, + "HashCode": -282130780, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -5333,7 +5333,7 @@ } }, { - "HashCode": -26390141, + "HashCode": -2060074971, "Kind": "Components.Component", "Name": "Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -5487,7 +5487,7 @@ } }, { - "HashCode": 556954818, + "HashCode": -57171986, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -5520,7 +5520,7 @@ } }, { - "HashCode": 207593977, + "HashCode": -1763324369, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize.ChildContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -5554,7 +5554,7 @@ } }, { - "HashCode": 1755539308, + "HashCode": -1265985932, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize.ItemContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -5587,7 +5587,7 @@ } }, { - "HashCode": -2000969995, + "HashCode": -2024998222, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize.ItemContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -5621,7 +5621,7 @@ } }, { - "HashCode": 2096234942, + "HashCode": 1979433418, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize.Placeholder", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -5654,7 +5654,7 @@ } }, { - "HashCode": -793241526, + "HashCode": -1576274473, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize.Placeholder", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -5688,7 +5688,7 @@ } }, { - "HashCode": 1971604695, + "HashCode": 1182671717, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize.EmptyContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -5709,7 +5709,7 @@ } }, { - "HashCode": -772618829, + "HashCode": 1075486262, "Kind": "Components.ChildContent", "Name": "Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize.EmptyContent", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -5731,7 +5731,7 @@ } }, { - "HashCode": -222292131, + "HashCode": -710696397, "Kind": "Components.EventHandler", "Name": "onfocus", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -5814,7 +5814,7 @@ } }, { - "HashCode": 1874578737, + "HashCode": -790863278, "Kind": "Components.EventHandler", "Name": "onblur", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -5897,7 +5897,7 @@ } }, { - "HashCode": -2059473001, + "HashCode": 307936767, "Kind": "Components.EventHandler", "Name": "onfocusin", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -5980,7 +5980,7 @@ } }, { - "HashCode": -1179890641, + "HashCode": 1684878061, "Kind": "Components.EventHandler", "Name": "onfocusout", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -6063,7 +6063,7 @@ } }, { - "HashCode": -1376013319, + "HashCode": 782242507, "Kind": "Components.EventHandler", "Name": "onmouseover", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -6146,7 +6146,7 @@ } }, { - "HashCode": 603254107, + "HashCode": -403108300, "Kind": "Components.EventHandler", "Name": "onmouseout", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -6229,7 +6229,7 @@ } }, { - "HashCode": -2019751294, + "HashCode": -1331475847, "Kind": "Components.EventHandler", "Name": "onmouseleave", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -6312,7 +6312,7 @@ } }, { - "HashCode": -1193835254, + "HashCode": -1921171039, "Kind": "Components.EventHandler", "Name": "onmouseenter", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -6395,7 +6395,7 @@ } }, { - "HashCode": -1153154954, + "HashCode": 1212953328, "Kind": "Components.EventHandler", "Name": "onmousemove", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -6478,7 +6478,7 @@ } }, { - "HashCode": 1993893232, + "HashCode": 162527122, "Kind": "Components.EventHandler", "Name": "onmousedown", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -6561,7 +6561,7 @@ } }, { - "HashCode": -833637054, + "HashCode": 1309047492, "Kind": "Components.EventHandler", "Name": "onmouseup", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -6644,7 +6644,7 @@ } }, { - "HashCode": 29578524, + "HashCode": 1332193223, "Kind": "Components.EventHandler", "Name": "onclick", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -6727,7 +6727,7 @@ } }, { - "HashCode": 278961686, + "HashCode": 922451166, "Kind": "Components.EventHandler", "Name": "ondblclick", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -6810,7 +6810,7 @@ } }, { - "HashCode": -1801288217, + "HashCode": -855187832, "Kind": "Components.EventHandler", "Name": "onwheel", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -6893,7 +6893,7 @@ } }, { - "HashCode": 974886, + "HashCode": -1881465727, "Kind": "Components.EventHandler", "Name": "onmousewheel", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -6976,7 +6976,7 @@ } }, { - "HashCode": -842097875, + "HashCode": 1937603245, "Kind": "Components.EventHandler", "Name": "oncontextmenu", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -7059,7 +7059,7 @@ } }, { - "HashCode": -1621999751, + "HashCode": -1045968280, "Kind": "Components.EventHandler", "Name": "ondrag", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -7142,7 +7142,7 @@ } }, { - "HashCode": -1326627868, + "HashCode": -1257236332, "Kind": "Components.EventHandler", "Name": "ondragend", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -7225,7 +7225,7 @@ } }, { - "HashCode": -1163258073, + "HashCode": 496891354, "Kind": "Components.EventHandler", "Name": "ondragenter", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -7308,7 +7308,7 @@ } }, { - "HashCode": -1254166905, + "HashCode": 944943740, "Kind": "Components.EventHandler", "Name": "ondragleave", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -7391,7 +7391,7 @@ } }, { - "HashCode": 1618752617, + "HashCode": 1126260752, "Kind": "Components.EventHandler", "Name": "ondragover", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -7474,7 +7474,7 @@ } }, { - "HashCode": -1030369724, + "HashCode": 1199352606, "Kind": "Components.EventHandler", "Name": "ondragstart", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -7557,7 +7557,7 @@ } }, { - "HashCode": 64643039, + "HashCode": -2092820847, "Kind": "Components.EventHandler", "Name": "ondrop", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -7640,7 +7640,7 @@ } }, { - "HashCode": 703477556, + "HashCode": -2027489759, "Kind": "Components.EventHandler", "Name": "onkeydown", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -7723,7 +7723,7 @@ } }, { - "HashCode": -1357638230, + "HashCode": 298445123, "Kind": "Components.EventHandler", "Name": "onkeyup", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -7806,7 +7806,7 @@ } }, { - "HashCode": -930243044, + "HashCode": 2058114097, "Kind": "Components.EventHandler", "Name": "onkeypress", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -7889,7 +7889,7 @@ } }, { - "HashCode": 1262592470, + "HashCode": 1893136683, "Kind": "Components.EventHandler", "Name": "onchange", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -7972,7 +7972,7 @@ } }, { - "HashCode": 1622316058, + "HashCode": -1547131950, "Kind": "Components.EventHandler", "Name": "oninput", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -8055,7 +8055,7 @@ } }, { - "HashCode": 1420893055, + "HashCode": 1822727547, "Kind": "Components.EventHandler", "Name": "oninvalid", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -8138,7 +8138,7 @@ } }, { - "HashCode": -275077639, + "HashCode": -98912904, "Kind": "Components.EventHandler", "Name": "onreset", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -8221,7 +8221,7 @@ } }, { - "HashCode": -2033172477, + "HashCode": -542941436, "Kind": "Components.EventHandler", "Name": "onselect", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -8304,7 +8304,7 @@ } }, { - "HashCode": -91141038, + "HashCode": -666124271, "Kind": "Components.EventHandler", "Name": "onselectstart", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -8387,7 +8387,7 @@ } }, { - "HashCode": -1487932961, + "HashCode": -86686280, "Kind": "Components.EventHandler", "Name": "onselectionchange", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -8470,7 +8470,7 @@ } }, { - "HashCode": 273747195, + "HashCode": 1887418422, "Kind": "Components.EventHandler", "Name": "onsubmit", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -8553,7 +8553,7 @@ } }, { - "HashCode": -1423386949, + "HashCode": 1714460279, "Kind": "Components.EventHandler", "Name": "onbeforecopy", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -8636,7 +8636,7 @@ } }, { - "HashCode": -1352305633, + "HashCode": 1052448705, "Kind": "Components.EventHandler", "Name": "onbeforecut", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -8719,7 +8719,7 @@ } }, { - "HashCode": -645852064, + "HashCode": -1177008591, "Kind": "Components.EventHandler", "Name": "onbeforepaste", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -8802,7 +8802,7 @@ } }, { - "HashCode": 779206804, + "HashCode": -1086610149, "Kind": "Components.EventHandler", "Name": "oncopy", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -8885,7 +8885,7 @@ } }, { - "HashCode": -849492734, + "HashCode": -429160624, "Kind": "Components.EventHandler", "Name": "oncut", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -8968,7 +8968,7 @@ } }, { - "HashCode": 1010606584, + "HashCode": 595740104, "Kind": "Components.EventHandler", "Name": "onpaste", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -9051,7 +9051,7 @@ } }, { - "HashCode": -377680192, + "HashCode": -826799013, "Kind": "Components.EventHandler", "Name": "ontouchcancel", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -9134,7 +9134,7 @@ } }, { - "HashCode": 139558365, + "HashCode": 1788665551, "Kind": "Components.EventHandler", "Name": "ontouchend", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -9217,7 +9217,7 @@ } }, { - "HashCode": 2076257839, + "HashCode": 198945168, "Kind": "Components.EventHandler", "Name": "ontouchmove", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -9300,7 +9300,7 @@ } }, { - "HashCode": 1448848796, + "HashCode": 23669180, "Kind": "Components.EventHandler", "Name": "ontouchstart", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -9383,7 +9383,7 @@ } }, { - "HashCode": -1355564139, + "HashCode": 1079696955, "Kind": "Components.EventHandler", "Name": "ontouchenter", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -9466,7 +9466,7 @@ } }, { - "HashCode": -2070701506, + "HashCode": 1857503947, "Kind": "Components.EventHandler", "Name": "ontouchleave", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -9549,7 +9549,7 @@ } }, { - "HashCode": 1383059830, + "HashCode": -1561347843, "Kind": "Components.EventHandler", "Name": "ongotpointercapture", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -9632,7 +9632,7 @@ } }, { - "HashCode": -376796268, + "HashCode": -270526112, "Kind": "Components.EventHandler", "Name": "onlostpointercapture", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -9715,7 +9715,7 @@ } }, { - "HashCode": -1689183404, + "HashCode": -103938221, "Kind": "Components.EventHandler", "Name": "onpointercancel", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -9798,7 +9798,7 @@ } }, { - "HashCode": 1003227680, + "HashCode": -1655256037, "Kind": "Components.EventHandler", "Name": "onpointerdown", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -9881,7 +9881,7 @@ } }, { - "HashCode": -952762779, + "HashCode": 1259998131, "Kind": "Components.EventHandler", "Name": "onpointerenter", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -9964,7 +9964,7 @@ } }, { - "HashCode": 1312621879, + "HashCode": -1411356384, "Kind": "Components.EventHandler", "Name": "onpointerleave", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -10047,7 +10047,7 @@ } }, { - "HashCode": 1220858826, + "HashCode": -1599915802, "Kind": "Components.EventHandler", "Name": "onpointermove", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -10130,7 +10130,7 @@ } }, { - "HashCode": -1332495075, + "HashCode": -1366490865, "Kind": "Components.EventHandler", "Name": "onpointerout", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -10213,7 +10213,7 @@ } }, { - "HashCode": 712054201, + "HashCode": 2014541707, "Kind": "Components.EventHandler", "Name": "onpointerover", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -10296,7 +10296,7 @@ } }, { - "HashCode": -766863310, + "HashCode": 2018705398, "Kind": "Components.EventHandler", "Name": "onpointerup", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -10379,7 +10379,7 @@ } }, { - "HashCode": 364633696, + "HashCode": -2112386497, "Kind": "Components.EventHandler", "Name": "oncanplay", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -10462,7 +10462,7 @@ } }, { - "HashCode": -181754123, + "HashCode": -360933916, "Kind": "Components.EventHandler", "Name": "oncanplaythrough", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -10545,7 +10545,7 @@ } }, { - "HashCode": -1651361106, + "HashCode": -1644960550, "Kind": "Components.EventHandler", "Name": "oncuechange", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -10628,7 +10628,7 @@ } }, { - "HashCode": -308793792, + "HashCode": 2026404520, "Kind": "Components.EventHandler", "Name": "ondurationchange", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -10711,7 +10711,7 @@ } }, { - "HashCode": -1779587125, + "HashCode": -1175555194, "Kind": "Components.EventHandler", "Name": "onemptied", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -10794,7 +10794,7 @@ } }, { - "HashCode": 1300954550, + "HashCode": -66260819, "Kind": "Components.EventHandler", "Name": "onpause", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -10877,7 +10877,7 @@ } }, { - "HashCode": -1628949411, + "HashCode": -1751999758, "Kind": "Components.EventHandler", "Name": "onplay", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -10960,7 +10960,7 @@ } }, { - "HashCode": -1800407256, + "HashCode": 1452386410, "Kind": "Components.EventHandler", "Name": "onplaying", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -11043,7 +11043,7 @@ } }, { - "HashCode": 245093435, + "HashCode": 650808400, "Kind": "Components.EventHandler", "Name": "onratechange", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -11126,7 +11126,7 @@ } }, { - "HashCode": 1745281628, + "HashCode": -1020677187, "Kind": "Components.EventHandler", "Name": "onseeked", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -11209,7 +11209,7 @@ } }, { - "HashCode": -2028097530, + "HashCode": 799660430, "Kind": "Components.EventHandler", "Name": "onseeking", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -11292,7 +11292,7 @@ } }, { - "HashCode": -422299404, + "HashCode": 1985804932, "Kind": "Components.EventHandler", "Name": "onstalled", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -11375,7 +11375,7 @@ } }, { - "HashCode": 1804336775, + "HashCode": -1305536457, "Kind": "Components.EventHandler", "Name": "onstop", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -11458,7 +11458,7 @@ } }, { - "HashCode": 1816444236, + "HashCode": 899319501, "Kind": "Components.EventHandler", "Name": "onsuspend", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -11541,7 +11541,7 @@ } }, { - "HashCode": -1971501417, + "HashCode": 1737441528, "Kind": "Components.EventHandler", "Name": "ontimeupdate", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -11624,7 +11624,7 @@ } }, { - "HashCode": -1930153932, + "HashCode": -1724675130, "Kind": "Components.EventHandler", "Name": "onvolumechange", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -11707,7 +11707,7 @@ } }, { - "HashCode": 1843394571, + "HashCode": -1375514603, "Kind": "Components.EventHandler", "Name": "onwaiting", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -11790,7 +11790,7 @@ } }, { - "HashCode": -1879843179, + "HashCode": 1025132179, "Kind": "Components.EventHandler", "Name": "onloadstart", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -11873,7 +11873,7 @@ } }, { - "HashCode": 2011015997, + "HashCode": -124906965, "Kind": "Components.EventHandler", "Name": "ontimeout", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -11956,7 +11956,7 @@ } }, { - "HashCode": -716854861, + "HashCode": -720921655, "Kind": "Components.EventHandler", "Name": "onabort", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -12039,7 +12039,7 @@ } }, { - "HashCode": -341245904, + "HashCode": 13566664, "Kind": "Components.EventHandler", "Name": "onload", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -12122,7 +12122,7 @@ } }, { - "HashCode": 716653105, + "HashCode": -1389679136, "Kind": "Components.EventHandler", "Name": "onloadend", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -12205,7 +12205,7 @@ } }, { - "HashCode": 829744998, + "HashCode": -1345083992, "Kind": "Components.EventHandler", "Name": "onprogress", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -12288,7 +12288,7 @@ } }, { - "HashCode": -221612451, + "HashCode": -2106694373, "Kind": "Components.EventHandler", "Name": "onerror", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -12371,7 +12371,7 @@ } }, { - "HashCode": -823588493, + "HashCode": 475485833, "Kind": "Components.EventHandler", "Name": "onactivate", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -12454,7 +12454,7 @@ } }, { - "HashCode": -611483521, + "HashCode": -1217105854, "Kind": "Components.EventHandler", "Name": "onbeforeactivate", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -12537,7 +12537,7 @@ } }, { - "HashCode": -419594993, + "HashCode": 772509483, "Kind": "Components.EventHandler", "Name": "onbeforedeactivate", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -12620,7 +12620,7 @@ } }, { - "HashCode": -1409848665, + "HashCode": 539445269, "Kind": "Components.EventHandler", "Name": "ondeactivate", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -12703,7 +12703,7 @@ } }, { - "HashCode": -1849601647, + "HashCode": -499890672, "Kind": "Components.EventHandler", "Name": "onended", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -12786,7 +12786,7 @@ } }, { - "HashCode": -123888754, + "HashCode": 1896517560, "Kind": "Components.EventHandler", "Name": "onfullscreenchange", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -12869,7 +12869,7 @@ } }, { - "HashCode": -1392533249, + "HashCode": -1285990035, "Kind": "Components.EventHandler", "Name": "onfullscreenerror", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -12952,7 +12952,7 @@ } }, { - "HashCode": -1330967849, + "HashCode": -1918614012, "Kind": "Components.EventHandler", "Name": "onloadeddata", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -13035,7 +13035,7 @@ } }, { - "HashCode": -1549279633, + "HashCode": -998762212, "Kind": "Components.EventHandler", "Name": "onloadedmetadata", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -13118,7 +13118,7 @@ } }, { - "HashCode": 1487979083, + "HashCode": -139796883, "Kind": "Components.EventHandler", "Name": "onpointerlockchange", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -13201,7 +13201,7 @@ } }, { - "HashCode": 49729496, + "HashCode": 1915123833, "Kind": "Components.EventHandler", "Name": "onpointerlockerror", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -13284,7 +13284,7 @@ } }, { - "HashCode": 738879005, + "HashCode": -738796316, "Kind": "Components.EventHandler", "Name": "onreadystatechange", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -13367,7 +13367,7 @@ } }, { - "HashCode": 51693000, + "HashCode": 126651297, "Kind": "Components.EventHandler", "Name": "onscroll", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -13450,7 +13450,7 @@ } }, { - "HashCode": -2076287818, + "HashCode": 1807248062, "Kind": "Components.EventHandler", "Name": "ontoggle", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -13533,7 +13533,7 @@ } }, { - "HashCode": 339394232, + "HashCode": 1074737515, "Kind": "Components.EventHandler", "Name": "oncancel", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -13597,7 +13597,7 @@ } }, { - "HashCode": -901596015, + "HashCode": 58274681, "Kind": "Components.EventHandler", "Name": "onclose", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -13661,7 +13661,7 @@ } }, { - "HashCode": 92653380, + "HashCode": 112462007, "Kind": "Components.Splat", "Name": "Attributes", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -13700,7 +13700,7 @@ } }, { - "HashCode": 1126111625, + "HashCode": 1484194870, "Kind": "ITagHelper", "Name": "Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResolutionTagHelper", "AssemblyName": "Microsoft.AspNetCore.Mvc.Razor", @@ -14017,7 +14017,7 @@ } }, { - "HashCode": -1158900746, + "HashCode": -1040344616, "Kind": "ITagHelper", "Name": "Microsoft.AspNetCore.Mvc.TagHelpers.AnchorTagHelper", "AssemblyName": "Microsoft.AspNetCore.Mvc.TagHelpers", @@ -14216,7 +14216,7 @@ } }, { - "HashCode": 1039721999, + "HashCode": -1176657263, "Kind": "ITagHelper", "Name": "Microsoft.AspNetCore.Mvc.TagHelpers.CacheTagHelper", "AssemblyName": "Microsoft.AspNetCore.Mvc.TagHelpers", @@ -14345,7 +14345,7 @@ } }, { - "HashCode": -1895435129, + "HashCode": 1017411356, "Kind": "ITagHelper", "Name": "Microsoft.AspNetCore.Mvc.TagHelpers.ComponentTagHelper", "AssemblyName": "Microsoft.AspNetCore.Mvc.TagHelpers", @@ -14402,7 +14402,7 @@ } }, { - "HashCode": 918333386, + "HashCode": -1761682311, "Kind": "ITagHelper", "Name": "Microsoft.AspNetCore.Mvc.TagHelpers.DistributedCacheTagHelper", "AssemblyName": "Microsoft.AspNetCore.Mvc.TagHelpers", @@ -14536,7 +14536,7 @@ } }, { - "HashCode": -1958160837, + "HashCode": 1382602730, "Kind": "ITagHelper", "Name": "Microsoft.AspNetCore.Mvc.TagHelpers.EnvironmentTagHelper", "AssemblyName": "Microsoft.AspNetCore.Mvc.TagHelpers", @@ -14584,7 +14584,7 @@ } }, { - "HashCode": 1959632180, + "HashCode": 1028453366, "Kind": "ITagHelper", "Name": "Microsoft.AspNetCore.Mvc.TagHelpers.FormActionTagHelper", "AssemblyName": "Microsoft.AspNetCore.Mvc.TagHelpers", @@ -15003,7 +15003,7 @@ } }, { - "HashCode": 1996419035, + "HashCode": 237699670, "Kind": "ITagHelper", "Name": "Microsoft.AspNetCore.Mvc.TagHelpers.FormTagHelper", "AssemblyName": "Microsoft.AspNetCore.Mvc.TagHelpers", @@ -15107,7 +15107,7 @@ } }, { - "HashCode": 1628431274, + "HashCode": -1574392494, "Kind": "ITagHelper", "Name": "Microsoft.AspNetCore.Mvc.TagHelpers.ImageTagHelper", "AssemblyName": "Microsoft.AspNetCore.Mvc.TagHelpers", @@ -15155,7 +15155,7 @@ } }, { - "HashCode": 1979903215, + "HashCode": 246565010, "Kind": "ITagHelper", "Name": "Microsoft.AspNetCore.Mvc.TagHelpers.InputTagHelper", "AssemblyName": "Microsoft.AspNetCore.Mvc.TagHelpers", @@ -15236,7 +15236,7 @@ } }, { - "HashCode": -841708015, + "HashCode": -669320060, "Kind": "ITagHelper", "Name": "Microsoft.AspNetCore.Mvc.TagHelpers.LabelTagHelper", "AssemblyName": "Microsoft.AspNetCore.Mvc.TagHelpers", @@ -15271,7 +15271,7 @@ } }, { - "HashCode": -2121726888, + "HashCode": 1669442708, "Kind": "ITagHelper", "Name": "Microsoft.AspNetCore.Mvc.TagHelpers.LinkTagHelper", "AssemblyName": "Microsoft.AspNetCore.Mvc.TagHelpers", @@ -15469,7 +15469,7 @@ } }, { - "HashCode": 1247697248, + "HashCode": -205877354, "Kind": "ITagHelper", "Name": "Microsoft.AspNetCore.Mvc.TagHelpers.OptionTagHelper", "AssemblyName": "Microsoft.AspNetCore.Mvc.TagHelpers", @@ -15499,7 +15499,7 @@ } }, { - "HashCode": -1592780819, + "HashCode": 961555497, "Kind": "ITagHelper", "Name": "Microsoft.AspNetCore.Mvc.TagHelpers.PartialTagHelper", "AssemblyName": "Microsoft.AspNetCore.Mvc.TagHelpers", @@ -15582,7 +15582,7 @@ } }, { - "HashCode": -972524372, + "HashCode": 993821313, "Kind": "ITagHelper", "Name": "Microsoft.AspNetCore.Mvc.TagHelpers.PersistComponentStateTagHelper", "AssemblyName": "Microsoft.AspNetCore.Mvc.TagHelpers", @@ -15613,7 +15613,7 @@ } }, { - "HashCode": 1110478846, + "HashCode": 1352895388, "Kind": "ITagHelper", "Name": "Microsoft.AspNetCore.Mvc.TagHelpers.ScriptTagHelper", "AssemblyName": "Microsoft.AspNetCore.Mvc.TagHelpers", @@ -15802,7 +15802,7 @@ } }, { - "HashCode": -981815151, + "HashCode": 1981770082, "Kind": "ITagHelper", "Name": "Microsoft.AspNetCore.Mvc.TagHelpers.SelectTagHelper", "AssemblyName": "Microsoft.AspNetCore.Mvc.TagHelpers", @@ -15863,7 +15863,7 @@ } }, { - "HashCode": 813569028, + "HashCode": -1731021005, "Kind": "ITagHelper", "Name": "Microsoft.AspNetCore.Mvc.TagHelpers.TextAreaTagHelper", "AssemblyName": "Microsoft.AspNetCore.Mvc.TagHelpers", @@ -15907,7 +15907,7 @@ } }, { - "HashCode": 1411146423, + "HashCode": -1286587025, "Kind": "ITagHelper", "Name": "Microsoft.AspNetCore.Mvc.TagHelpers.ValidationMessageTagHelper", "AssemblyName": "Microsoft.AspNetCore.Mvc.TagHelpers", @@ -15942,7 +15942,7 @@ } }, { - "HashCode": -308126972, + "HashCode": 1081566059, "Kind": "ITagHelper", "Name": "Microsoft.AspNetCore.Mvc.TagHelpers.ValidationSummaryTagHelper", "AssemblyName": "Microsoft.AspNetCore.Mvc.TagHelpers", @@ -15978,7 +15978,7 @@ } }, { - "HashCode": -1729356902, + "HashCode": 107609633, "Kind": "Components.Bind", "Name": "Bind", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -16074,7 +16074,7 @@ } }, { - "HashCode": 284016882, + "HashCode": 207547847, "Kind": "Components.Bind", "Name": "Bind", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -16196,7 +16196,7 @@ } }, { - "HashCode": -1501856915, + "HashCode": -1456376174, "Kind": "Components.Bind", "Name": "Bind_value", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -16318,7 +16318,7 @@ } }, { - "HashCode": 1792760901, + "HashCode": 2019687687, "Kind": "Components.Bind", "Name": "Bind", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -16451,7 +16451,7 @@ } }, { - "HashCode": -1353409521, + "HashCode": 1470404201, "Kind": "Components.Bind", "Name": "Bind", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -16584,7 +16584,7 @@ } }, { - "HashCode": 920321315, + "HashCode": -783518459, "Kind": "Components.Bind", "Name": "Bind", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -16717,7 +16717,7 @@ } }, { - "HashCode": 1677048572, + "HashCode": 406038852, "Kind": "Components.Bind", "Name": "Bind_value", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -16850,7 +16850,7 @@ } }, { - "HashCode": -1692971563, + "HashCode": 547226184, "Kind": "Components.Bind", "Name": "Bind", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -16983,7 +16983,7 @@ } }, { - "HashCode": -2123763544, + "HashCode": 140103820, "Kind": "Components.Bind", "Name": "Bind_value", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -17116,7 +17116,7 @@ } }, { - "HashCode": 2125212290, + "HashCode": -813510802, "Kind": "Components.Bind", "Name": "Bind", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -17249,7 +17249,7 @@ } }, { - "HashCode": -1360088123, + "HashCode": 826004962, "Kind": "Components.Bind", "Name": "Bind_value", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -17382,7 +17382,7 @@ } }, { - "HashCode": 1726479052, + "HashCode": -2045737036, "Kind": "Components.Bind", "Name": "Bind", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -17515,7 +17515,7 @@ } }, { - "HashCode": -1691616552, + "HashCode": -1560706012, "Kind": "Components.Bind", "Name": "Bind_value", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -17648,7 +17648,7 @@ } }, { - "HashCode": 1630464412, + "HashCode": -579101799, "Kind": "Components.Bind", "Name": "Bind", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -17781,7 +17781,7 @@ } }, { - "HashCode": 375105379, + "HashCode": 1456962486, "Kind": "Components.Bind", "Name": "Bind_value", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -17914,7 +17914,7 @@ } }, { - "HashCode": 772614203, + "HashCode": 1664720526, "Kind": "Components.Bind", "Name": "Bind", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -18036,7 +18036,7 @@ } }, { - "HashCode": 757227773, + "HashCode": 1271311146, "Kind": "Components.Bind", "Name": "Bind", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -18158,7 +18158,7 @@ } }, { - "HashCode": 1399004936, + "HashCode": -1102334035, "Kind": "Components.Bind", "Name": "Microsoft.AspNetCore.Components.Forms.InputCheckbox", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -18245,7 +18245,7 @@ } }, { - "HashCode": 1872815984, + "HashCode": 677594569, "Kind": "Components.Bind", "Name": "Microsoft.AspNetCore.Components.Forms.InputCheckbox", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -18333,7 +18333,7 @@ } }, { - "HashCode": -1425576639, + "HashCode": 1605832604, "Kind": "Components.Bind", "Name": "Microsoft.AspNetCore.Components.Forms.InputDate", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -18420,7 +18420,7 @@ } }, { - "HashCode": 1144093020, + "HashCode": 462407096, "Kind": "Components.Bind", "Name": "Microsoft.AspNetCore.Components.Forms.InputDate", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -18508,7 +18508,7 @@ } }, { - "HashCode": -220609943, + "HashCode": -1541580324, "Kind": "Components.Bind", "Name": "Microsoft.AspNetCore.Components.Forms.InputNumber", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -18595,7 +18595,7 @@ } }, { - "HashCode": -1426816993, + "HashCode": 587451012, "Kind": "Components.Bind", "Name": "Microsoft.AspNetCore.Components.Forms.InputNumber", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -18683,7 +18683,7 @@ } }, { - "HashCode": 1811312744, + "HashCode": -955902564, "Kind": "Components.Bind", "Name": "Microsoft.AspNetCore.Components.Forms.InputRadioGroup", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -18770,7 +18770,7 @@ } }, { - "HashCode": -1563477633, + "HashCode": -2039374670, "Kind": "Components.Bind", "Name": "Microsoft.AspNetCore.Components.Forms.InputRadioGroup", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -18858,7 +18858,7 @@ } }, { - "HashCode": 469140694, + "HashCode": 1099817699, "Kind": "Components.Bind", "Name": "Microsoft.AspNetCore.Components.Forms.InputSelect", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -18945,7 +18945,7 @@ } }, { - "HashCode": -1409845856, + "HashCode": 1443466231, "Kind": "Components.Bind", "Name": "Microsoft.AspNetCore.Components.Forms.InputSelect", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -19033,7 +19033,7 @@ } }, { - "HashCode": 1408465457, + "HashCode": -443615211, "Kind": "Components.Bind", "Name": "Microsoft.AspNetCore.Components.Forms.InputText", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -19120,7 +19120,7 @@ } }, { - "HashCode": 2004696583, + "HashCode": -398562487, "Kind": "Components.Bind", "Name": "Microsoft.AspNetCore.Components.Forms.InputText", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -19208,7 +19208,7 @@ } }, { - "HashCode": -1214350260, + "HashCode": 26608493, "Kind": "Components.Bind", "Name": "Microsoft.AspNetCore.Components.Forms.InputTextArea", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -19295,7 +19295,7 @@ } }, { - "HashCode": 808575230, + "HashCode": -70604439, "Kind": "Components.Bind", "Name": "Microsoft.AspNetCore.Components.Forms.InputTextArea", "AssemblyName": "Microsoft.AspNetCore.Components.Web", @@ -19383,7 +19383,7 @@ } }, { - "HashCode": -411351300, + "HashCode": -1951957771, "Kind": "Components.Ref", "Name": "Ref", "AssemblyName": "Microsoft.AspNetCore.Components", @@ -19422,7 +19422,7 @@ } }, { - "HashCode": 776839100, + "HashCode": 1878886064, "Kind": "Components.Key", "Name": "Key", "AssemblyName": "Microsoft.AspNetCore.Components", diff --git a/FutureMailAPI/obj/Debug/net9.0/ref/FutureMailAPI.dll b/FutureMailAPI/obj/Debug/net9.0/ref/FutureMailAPI.dll index 2437e0d..0df7c56 100644 Binary files a/FutureMailAPI/obj/Debug/net9.0/ref/FutureMailAPI.dll and b/FutureMailAPI/obj/Debug/net9.0/ref/FutureMailAPI.dll differ diff --git a/FutureMailAPI/obj/Debug/net9.0/refint/FutureMailAPI.dll b/FutureMailAPI/obj/Debug/net9.0/refint/FutureMailAPI.dll index 2437e0d..0df7c56 100644 Binary files a/FutureMailAPI/obj/Debug/net9.0/refint/FutureMailAPI.dll and b/FutureMailAPI/obj/Debug/net9.0/refint/FutureMailAPI.dll differ diff --git a/FutureMailAPI/obj/Debug/net9.0/rjsmcshtml.dswa.cache.json b/FutureMailAPI/obj/Debug/net9.0/rjsmcshtml.dswa.cache.json index 63807e1..70991dd 100644 --- a/FutureMailAPI/obj/Debug/net9.0/rjsmcshtml.dswa.cache.json +++ b/FutureMailAPI/obj/Debug/net9.0/rjsmcshtml.dswa.cache.json @@ -1 +1 @@ -{"GlobalPropertiesHash":"1nyXR9zdL54Badakr4zt6ZsTCwUunwdqRSmf7XLLUwI=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["PSBb4S8lcZQPImBE8id7O4eeN8h3whFn6j1jGYFQciQ=","FLPLXKwQVK16UZsWKzZrjH5kq4sMEtCZqAIkpUwa2pU=","qvO3Mvo7bXJuih7HQYVXFfIR0uMf6fdipVLWW\u002BbFgXY=","XAE6ulqLzJRH50\u002Bc9Nteizd/x9s3rvSHUFwFL265XX4=","talZRfyIQIv4aZc27HTn01\u002B12VbY6LMFOy3\u002B3r448jo=","kyazWY\u002B8n0dtdadKg35ovAVMceUhOAzyYnxEaTobs08="],"CachedAssets":{},"CachedCopyCandidates":{}} \ No newline at end of file +{"GlobalPropertiesHash":"1nyXR9zdL54Badakr4zt6ZsTCwUunwdqRSmf7XLLUwI=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["PSBb4S8lcZQPImBE8id7O4eeN8h3whFn6j1jGYFQciQ=","FLPLXKwQVK16UZsWKzZrjH5kq4sMEtCZqAIkpUwa2pU=","ed7if\u002BhRbLX5eYApJ6Bg4oF5be3kjR0VKVKgf\u002BgmyVo=","XAE6ulqLzJRH50\u002Bc9Nteizd/x9s3rvSHUFwFL265XX4=","talZRfyIQIv4aZc27HTn01\u002B12VbY6LMFOy3\u002B3r448jo=","R7hxHyYox4qeYIBY\u002Bo3l0eo8ZIIfGZeN7PS3V61Q7Ks="],"CachedAssets":{},"CachedCopyCandidates":{}} \ No newline at end of file diff --git a/FutureMailAPI/obj/Debug/net9.0/rjsmrazor.dswa.cache.json b/FutureMailAPI/obj/Debug/net9.0/rjsmrazor.dswa.cache.json index 9950da3..ae7b018 100644 --- a/FutureMailAPI/obj/Debug/net9.0/rjsmrazor.dswa.cache.json +++ b/FutureMailAPI/obj/Debug/net9.0/rjsmrazor.dswa.cache.json @@ -1 +1 @@ -{"GlobalPropertiesHash":"hRzLFfhtQD0jC2zNthCXf5A5W0LGZjQdHMs0v5Enof8=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["PSBb4S8lcZQPImBE8id7O4eeN8h3whFn6j1jGYFQciQ=","FLPLXKwQVK16UZsWKzZrjH5kq4sMEtCZqAIkpUwa2pU=","qvO3Mvo7bXJuih7HQYVXFfIR0uMf6fdipVLWW\u002BbFgXY=","XAE6ulqLzJRH50\u002Bc9Nteizd/x9s3rvSHUFwFL265XX4=","talZRfyIQIv4aZc27HTn01\u002B12VbY6LMFOy3\u002B3r448jo=","kyazWY\u002B8n0dtdadKg35ovAVMceUhOAzyYnxEaTobs08="],"CachedAssets":{},"CachedCopyCandidates":{}} \ No newline at end of file +{"GlobalPropertiesHash":"hRzLFfhtQD0jC2zNthCXf5A5W0LGZjQdHMs0v5Enof8=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["PSBb4S8lcZQPImBE8id7O4eeN8h3whFn6j1jGYFQciQ=","FLPLXKwQVK16UZsWKzZrjH5kq4sMEtCZqAIkpUwa2pU=","ed7if\u002BhRbLX5eYApJ6Bg4oF5be3kjR0VKVKgf\u002BgmyVo=","XAE6ulqLzJRH50\u002Bc9Nteizd/x9s3rvSHUFwFL265XX4=","talZRfyIQIv4aZc27HTn01\u002B12VbY6LMFOy3\u002B3r448jo=","R7hxHyYox4qeYIBY\u002Bo3l0eo8ZIIfGZeN7PS3V61Q7Ks="],"CachedAssets":{},"CachedCopyCandidates":{}} \ No newline at end of file diff --git a/simple_test.html b/simple_test.html new file mode 100644 index 0000000..4e830ba --- /dev/null +++ b/simple_test.html @@ -0,0 +1,307 @@ + + + + + + API测试 + + + +

API测试

+ +
+

用户注册

+
+ + +
+
+ + +
+
+ + +
+ +
+
+ +
+

用户登录

+
+ + +
+
+ + +
+ +
+
+ +
+

创建邮件

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+ +
+

获取邮件列表

+
+ + +
+ +
+
+ + + + \ No newline at end of file diff --git a/test_api.js b/test_api.js new file mode 100644 index 0000000..133b82d --- /dev/null +++ b/test_api.js @@ -0,0 +1,53 @@ +const axios = require('axios'); + +const API_BASE_URL = 'http://localhost:5003/api/v1'; + +async function testAPI() { + try { + // 1. 注册用户 + console.log('1. 注册用户...'); + const registerResponse = await axios.post(`${API_BASE_URL}/auth/register`, { + username: 'testuser', + email: 'test@example.com', + password: 'password123' + }); + const token = registerResponse.data.data.token; + console.log('注册成功,获取到token:', token); + + // 2. 创建邮件 + console.log('\n2. 创建邮件...'); + const mailData = { + title: '测试邮件', + content: '这是一封测试邮件内容', + recipientType: 'SELF', + sendTime: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), // 明天这个时候 + triggerType: 'TIME', + triggerCondition: {}, + attachments: [], + isEncrypted: false, + capsuleStyle: 'default' + }; + + const createMailResponse = await axios.post(`${API_BASE_URL}/mails/create`, mailData, { + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + } + }); + console.log('创建邮件成功:', createMailResponse.data); + + // 3. 获取邮件列表 + console.log('\n3. 获取邮件列表...'); + const mailsResponse = await axios.get(`${API_BASE_URL}/mails?type=SENT&page=1&size=20`, { + headers: { + 'Authorization': `Bearer ${token}` + } + }); + console.log('获取邮件列表成功:', mailsResponse.data); + + } catch (error) { + console.error('测试失败:', error.response?.data || error.message); + } +} + +testAPI(); \ No newline at end of file diff --git a/test_api.ps1 b/test_api.ps1 new file mode 100644 index 0000000..619ee28 --- /dev/null +++ b/test_api.ps1 @@ -0,0 +1,56 @@ +# 测试API的PowerShell脚本 + +$API_BASE_URL = "http://localhost:5003/api/v1" + +try { + # 1. 注册用户 + Write-Host "1. 注册用户..." + $registerBody = @{ + username = "testuser" + email = "test@example.com" + password = "password123" + } | ConvertTo-Json + + $registerResponse = Invoke-RestMethod -Uri "$API_BASE_URL/auth/register" -Method Post -Body $registerBody -ContentType "application/json" + $token = $registerResponse.data.token + Write-Host "注册成功,获取到token: $token" + + # 2. 创建邮件 + Write-Host "`n2. 创建邮件..." + $sendTime = (Get-Date).AddDays(1).ToString("yyyy-MM-ddTHH:mm:ss.fffZ") + $mailData = @{ + title = "测试邮件" + content = "这是一封测试邮件内容" + recipientType = "SELF" + sendTime = $sendTime + triggerType = "TIME" + triggerCondition = @{} + attachments = @() + isEncrypted = $false + capsuleStyle = "default" + } | ConvertTo-Json -Depth 10 + + $headers = @{ + "Authorization" = "Bearer $token" + "Content-Type" = "application/json" + } + + $createMailResponse = Invoke-RestMethod -Uri "$API_BASE_URL/mails/create" -Method Post -Body $mailData -Headers $headers + Write-Host "创建邮件成功:" + $createMailResponse | ConvertTo-Json -Depth 10 + + # 3. 获取邮件列表 + Write-Host "`n3. 获取邮件列表..." + $mailsResponse = Invoke-RestMethod -Uri "$API_BASE_URL/mails?type=SENT&page=1&size=20" -Method Get -Headers $headers + Write-Host "获取邮件列表成功:" + $mailsResponse | ConvertTo-Json -Depth 10 + +} catch { + Write-Host "测试失败: $($_.Exception.Message)" + if ($_.Exception.Response) { + $reader = New-Object System.IO.StreamReader($_.Exception.Response.GetResponseStream()) + $reader.BaseStream.Position = 0 + $errorBody = $reader.ReadToEnd() + Write-Host "错误详情: $errorBody" + } +} \ No newline at end of file