refactor travel

This commit is contained in:
Timi
2025-12-13 18:45:50 +08:00
parent d12c76fe03
commit 658765df6f
17 changed files with 179 additions and 155 deletions

2
.gitignore vendored
View File

@ -1,8 +1,10 @@
/config
/data
/docs
/logs
/target
/temp
CLAUDE.md
multilingualField/
!.mvn/wrapper/maven-wrapper.jar

View File

@ -0,0 +1,29 @@
package com.imyeyu.api.bean;
import com.imyeyu.spring.bean.Page;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author 夜雨
* @since 2025-12-12 23:07
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class PreviewPage<T> extends Page<T> {
/**
*
*
* @author 夜雨
* @since 2025-12-12 23:09
*/
public enum Type {
NORMAL,
PREVIEW
}
protected Type type;
}

View File

@ -3,12 +3,15 @@ package com.imyeyu.api.modules.common.entity;
import com.google.gson.JsonObject;
import com.imyeyu.api.bean.MultilingualHandler;
import com.imyeyu.java.ref.Ref;
import com.imyeyu.spring.annotation.table.Transient;
import com.imyeyu.spring.entity.Entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import java.io.InputStream;
/**
* @author 夜雨
* @since 2023-08-15 10:17
@ -80,6 +83,9 @@ public class Attachment extends Entity implements MultilingualHandler {
protected Long destroyAt;
@Transient
protected InputStream inputStream;
public void setAttachTypeValue(Enum<?> attachType) {
this.attachType = attachType.toString();
}

View File

@ -4,11 +4,7 @@ import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.api.modules.common.vo.attachment.AttachmentRequest;
import com.imyeyu.api.modules.common.vo.attachment.AttachmentView;
import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.spring.service.DeletableService;
import com.imyeyu.spring.service.DestroyableService;
import com.imyeyu.spring.service.GettableService;
import com.imyeyu.spring.service.PageableService;
import com.imyeyu.spring.service.UpdatableService;
import com.imyeyu.spring.service.BaseService;
import com.mongodb.client.gridfs.model.GridFSFile;
import java.io.InputStream;
@ -22,14 +18,19 @@ import java.util.List;
* @author 夜雨
* @since 2023-08-15 10:21
*/
public interface AttachmentService extends GettableService<Attachment, Long>, PageableService<Attachment>, UpdatableService<Attachment>, DeletableService<Long>, DestroyableService<Long> {
public interface AttachmentService extends BaseService<Attachment, Long> {
Attachment createMedia(Attachment attachment);
/**
*
*
* @param request
*/
void create(AttachmentRequest request);
@Deprecated
default void create(AttachmentRequest request) {
create((Attachment) request);
}
/**
* 创建媒体附件,同步创建缩略图
@ -37,7 +38,10 @@ public interface AttachmentService extends GettableService<Attachment, Long>, Pa
* @param request 附件请求
* @return 缩略图附件
*/
Attachment createMedia(AttachmentRequest request) throws TimiException;
@Deprecated
default Attachment createMedia(AttachmentRequest request) throws TimiException {
return createMedia((Attachment) request);
}
void deleteMedia(Long thumbId) throws TimiException;

View File

@ -6,6 +6,7 @@ import com.imyeyu.java.bean.timi.TimiException;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.InputStream;
import java.util.List;
/**
@ -18,5 +19,7 @@ public interface TempFileService {
File get(String id) throws TimiException;
InputStream getInputStream(String id) throws TimiException;
TempFileMetaData metadata(String id) throws TimiException;
}

View File

@ -8,7 +8,6 @@ import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.api.modules.common.mapper.AttachmentMapper;
import com.imyeyu.api.modules.common.service.AttachmentService;
import com.imyeyu.api.modules.common.service.SettingService;
import com.imyeyu.api.modules.common.vo.attachment.AttachmentRequest;
import com.imyeyu.api.modules.common.vo.attachment.AttachmentView;
import com.imyeyu.api.util.JavaCV;
import com.imyeyu.io.IO;
@ -65,6 +64,47 @@ public class AttachmentServiceImplement extends AbstractEntityService<Attachment
return mapper;
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void create(Attachment attachment) {
TimiException.required(attachment.getBizType(), "not found attachment.bizType");
TimiException.required(attachment.getBizId(), "not found attachment.bizId");
TimiException.required(attachment.getName(), "not found attachment.name");
String mongoId = null;
try {
InputStream is = attachment.getInputStream();
TimiException.required(is, "not found attachment.inputStream");
TimiException.requiredTrue(is.available() != 0, "empty attachment.inputStream");
StringBuilder mongoName = new StringBuilder(attachment.getBizType().toString());
if (TimiJava.isNotEmpty(attachment.getAttachType())) {
mongoName.append("_").append(attachment.getAttachType().toUpperCase()).append("_");
}
mongoName.append(attachment.getName());
mongoId = gridFsTemplate.store(attachment.getInputStream(), mongoName.toString()).toString();
GridFSFile gridFSFile = gridFsTemplate.findOne(new Query(Criteria.where("_id").is(mongoId)));
attachment.setMongoId(mongoId);
attachment.setSize(gridFSFile.getLength());
attachment.setMd5(IO.md5(gridFSBucket.openDownloadStream(gridFSFile.getObjectId())));
attachment.setMimeType(new Tika().detect(gridFSBucket.openDownloadStream(gridFSFile.getObjectId())));
if (attachment.getMimeType().startsWith("image")) {
BufferedImage image = ImageIO.read(gridFSBucket.openDownloadStream(gridFSFile.getObjectId()));
Metadata.Image metadata = new Metadata.Image();
metadata.setWidth(image.getWidth());
metadata.setHeight(image.getHeight());
attachment.setMetadata(gson.toJsonTree(metadata).getAsJsonObject());
}
mapper.insert(attachment);
} catch (Exception e) {
if (mongoId != null) {
gridFsTemplate.delete(Query.query(Criteria.where("_id").is(mongoId)));
}
log.error("create attachment error", e);
throw new TimiException(TimiCode.ARG_BAD).msgKey("TODO read attachment input stream error");
}
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void destroy(Long id) {
@ -83,62 +123,18 @@ public class AttachmentServiceImplement extends AbstractEntityService<Attachment
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void create(AttachmentRequest request) {
TimiException.required(request.getBizType(), "not found request.bizType");
TimiException.required(request.getBizId(), "not found request.bizId");
TimiException.required(request.getName(), "not found request.name");
String mongoId = null;
try {
InputStream is = request.getInputStream();
TimiException.required(is, "not found request.inputStream");
TimiException.requiredTrue(is.available() != 0, "empty request.inputStream");
StringBuilder mongoName = new StringBuilder(request.getBizType().toString());
if (TimiJava.isNotEmpty(request.getAttachType())) {
mongoName.append("_").append(request.getAttachType().toUpperCase()).append("_");
}
mongoName.append(request.getName());
mongoId = gridFsTemplate.store(request.getInputStream(), mongoName.toString()).toString();
GridFSFile gridFSFile = gridFsTemplate.findOne(new Query(Criteria.where("_id").is(mongoId)));
request.setMongoId(mongoId);
request.setSize(gridFSFile.getLength());
request.setMd5(IO.md5(gridFSBucket.openDownloadStream(gridFSFile.getObjectId())));
request.setMimeType(new Tika().detect(gridFSBucket.openDownloadStream(gridFSFile.getObjectId())));
if (request.getMimeType().startsWith("image")) {
BufferedImage image = ImageIO.read(gridFSBucket.openDownloadStream(gridFSFile.getObjectId()));
Metadata.Image metadata = new Metadata.Image();
metadata.setWidth(image.getWidth());
metadata.setHeight(image.getHeight());
request.setMetadata(gson.toJsonTree(metadata).getAsJsonObject());
}
mapper.insert(request);
} catch (Exception e) {
if (mongoId != null) {
gridFsTemplate.delete(Query.query(Criteria.where("_id").is(mongoId)));
}
log.error("create attachment error", e);
throw new TimiException(TimiCode.ARG_BAD).msgKey("TODO read attachment input stream error");
}
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public Attachment createMedia(AttachmentRequest request) throws TimiException {
TimiException.required(request.getName(), "not found name");
TimiException.required(request.getBizType(), "not found bizType");
TimiException.required(request.getInputStream(), "not found inputStream");
public Attachment createMedia(Attachment attachment) throws TimiException {
try {
// 储存源文件
request.setAttachTypeValue(MediaAttach.Type.SOURCE);
create(request);
attachment.setAttachTypeValue(MediaAttach.Type.SOURCE);
create(attachment);
// 生成缩略图
InputStream mimeStream = getInputStreamByMongoId(request.getMongoId());
InputStream mimeStream = getInputStreamByMongoId(attachment.getMongoId());
String mimeType = new Tika().detect(mimeStream);
boolean isImage = false;
InputStream sourceStream = getInputStreamByMongoId(request.getMongoId());
InputStream sourceStream = getInputStreamByMongoId(attachment.getMongoId());
ByteArrayOutputStream thumbStream = new ByteArrayOutputStream();
switch (mimeType) {
case "image/png", "image/bmp", "image/jpeg" -> {
@ -146,14 +142,14 @@ public class AttachmentServiceImplement extends AbstractEntityService<Attachment
Thumbnails.of(sourceStream).width(256).keepAspectRatio(true).toOutputStream(thumbStream);
}
case "video/mp4", "video/quicktime" -> {
log.info("capturing thumbnail: {}", request.getName());
log.info("capturing thumbnail: {}", attachment.getName());
long start = Time.now();
File tempFile = IO.file("temp/%s_%s".formatted(UUID.randomUUID().toString(), request.getName()));
File tempFile = IO.file("temp/%s_%s".formatted(UUID.randomUUID().toString(), attachment.getName()));
try {
IO.toFile(tempFile, sourceStream);
ByteArrayOutputStream baos = JavaCV.captureThumbnail(IO.getInputStream(tempFile), 2);
Thumbnails.of(IO.toInputStream(baos)).width(256).keepAspectRatio(true).toOutputStream(thumbStream);
log.info("captured thumbnail: {} at {} ms", request.getName(), Time.now() - start);
log.info("captured thumbnail: {} at {} ms", attachment.getName(), Time.now() - start);
} finally {
IO.destroy(tempFile);
}
@ -162,13 +158,13 @@ public class AttachmentServiceImplement extends AbstractEntityService<Attachment
MediaAttach.ExtData extData = new MediaAttach.ExtData();
extData.setImage(isImage);
extData.setVideo(!isImage);
extData.setSourceId(request.getId());
extData.setSourceMongoId(request.getMongoId());
extData.setSourceId(attachment.getId());
extData.setSourceMongoId(attachment.getMongoId());
AttachmentRequest thumbAttach = new AttachmentRequest();
thumbAttach.setName(Network.simpleURIFileName(request.getName()) + ".png");
thumbAttach.setBizType(request.getBizType());
thumbAttach.setBizId(request.getBizId());
Attachment thumbAttach = new Attachment();
thumbAttach.setName(Network.simpleURIFileName(attachment.getName()) + ".png");
thumbAttach.setBizType(attachment.getBizType());
thumbAttach.setBizId(attachment.getBizId());
thumbAttach.setAttachTypeValue(MediaAttach.Type.THUMB);
thumbAttach.setExt(gson.toJson(extData));
thumbAttach.setInputStream(new ByteArrayInputStream(thumbStream.toByteArray()));

View File

@ -22,7 +22,9 @@ import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@ -108,6 +110,15 @@ public class TempFileServiceImplement implements TempFileService {
return metaData.getPath().toFile();
}
@Override
public InputStream getInputStream(String id) throws TimiException {
try {
return IO.getInputStream(get(id));
} catch (FileNotFoundException e) {
throw new TimiException(TimiCode.RESULT_NULL, "not found temp file");
}
}
@Override
public TempFileMetaData metadata(String id) throws TimiException {
return metadataMap.get(id);

View File

@ -5,8 +5,6 @@ import com.imyeyu.spring.annotation.table.Transient;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.InputStream;
/**
* @author 夜雨
@ -14,8 +12,7 @@ import java.io.InputStream;
*/
@Data
@Transient
@Deprecated
@EqualsAndHashCode(callSuper = true)
public class AttachmentRequest extends Attachment {
private InputStream inputStream;
}

View File

@ -1,5 +1,6 @@
package com.imyeyu.api.modules.journal.controller;
import com.imyeyu.api.bean.PreviewPage;
import com.imyeyu.api.bean.wechat.InitCodeResponse;
import com.imyeyu.api.modules.common.bean.MediaAttach;
import com.imyeyu.api.modules.common.bean.SettingKey;
@ -9,11 +10,10 @@ import com.imyeyu.api.modules.common.service.SettingService;
import com.imyeyu.api.modules.journal.bean.Travel;
import com.imyeyu.api.modules.journal.entity.Journal;
import com.imyeyu.api.modules.journal.service.JournalService;
import com.imyeyu.api.modules.journal.vo.ArchiveRequest;
import com.imyeyu.api.modules.journal.vo.JournalPage;
import com.imyeyu.api.modules.journal.vo.JournalRequest;
import com.imyeyu.api.modules.journal.vo.JournalResponse;
import com.imyeyu.api.modules.journal.vo.UpdateRequest;
import com.imyeyu.api.modules.journal.vo.journal.ArchiveRequest;
import com.imyeyu.api.modules.journal.vo.journal.JournalRequest;
import com.imyeyu.api.modules.journal.vo.journal.JournalResponse;
import com.imyeyu.api.modules.journal.vo.journal.UpdateRequest;
import com.imyeyu.java.bean.timi.TimiCode;
import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.network.ArgMap;
@ -141,7 +141,7 @@ public class JournalController {
@AOPLog
@RequestRateLimit
@RequestMapping("/list")
public PageResult<JournalResponse> list(@RequestBody JournalPage page) {
public PageResult<JournalResponse> list(@RequestBody PreviewPage<Journal> page) {
PageResult<Journal> pageResult = service.page(page);
PageResult<JournalResponse> result = new PageResult<>();

View File

@ -3,9 +3,9 @@ package com.imyeyu.api.modules.journal.service;
import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.api.modules.journal.bean.Travel;
import com.imyeyu.api.modules.journal.entity.Journal;
import com.imyeyu.api.modules.journal.vo.ArchiveRequest;
import com.imyeyu.api.modules.journal.vo.JournalRequest;
import com.imyeyu.api.modules.journal.vo.UpdateRequest;
import com.imyeyu.api.modules.journal.vo.journal.ArchiveRequest;
import com.imyeyu.api.modules.journal.vo.journal.JournalRequest;
import com.imyeyu.api.modules.journal.vo.journal.UpdateRequest;
import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.spring.service.DeletableService;
import com.imyeyu.spring.service.GettableService;

View File

@ -10,14 +10,13 @@ import com.imyeyu.api.modules.common.entity.Setting;
import com.imyeyu.api.modules.common.service.AttachmentService;
import com.imyeyu.api.modules.common.service.SettingService;
import com.imyeyu.api.modules.common.service.TempFileService;
import com.imyeyu.api.modules.common.vo.attachment.AttachmentRequest;
import com.imyeyu.api.modules.journal.bean.Travel;
import com.imyeyu.api.modules.journal.entity.Journal;
import com.imyeyu.api.modules.journal.mapper.JournalMapper;
import com.imyeyu.api.modules.journal.service.JournalService;
import com.imyeyu.api.modules.journal.vo.ArchiveRequest;
import com.imyeyu.api.modules.journal.vo.JournalRequest;
import com.imyeyu.api.modules.journal.vo.UpdateRequest;
import com.imyeyu.api.modules.journal.vo.journal.ArchiveRequest;
import com.imyeyu.api.modules.journal.vo.journal.JournalRequest;
import com.imyeyu.api.modules.journal.vo.journal.UpdateRequest;
import com.imyeyu.io.IO;
import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.timi.TimiCode;
@ -75,7 +74,7 @@ public class JournalServiceImplement extends AbstractEntityService<Journal, Long
TempFileMetaData metadata = tempFileService.metadata(tempFileIds[i]);
File file = tempFileService.get(tempFileIds[i]);
AttachmentRequest sourceAttach = new AttachmentRequest();
Attachment sourceAttach = new Attachment();
sourceAttach.setName(metadata.getOriginalName());
sourceAttach.setBizType(Attachment.BizType.JOURNAL);
sourceAttach.setBizId(journal.getId());
@ -92,7 +91,6 @@ public class JournalServiceImplement extends AbstractEntityService<Journal, Long
@Override
public void update(UpdateRequest request) throws TimiException {
TimiException.required(request.getId(), "not found request.id");
try {
Journal journal = get(request.getId());
BeanUtils.copyProperties(request, journal);
super.update(journal);
@ -108,25 +106,17 @@ public class JournalServiceImplement extends AbstractEntityService<Journal, Long
attachmentService.deleteMedia(removeId);
}
// 新增
String[] tempFileIds = request.getTempFileIds();
if (TimiJava.isNotEmpty(tempFileIds)) {
for (int i = 0; i < tempFileIds.length; i++) {
TempFileMetaData metadata = tempFileService.metadata(tempFileIds[i]);
File file = tempFileService.get(tempFileIds[i]);
for (String tempFileId : TimiJava.firstNotNull(request.getTempFileIds(), new String[0])) {
TempFileMetaData metadata = tempFileService.metadata(tempFileId);
AttachmentRequest sourceAttach = new AttachmentRequest();
Attachment sourceAttach = new Attachment();
sourceAttach.setName(metadata.getOriginalName());
sourceAttach.setBizType(Attachment.BizType.JOURNAL);
sourceAttach.setBizId(journal.getId());
sourceAttach.setInputStream(IO.getInputStream(file));
sourceAttach.setInputStream(tempFileService.getInputStream(tempFileId));
attachmentService.createMedia(sourceAttach);
}
}
} catch (Exception e) {
log.error("update journal error", e);
throw new TimiException(TimiCode.ERROR).msgKey("TODO update journal error");
}
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
@ -163,7 +153,7 @@ public class JournalServiceImplement extends AbstractEntityService<Journal, Long
TempFileMetaData metadata = tempFileService.metadata(tempFileIds[i]);
File file = tempFileService.get(tempFileIds[i]);
AttachmentRequest sourceAttach = new AttachmentRequest();
Attachment sourceAttach = new Attachment();
sourceAttach.setName(metadata.getOriginalName());
sourceAttach.setBizType(Attachment.BizType.JOURNAL_MOMENT);
sourceAttach.setBizId(0L);

View File

@ -1,34 +0,0 @@
package com.imyeyu.api.modules.journal.vo;
import com.imyeyu.api.modules.journal.entity.Journal;
import com.imyeyu.spring.bean.Page;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author 夜雨
* @since 2025-10-09 18:48
*/
@Data
@Valid
@EqualsAndHashCode(callSuper = true)
public class JournalPage extends Page<Journal> {
/**
*
*
* @author 夜雨
* @since 2025-12-08 16:01
*/
public enum Type {
NORMAL,
PREVIEW
}
@NotNull
private Type type;
}

View File

@ -1,4 +1,4 @@
package com.imyeyu.api.modules.journal.vo;
package com.imyeyu.api.modules.journal.vo.journal;
import com.imyeyu.api.modules.journal.entity.Journal;
import lombok.Data;

View File

@ -1,4 +1,4 @@
package com.imyeyu.api.modules.journal.vo;
package com.imyeyu.api.modules.journal.vo.journal;
import com.imyeyu.api.modules.journal.entity.Journal;
import com.imyeyu.spring.annotation.table.Transient;

View File

@ -1,4 +1,4 @@
package com.imyeyu.api.modules.journal.vo;
package com.imyeyu.api.modules.journal.vo.journal;
import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.api.modules.journal.entity.Journal;

View File

@ -1,4 +1,4 @@
package com.imyeyu.api.modules.journal.vo;
package com.imyeyu.api.modules.journal.vo.journal;
import com.imyeyu.api.modules.journal.entity.Journal;
import com.imyeyu.spring.annotation.table.Transient;

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.imyeyu.api.modules.journal.mapper.TravelLocationMapper">
<sql id="table">travel_location</sql>
<select id="listByIds" resultType="com.imyeyu.api.modules.journal.entity.TravelLocation">
SELECT
*
FROM
<include refid="table" />
WHERE
1 = 1
<if test="ids != null and 0 &lt; ids.length">
AND `id` IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</if>
AND deleted_at IS NULL
</select>
</mapper>