refactor travel

This commit is contained in:
Timi
2025-12-15 10:30:45 +08:00
parent 658765df6f
commit d82c5200e7
10 changed files with 642 additions and 0 deletions

View File

@ -0,0 +1,97 @@
package com.imyeyu.api.modules.journal.controller;
import com.imyeyu.api.modules.common.service.AttachmentService;
import com.imyeyu.api.modules.journal.entity.Travel;
import com.imyeyu.api.modules.journal.service.TravelService;
import com.imyeyu.spring.annotation.AOPLog;
import com.imyeyu.spring.annotation.RequestRateLimit;
import com.imyeyu.spring.annotation.RequestSingleParam;
import com.imyeyu.spring.bean.Page;
import com.imyeyu.spring.bean.PageResult;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 旅行计划接口
*
* @author 夜雨
* @since 2025-12-12 14:50
*/
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/journal/travel")
public class TravelController {
private final TravelService service;
private final AttachmentService attachmentService;
/**
* 创建旅行计划
*
* @param travel 旅行计划
*/
@AOPLog
@RequestRateLimit
@PostMapping("/create")
public void create(@RequestBody @Valid Travel travel) {
service.create(travel);
}
/**
* 更新旅行计划
*
* @param travel 旅行计划
*/
@AOPLog
@RequestRateLimit
@PostMapping("/update")
public void update(@RequestBody @Valid Travel travel) {
service.update(travel);
}
/**
* 删除旅行计划(级联删除关联地点和附件)
*
* @param id 旅行 ID
*/
@AOPLog
@RequestRateLimit
@PostMapping("/delete")
public void delete(@RequestSingleParam Long id) {
service.delete(id);
}
/**
* 查询旅行计划详情
*
* @param id 旅行 ID
* @return 旅行计划
*/
@AOPLog
@RequestRateLimit
@GetMapping("/{id}")
public Travel detail(@PathVariable Long id) {
return service.get(id);
}
/**
* 旅行计划列表
*
* @param page 分页参数
* @return 旅行列表
*/
@AOPLog
@RequestRateLimit
@PostMapping("/list")
public PageResult<Travel> list(@RequestBody Page<Travel> page) {
return service.page(page);
}
}

View File

@ -0,0 +1,136 @@
package com.imyeyu.api.modules.journal.controller;
import com.imyeyu.api.bean.PreviewPage;
import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.api.modules.common.service.AttachmentService;
import com.imyeyu.api.modules.journal.entity.TravelLocation;
import com.imyeyu.api.modules.journal.service.TravelLocationService;
import com.imyeyu.spring.annotation.AOPLog;
import com.imyeyu.spring.annotation.RequestRateLimit;
import com.imyeyu.spring.annotation.RequestSingleParam;
import com.imyeyu.spring.bean.Page;
import com.imyeyu.spring.bean.PageResult;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 旅行地点接口
*
* @author 夜雨
* @since 2025-12-12 14:50
*/
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/journal/travel/location")
public class TravelLocationController {
private final AttachmentService attachmentService;
private final TravelLocationService service;
/**
* 创建旅行地点
*
* @param location 旅行地点
*/
@AOPLog
@RequestRateLimit
@PostMapping("/create")
public void create(@RequestBody @Valid TravelLocation location) {
service.create(location);
}
/**
* 更新旅行地点
*
* @param location 旅行地点
*/
@AOPLog
@RequestRateLimit
@PostMapping("/update")
public void update(@RequestBody @Valid TravelLocation location) {
service.update(location);
}
/**
* 删除旅行地点(级联删除附件)
*
* @param id 地点 ID
*/
@AOPLog
@RequestRateLimit
@PostMapping("/delete")
public void delete(@RequestSingleParam Long id) {
service.delete(id);
}
/**
* 查询旅行地点详情
*
* @param id 地点 ID
* @return 旅行地点
*/
@AOPLog
@RequestRateLimit
@GetMapping("/{id}")
public TravelLocation detail(@PathVariable Long id) {
TravelLocation location = service.get(id);
location.setItems(attachmentService.listByBizId(Attachment.BizType.JOURNAL_TRAVEL, location.getId()));
return location;
}
/**
* 旅行地点列表
*
* @param page 分页参数
* @return 地点列表
*/
@AOPLog
@RequestRateLimit
@PostMapping("/list")
public PageResult<TravelLocation> list(@RequestBody PreviewPage<TravelLocation> page) {
PageResult<TravelLocation> result = service.page(page);
for (TravelLocation location : result.getList()) {
Page<Attachment> attachPage = new Page<>();
{
Attachment example = new Attachment();
example.setBizType(Attachment.BizType.JOURNAL_TRAVEL);
example.setBizId(location.getId());
attachPage.setEqualsExample(example);
}
attachPage.setIndex(0);
attachPage.setSize(1); // 列表接口只允许预览
location.setItems(attachmentService.page(attachPage).getList());
}
return result;
}
@AOPLog
@RequestRateLimit
@PostMapping("/list/ids")
public List<TravelLocation> listByIds(@RequestBody Long[] ids) {
List<TravelLocation> locationList = service.listByIds(ids);
for (TravelLocation location : locationList) {
Page<Attachment> attachPage = new Page<>();
{
Attachment example = new Attachment();
example.setBizType(Attachment.BizType.JOURNAL_TRAVEL);
example.setBizId(location.getId());
attachPage.setEqualsExample(example);
}
attachPage.setIndex(0);
attachPage.setSize(Long.MAX_VALUE);
location.setItems(attachmentService.page(attachPage).getList());
}
return locationList;
}
}

View File

@ -0,0 +1,83 @@
package com.imyeyu.api.modules.journal.entity;
import com.imyeyu.spring.annotation.table.PageIgnore;
import com.imyeyu.spring.entity.Entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 旅行计划
*
* @author 夜雨
* @since 2025-12-12 14:30
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class Travel extends Entity {
/**
* 交通类型
*
* @author 夜雨
* @since 2025-12-12 14:30
*/
public enum TransportationType {
/** 飞机 */
PLANE,
/** 火车 */
TRAIN,
/** 汽车 */
CAR,
/** 轮船 */
SHIP,
/** 自驾 */
SELF_DRIVING,
/** 其他 */
OTHER
}
/**
* 旅行状态
*
* @author 夜雨
* @since 2025-12-12 14:30
*/
public enum Status {
/** 计划中 */
PLANNING,
/** 进行中 */
ONGOING,
/** 已完成 */
COMPLETED,
}
/** 交通类型 */
@PageIgnore
private TransportationType transportationType;
/** 标题 */
private String title;
/** 内容 */
@PageIgnore
private String content;
/** 出行时间 */
private Long travelAt;
/** 天数 */
@PageIgnore
private Integer days;
/** 状态 */
private Status status;
}

View File

@ -0,0 +1,87 @@
package com.imyeyu.api.modules.journal.entity;
import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.spring.annotation.table.Transient;
import com.imyeyu.spring.entity.Entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.util.List;
/**
* 旅行地点
*
* @author 夜雨
* @since 2025-12-12 14:30
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class TravelLocation extends Entity {
/**
* 地点类型
*
* @author 夜雨
* @since 2025-12-12 14:30
*/
public enum Type {
/** 景点 */
ATTRACTION,
/** 酒店 */
HOTEL,
/** 餐厅 */
RESTAURANT,
/** 交通站点 */
TRANSPORT,
/** 购物 */
SHOPPING,
/** 其他 */
OTHER
}
/** 旅行计划 ID */
private Long travelId;
/** 类型 */
private Type type;
/** 标题 */
private String title;
/** 说明 */
private String description;
/** 纬度 */
private Double lat;
/** 经度 */
private Double lng;
/** 位置 */
private String location;
/** 费用 */
private BigDecimal amount;
/** true 为需要身份证 */
private Boolean requireIdCard;
/** 必要评分 */
private Integer score;
@Transient
private Long[] attachmentIds;
@Transient
private String[] tempFileIds;
@Transient
private List<Attachment> items;
}

View File

@ -0,0 +1,17 @@
package com.imyeyu.api.modules.journal.mapper;
import com.imyeyu.api.modules.journal.entity.TravelLocation;
import com.imyeyu.spring.mapper.BaseMapper;
import java.util.List;
/**
* 旅行地点 Mapper
*
* @author 夜雨
* @since 2025-12-12 14:35
*/
public interface TravelLocationMapper extends BaseMapper<TravelLocation, Long> {
List<TravelLocation> listByIds(Long[] ids);
}

View File

@ -0,0 +1,32 @@
package com.imyeyu.api.modules.journal.mapper;
import com.imyeyu.api.modules.journal.entity.Travel;
import com.imyeyu.spring.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 旅行计划 Mapper
*
* @author 夜雨
* @since 2025-12-12 14:35
*/
public interface TravelMapper extends BaseMapper<Travel, Long> {
/**
* 根据 ID 列表查询
*
* @param ids ID 列表
* @return 旅行列表
*/
List<Travel> listByIds(@Param("ids") Long[] ids);
/**
* 根据状态查询
*
* @param status 状态
* @return 旅行列表
*/
List<Travel> listByStatus(@Param("status") Travel.Status status);
}

View File

@ -0,0 +1,26 @@
package com.imyeyu.api.modules.journal.service;
import com.imyeyu.api.modules.journal.entity.TravelLocation;
import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.spring.service.BaseService;
import java.util.List;
/**
* 旅行地点服务
*
* @author 夜雨
* @since 2025-12-12 14:40
*/
public interface TravelLocationService extends BaseService<TravelLocation, Long> {
List<TravelLocation> listByIds(Long... ids);
/**
* 根据旅行 ID 删除所有地点
*
* @param travelId 旅行 ID
* @throws TimiException 服务异常
*/
void deleteByTravelId(Long travelId) throws TimiException;
}

View File

@ -0,0 +1,13 @@
package com.imyeyu.api.modules.journal.service;
import com.imyeyu.api.modules.journal.entity.Travel;
import com.imyeyu.spring.service.BaseService;
/**
* 旅行计划服务
*
* @author 夜雨
* @since 2025-12-12 14:40
*/
public interface TravelService extends BaseService<Travel, Long> {
}

View File

@ -0,0 +1,111 @@
package com.imyeyu.api.modules.journal.service.implement;
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
import com.imyeyu.api.modules.common.bean.MediaAttach;
import com.imyeyu.api.modules.common.bean.TempFileMetaData;
import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.api.modules.common.service.AttachmentService;
import com.imyeyu.api.modules.common.service.TempFileService;
import com.imyeyu.api.modules.journal.entity.TravelLocation;
import com.imyeyu.api.modules.journal.mapper.TravelLocationMapper;
import com.imyeyu.api.modules.journal.service.TravelLocationService;
import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.spring.mapper.BaseMapper;
import com.imyeyu.spring.service.AbstractEntityService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 旅行地点服务实现
*
* @author 夜雨
* @since 2025-12-12 14:45
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class TravelLocationServiceImplement extends AbstractEntityService<TravelLocation, Long> implements TravelLocationService {
private final TempFileService tempFileService;
private final AttachmentService attachmentService;
private final TravelLocationMapper mapper;
@Override
protected BaseMapper<TravelLocation, Long> mapper() {
return mapper;
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void create(TravelLocation travelLocation) {
super.create(travelLocation);
for (String tempFileId : travelLocation.getTempFileIds()) {
TempFileMetaData metadata = tempFileService.metadata(tempFileId);
Attachment attach = new Attachment();
attach.setName(metadata.getOriginalName());
attach.setBizType(Attachment.BizType.JOURNAL_TRAVEL);
attach.setBizId(travelLocation.getId());
attach.setInputStream(tempFileService.getInputStream(tempFileId));
attachmentService.createMedia(attach);
}
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void update(TravelLocation travelLocation) {
super.update(travelLocation);
// 删除
Set<Long> dbAttachSet = attachmentService.listByBizId(Attachment.BizType.JOURNAL_TRAVEL, travelLocation.getId(), MediaAttach.Type.THUMB)
.stream()
.map(Attachment::getId)
.collect(Collectors.toSet());
Set<Long> retainIds = Set.of(TimiJava.firstNotEmpty(travelLocation.getAttachmentIds(), new Long[0]));
dbAttachSet.removeAll(retainIds);
for (Long removeId : dbAttachSet) {
attachmentService.deleteMedia(removeId);
}
// 新增
for (String tempFileId : TimiJava.firstNotNull(travelLocation.getTempFileIds(), new String[0])) {
TempFileMetaData metadata = tempFileService.metadata(tempFileId);
Attachment attach = new Attachment();
attach.setName(metadata.getOriginalName());
attach.setBizType(Attachment.BizType.JOURNAL_TRAVEL);
attach.setBizId(travelLocation.getId());
attach.setInputStream(tempFileService.getInputStream(tempFileId));
attachmentService.createMedia(attach);
}
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void delete(Long id) {
super.delete(id);
attachmentService.deleteByBizId(Attachment.BizType.JOURNAL_TRAVEL, id);
}
@Override
public List<TravelLocation> listByIds(Long... ids) {
return mapper.listByIds(ids);
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void deleteByTravelId(Long travelId) throws TimiException {
TravelLocation example = new TravelLocation();
example.setTravelId(travelId);
List<TravelLocation> locationList = mapper.selectAllByExample(example);
for (TravelLocation location : locationList) {
delete(location.getId());
}
}
}

View File

@ -0,0 +1,40 @@
package com.imyeyu.api.modules.journal.service.implement;
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
import com.imyeyu.api.modules.journal.entity.Travel;
import com.imyeyu.api.modules.journal.mapper.TravelMapper;
import com.imyeyu.api.modules.journal.service.TravelLocationService;
import com.imyeyu.api.modules.journal.service.TravelService;
import com.imyeyu.spring.mapper.BaseMapper;
import com.imyeyu.spring.service.AbstractEntityService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 旅行计划服务实现
*
* @author 夜雨
* @since 2025-12-12 14:45
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class TravelServiceImplement extends AbstractEntityService<Travel, Long> implements TravelService {
private final TravelMapper mapper;
private final TravelLocationService travelLocationService;
@Override
protected BaseMapper<Travel, Long> mapper() {
return mapper;
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void delete(Long id) {
super.delete(id);
travelLocationService.deleteByTravelId(id);
}
}