|
|
@@ -530,11 +530,6 @@ export type CreateChannelDto = z.infer<typeof CreateChannelSchema>;
|
|
|
@CreateDateColumn({ name: 'upload_time' })
|
|
|
uploadTime!: Date;
|
|
|
|
|
|
- // 计算属性:获取文件URL
|
|
|
- get photoUrl(): Promise<string> {
|
|
|
- return this.file?.fullUrl || Promise.resolve('');
|
|
|
- }
|
|
|
-
|
|
|
// 关联残疾人实体
|
|
|
@ManyToOne(() => DisabledPerson, person => person.photos)
|
|
|
@JoinColumn({ name: 'person_id' })
|
|
|
@@ -559,6 +554,41 @@ export type CreateChannelDto = z.infer<typeof CreateChannelSchema>;
|
|
|
const photo = await disabilityService.createPhoto(data);
|
|
|
return c.json(photo, 201);
|
|
|
});
|
|
|
+
|
|
|
+ // 获取照片详情(包含文件URL)
|
|
|
+ channelRoutes.get('/photos/:id', async (c) => {
|
|
|
+ const { id } = c.req.param();
|
|
|
+ const photo = await disabilityService.getPhotoWithFile(Number(id));
|
|
|
+
|
|
|
+ // 构建响应数据,包含文件URL
|
|
|
+ const response = {
|
|
|
+ ...photo,
|
|
|
+ photoUrl: await photo.file.fullUrl, // 通过关联实体获取文件URL
|
|
|
+ fileName: photo.file.name,
|
|
|
+ fileSize: photo.file.size,
|
|
|
+ fileType: photo.file.type
|
|
|
+ };
|
|
|
+
|
|
|
+ return c.json(response);
|
|
|
+ });
|
|
|
+
|
|
|
+ // 获取残疾人的所有照片(包含文件信息)
|
|
|
+ channelRoutes.get('/persons/:personId/photos', async (c) => {
|
|
|
+ const { personId } = c.req.param();
|
|
|
+ const photos = await disabilityService.getPersonPhotosWithFiles(Number(personId));
|
|
|
+
|
|
|
+ // 为每张照片添加文件URL
|
|
|
+ const photosWithUrls = await Promise.all(
|
|
|
+ photos.map(async (photo) => ({
|
|
|
+ ...photo,
|
|
|
+ photoUrl: await photo.file.fullUrl,
|
|
|
+ fileName: photo.file.name,
|
|
|
+ fileSize: photo.file.size
|
|
|
+ }))
|
|
|
+ );
|
|
|
+
|
|
|
+ return c.json(photosWithUrls);
|
|
|
+ });
|
|
|
```
|
|
|
|
|
|
4. **服务层实现**:
|
|
|
@@ -585,7 +615,8 @@ export type CreateChannelDto = z.infer<typeof CreateChannelSchema>;
|
|
|
return this.photoRepository.save(photo);
|
|
|
}
|
|
|
|
|
|
- async getPhotoWithUrl(photoId: number): Promise<DisabledPhoto> {
|
|
|
+ async getPhotoWithFile(photoId: number): Promise<DisabledPhoto> {
|
|
|
+ // 查询照片时加载关联的File实体
|
|
|
const photo = await this.photoRepository.findOne({
|
|
|
where: { photoId },
|
|
|
relations: ['file'] // 加载关联的File实体
|
|
|
@@ -597,6 +628,23 @@ export type CreateChannelDto = z.infer<typeof CreateChannelSchema>;
|
|
|
|
|
|
return photo;
|
|
|
}
|
|
|
+
|
|
|
+ async getPhotoUrl(photoId: number): Promise<string> {
|
|
|
+ const photo = await this.getPhotoWithFile(photoId);
|
|
|
+
|
|
|
+ // 通过关联的file实体访问fullUrl属性
|
|
|
+ // file.fullUrl是File实体中的Promise属性
|
|
|
+ return await photo.file.fullUrl;
|
|
|
+ }
|
|
|
+
|
|
|
+ async getPersonPhotosWithFiles(personId: number): Promise<DisabledPhoto[]> {
|
|
|
+ // 查询某个残疾人的所有照片,并加载关联的File实体
|
|
|
+ return this.photoRepository.find({
|
|
|
+ where: { personId },
|
|
|
+ relations: ['file'], // 加载关联的File实体
|
|
|
+ order: { uploadTime: 'DESC' }
|
|
|
+ });
|
|
|
+ }
|
|
|
}
|
|
|
```
|
|
|
|
|
|
@@ -677,8 +725,18 @@ API层(disability-module):
|
|
|
|
|
|
1. **数据迁移**:需要制定现有URL数据的迁移方案
|
|
|
2. **兼容性**:保持API兼容,逐步迁移
|
|
|
-3. **性能**:注意N+1查询问题,合理使用数据加载策略
|
|
|
-4. **错误处理**:处理文件服务不可用的情况
|
|
|
+3. **性能优化**:
|
|
|
+ - **关联查询**:使用`relations: ['file']`加载关联实体,避免N+1查询
|
|
|
+ - **选择性加载**:根据需要选择加载的关联字段
|
|
|
+ - **分页查询**:大数据量时使用分页,避免一次性加载所有关联数据
|
|
|
+4. **查询模式**:
|
|
|
+ - 通过关联的`file`实体访问文件属性(`file.fullUrl`、`file.name`等)
|
|
|
+ - 不需要在业务实体中定义重复的文件URL属性
|
|
|
+ - 使用TypeORM的`relations`选项加载关联数据
|
|
|
+5. **错误处理**:
|
|
|
+ - 验证`fileId`对应的文件是否存在
|
|
|
+ - 处理文件服务不可用的情况
|
|
|
+ - 处理关联实体加载失败的情况
|
|
|
|
|
|
## 兼容性要求
|
|
|
|