Bläddra i källkod

完成动态浏览历史记录功能

lutong 3 månader sedan
förälder
incheckning
af2e0475a3

+ 81 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreRenovationBrowseRecord.java

@@ -0,0 +1,81 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.util.Date;
+
+/**
+ * 装修商铺访问装修需求浏览记录表
+ * <p>
+ * 记录装修商铺(装修公司)查看/访问装修需求动态的历史记录
+ * </p>
+ *
+ * @author auto-generated
+ * @since 2025-01-15
+ */
+@Data
+@JsonInclude
+@TableName("store_renovation_browse_record")
+@ApiModel(value = "StoreRenovationBrowseRecord对象", description = "装修商铺访问装修需求浏览记录表")
+public class StoreRenovationBrowseRecord {
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "装修商铺ID(访问者,装修公司的门店ID)")
+    @TableField("renovation_store_id")
+    private Integer renovationStoreId;
+
+    @ApiModelProperty(value = "装修需求ID(被访问的装修需求)")
+    @TableField("requirement_id")
+    private Integer requirementId;
+
+    @ApiModelProperty(value = "访问时间")
+    @TableField("browse_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date browseTime;
+
+    @ApiModelProperty(value = "是否已联系(0:未联系, 1:已联系)")
+    @TableField("is_contacted")
+    private Integer isContacted;
+
+    @ApiModelProperty(value = "联系时间(首次联系时间)")
+    @TableField("contact_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date contactTime;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+}
+

+ 84 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/StoreRenovationBrowseRequirementDto.java

@@ -0,0 +1,84 @@
+package shop.alien.entity.store.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+import shop.alien.entity.store.dto.deserializer.StringToListDeserializer;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 装修商铺浏览过的装修需求DTO(包含浏览信息)
+ *
+ * @author auto-generated
+ * @since 2025-01-15
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "StoreRenovationBrowseRequirementDto对象", description = "装修商铺浏览过的装修需求DTO")
+public class StoreRenovationBrowseRequirementDto {
+
+    @ApiModelProperty(value = "装修需求ID")
+    private Integer requirementId;
+
+    @ApiModelProperty(value = "需求标题")
+    private String requirementTitle;
+
+    @ApiModelProperty(value = "装修类型(1:新房装修, 2:旧房改造, 3:局部装修)")
+    private Integer renovationType;
+
+    @ApiModelProperty(value = "房屋面积(平方米)")
+    private BigDecimal houseArea;
+
+    @ApiModelProperty(value = "装修预算(万元)")
+    private BigDecimal renovationBudget;
+
+    @ApiModelProperty(value = "详细需求描述")
+    private String detailedRequirement;
+
+    @ApiModelProperty(value = "期望装修时间")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private Date expectedRenovationTime;
+
+    @ApiModelProperty(value = "联系人姓名")
+    private String contactName;
+
+    @ApiModelProperty(value = "联系电话")
+    private String contactPhone;
+
+    @ApiModelProperty(value = "所在城市")
+    private String city;
+
+    @ApiModelProperty(value = "详细地址")
+    private String detailedAddress;
+
+    @ApiModelProperty(value = "附件列表(图片/视频URL列表)")
+    @JsonDeserialize(using = StringToListDeserializer.class)
+    private List<String> attachmentUrls;
+
+    @ApiModelProperty(value = "浏览时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date browseTime;
+
+    @ApiModelProperty(value = "是否已联系(0:未联系, 1:已联系)")
+    private Integer isContacted;
+
+    @ApiModelProperty(value = "联系时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date contactTime;
+
+    @ApiModelProperty(value = "创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createdTime;
+}
+

+ 13 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/StoreRenovationRequirementDto.java

@@ -90,5 +90,18 @@ public class StoreRenovationRequirementDto {
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date updatedTime;
     private Date updatedTime;
+
+    // --- 商铺信息字段 ---
+    @ApiModelProperty(value = "商铺名称")
+    private String storeName;
+
+    @ApiModelProperty(value = "商铺电话")
+    private String storeTel;
+
+    @ApiModelProperty(value = "商铺地址")
+    private String storeAddress;
+
+    @ApiModelProperty(value = "商铺简介")
+    private String storeBlurb;
 }
 }
 
 

+ 17 - 0
alien-entity/src/main/java/shop/alien/mapper/StoreRenovationBrowseRecordMapper.java

@@ -0,0 +1,17 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.StoreRenovationBrowseRecord;
+
+/**
+ * 装修商铺访问装修需求浏览记录表 Mapper 接口
+ *
+ * @author auto-generated
+ * @since 2025-01-15
+ */
+@Mapper
+public interface StoreRenovationBrowseRecordMapper extends BaseMapper<StoreRenovationBrowseRecord> {
+
+}
+

+ 2 - 0
alien-store/src/main/java/shop/alien/store/config/WebSocketProcess.java

@@ -245,4 +245,6 @@ public class WebSocketProcess implements ApplicationContextAware {
         }
         }
     }
     }
 
 
+
+
 }
 }

+ 43 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreRenovationRequirementController.java

@@ -7,7 +7,9 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 import shop.alien.entity.result.R;
 import shop.alien.entity.result.R;
+import shop.alien.entity.store.dto.StoreRenovationBrowseRequirementDto;
 import shop.alien.entity.store.dto.StoreRenovationRequirementDto;
 import shop.alien.entity.store.dto.StoreRenovationRequirementDto;
+import shop.alien.store.service.StoreRenovationBrowseRecordService;
 import shop.alien.store.service.StoreRenovationRequirementService;
 import shop.alien.store.service.StoreRenovationRequirementService;
 
 
 import java.util.List;
 import java.util.List;
@@ -28,6 +30,7 @@ import java.util.List;
 public class StoreRenovationRequirementController {
 public class StoreRenovationRequirementController {
 
 
     private final StoreRenovationRequirementService requirementService;
     private final StoreRenovationRequirementService requirementService;
+    private final StoreRenovationBrowseRecordService browseRecordService;
 
 
     @ApiOperation("发布或更新装修需求")
     @ApiOperation("发布或更新装修需求")
     @ApiOperationSupport(order = 1)
     @ApiOperationSupport(order = 1)
@@ -205,5 +208,45 @@ public class StoreRenovationRequirementController {
             return R.fail(e.getMessage());
             return R.fail(e.getMessage());
         }
         }
     }
     }
+
+    @ApiOperation("装修商铺咨询装修需求(记录浏览和咨询历史)")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "requirementId", value = "装修需求ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PostMapping("/consultRequirement")
+    public R<Boolean> consultRequirement(@RequestParam Integer requirementId) {
+        log.info("StoreRenovationRequirementController.consultRequirement?requirementId={}", requirementId);
+        try {
+            boolean result = browseRecordService.recordInquiryByCurrentUser(requirementId);
+            if (result) {
+                return R.success("咨询记录成功");
+            }
+            return R.fail("咨询记录失败");
+        } catch (Exception e) {
+            log.error("记录咨询失败: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("查询装修商铺浏览过的普通商铺发布的装修需求列表")
+    @ApiOperationSupport(order = 10)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "renovationStoreTel", value = "装修商铺电话(store_info.store_tel)", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "normalStoreTel", value = "普通商铺电话(store_info.store_tel,发布装修需求的店铺)", dataType = "String", paramType = "query", required = true)
+    })
+    @GetMapping("/getBrowsedRequirements")
+    public R<List<StoreRenovationBrowseRequirementDto>> getBrowsedRequirements(
+            @RequestParam String renovationStoreTel,
+            @RequestParam String normalStoreTel) {
+        log.info("StoreRenovationRequirementController.getBrowsedRequirements?renovationStoreTel={}, normalStoreTel={}", renovationStoreTel, normalStoreTel);
+        try {
+            List<StoreRenovationBrowseRequirementDto> list = browseRecordService.getBrowsedRequirementsByStoreTel(renovationStoreTel, normalStoreTel);
+            return R.data(list);
+        } catch (Exception e) {
+            log.error("查询浏览过的装修需求失败: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
 }
 }
 
 

+ 52 - 0
alien-store/src/main/java/shop/alien/store/service/StoreRenovationBrowseRecordService.java

@@ -0,0 +1,52 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.StoreRenovationBrowseRecord;
+import shop.alien.entity.store.dto.StoreRenovationBrowseRequirementDto;
+
+import java.util.List;
+
+/**
+ * 装修商铺访问装修需求浏览记录表 服务类
+ *
+ * @author auto-generated
+ * @since 2025-01-15
+ */
+public interface StoreRenovationBrowseRecordService extends IService<StoreRenovationBrowseRecord> {
+
+    /**
+     * 记录装修商铺浏览装修需求(仅浏览)
+     *
+     * @param renovationStoreId 装修商铺ID
+     * @param requirementId     装修需求ID
+     * @return 是否成功
+     */
+    boolean recordBrowse(Integer renovationStoreId, Integer requirementId);
+
+    /**
+     * 记录装修商铺咨询装修需求(浏览并咨询)
+     *
+     * @param renovationStoreId 装修商铺ID
+     * @param requirementId     装修需求ID
+     * @return 是否成功
+     */
+    boolean recordInquiry(Integer renovationStoreId, Integer requirementId);
+
+    /**
+     * 记录当前登录装修商铺咨询装修需求(自动获取当前用户的门店ID)
+     *
+     * @param requirementId 装修需求ID
+     * @return 是否成功
+     */
+    boolean recordInquiryByCurrentUser(Integer requirementId);
+
+    /**
+     * 查询装修商铺浏览过的普通商铺发布的装修需求列表
+     *
+     * @param renovationStoreTel 装修商铺电话(store_info.store_tel)
+     * @param normalStoreTel     普通商铺电话(store_info.store_tel,发布装修需求的店铺)
+     * @return 浏览过的装修需求列表(包含浏览信息)
+     */
+    List<StoreRenovationBrowseRequirementDto> getBrowsedRequirementsByStoreTel(String renovationStoreTel, String normalStoreTel);
+}
+

+ 313 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreRenovationBrowseRecordServiceImpl.java

@@ -0,0 +1,313 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.store.StoreInfo;
+import shop.alien.entity.store.StoreRenovationBrowseRecord;
+import shop.alien.entity.store.StoreRenovationRequirement;
+import shop.alien.entity.store.StoreUser;
+import shop.alien.entity.store.dto.StoreRenovationBrowseRequirementDto;
+import shop.alien.mapper.StoreInfoMapper;
+import shop.alien.mapper.StoreRenovationBrowseRecordMapper;
+import shop.alien.mapper.StoreUserMapper;
+import shop.alien.store.service.StoreRenovationBrowseRecordService;
+import shop.alien.store.service.StoreRenovationRequirementService;
+import shop.alien.util.common.JwtUtil;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 装修商铺访问装修需求浏览记录表 服务实现类
+ *
+ * @author auto-generated
+ * @since 2025-01-15
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@Transactional(rollbackFor = Exception.class)
+public class StoreRenovationBrowseRecordServiceImpl extends ServiceImpl<StoreRenovationBrowseRecordMapper, StoreRenovationBrowseRecord> implements StoreRenovationBrowseRecordService {
+
+    private final StoreRenovationRequirementService requirementService;
+    private final StoreUserMapper storeUserMapper;
+    private final StoreInfoMapper storeInfoMapper;
+
+    @Override
+    public boolean recordBrowse(Integer renovationStoreId, Integer requirementId) {
+        log.info("StoreRenovationBrowseRecordServiceImpl.recordBrowse?renovationStoreId={}, requirementId={}", renovationStoreId, requirementId);
+        try {
+            StoreRenovationBrowseRecord record = new StoreRenovationBrowseRecord();
+            record.setRenovationStoreId(renovationStoreId);
+            record.setRequirementId(requirementId);
+            record.setBrowseTime(new Date());
+            record.setIsContacted(0); // 仅浏览,未咨询
+
+            // 获取当前登录用户ID
+            Integer currentUserId = null;
+            try {
+                com.alibaba.fastjson.JSONObject userInfo = JwtUtil.getCurrentUserInfo();
+                if (userInfo != null) {
+                    currentUserId = userInfo.getInteger("userId");
+                }
+            } catch (Exception e) {
+                log.warn("获取当前用户ID失败: {}", e.getMessage());
+            }
+            record.setCreatedUserId(currentUserId);
+
+            return this.save(record);
+        } catch (Exception e) {
+            log.error("记录浏览失败: {}", e.getMessage(), e);
+            throw new RuntimeException("记录浏览失败: " + e.getMessage());
+        }
+    }
+
+    @Override
+    public boolean recordInquiry(Integer renovationStoreId, Integer requirementId) {
+        log.info("StoreRenovationBrowseRecordServiceImpl.recordInquiry?renovationStoreId={}, requirementId={}", renovationStoreId, requirementId);
+        try {
+            Date now = new Date();
+
+            // 检查是否已有浏览记录
+            LambdaQueryWrapper<StoreRenovationBrowseRecord> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(StoreRenovationBrowseRecord::getRenovationStoreId, renovationStoreId);
+            queryWrapper.eq(StoreRenovationBrowseRecord::getRequirementId, requirementId);
+            queryWrapper.eq(StoreRenovationBrowseRecord::getDeleteFlag, 0);
+            StoreRenovationBrowseRecord existingRecord = this.getOne(queryWrapper, false);
+
+            boolean isFirstContact = false;
+
+            if (existingRecord != null) {
+                // 如果已有记录但未咨询过,更新为已咨询
+                if (existingRecord.getIsContacted() == null || existingRecord.getIsContacted() == 0) {
+                    LambdaUpdateWrapper<StoreRenovationBrowseRecord> updateWrapper = new LambdaUpdateWrapper<>();
+                    updateWrapper.eq(StoreRenovationBrowseRecord::getId, existingRecord.getId());
+                    updateWrapper.set(StoreRenovationBrowseRecord::getIsContacted, 1);
+                    updateWrapper.set(StoreRenovationBrowseRecord::getContactTime, now);
+
+                    // 获取当前登录用户ID
+                    Integer currentUserId = null;
+                    try {
+                        com.alibaba.fastjson.JSONObject userInfo = JwtUtil.getCurrentUserInfo();
+                        if (userInfo != null) {
+                            currentUserId = userInfo.getInteger("userId");
+                        }
+                    } catch (Exception e) {
+                        log.warn("获取当前用户ID失败: {}", e.getMessage());
+                    }
+                    updateWrapper.set(StoreRenovationBrowseRecord::getUpdatedUserId, currentUserId);
+
+                    this.update(updateWrapper);
+                    isFirstContact = true;
+                }
+                // 如果已咨询过,不再重复记录,但可以记录新的浏览记录(可选)
+                // 这里选择不重复记录咨询,只更新浏览时间
+                existingRecord.setBrowseTime(now);
+                this.updateById(existingRecord);
+            } else {
+                // 如果没有记录,创建新记录(浏览+咨询)
+                StoreRenovationBrowseRecord record = new StoreRenovationBrowseRecord();
+                record.setRenovationStoreId(renovationStoreId);
+                record.setRequirementId(requirementId);
+                record.setBrowseTime(now);
+                record.setIsContacted(1); // 已咨询
+                record.setContactTime(now);
+
+                // 获取当前登录用户ID
+                Integer currentUserId = null;
+                try {
+                    com.alibaba.fastjson.JSONObject userInfo = JwtUtil.getCurrentUserInfo();
+                    if (userInfo != null) {
+                        currentUserId = userInfo.getInteger("userId");
+                    }
+                } catch (Exception e) {
+                    log.warn("获取当前用户ID失败: {}", e.getMessage());
+                }
+                record.setCreatedUserId(currentUserId);
+
+                this.save(record);
+                isFirstContact = true;
+            }
+
+            // 如果是首次咨询,增加需求的咨询数
+            if (isFirstContact) {
+                requirementService.incrementInquiryCount(requirementId);
+            }
+
+            return true;
+        } catch (Exception e) {
+            log.error("记录咨询失败: {}", e.getMessage(), e);
+            throw new RuntimeException("记录咨询失败: " + e.getMessage());
+        }
+    }
+
+    @Override
+    public boolean recordInquiryByCurrentUser(Integer requirementId) {
+        log.info("StoreRenovationBrowseRecordServiceImpl.recordInquiryByCurrentUser?requirementId={}", requirementId);
+        try {
+            // 获取当前登录用户信息
+            Integer currentUserId = null;
+            String phone = null;
+            try {
+                com.alibaba.fastjson.JSONObject userInfo = JwtUtil.getCurrentUserInfo();
+                if (userInfo != null) {
+                    currentUserId = userInfo.getInteger("userId");
+                    phone = userInfo.getString("phone");
+                }
+            } catch (Exception e) {
+                log.warn("获取当前用户信息失败: {}", e.getMessage());
+                throw new RuntimeException("获取当前用户信息失败,请先登录");
+            }
+
+            if (currentUserId == null) {
+                throw new RuntimeException("用户未登录");
+            }
+
+            // 根据用户ID或手机号查询 StoreUser,获取门店ID
+            StoreUser storeUser = null;
+            if (currentUserId != null) {
+                storeUser = storeUserMapper.selectById(currentUserId);
+            }
+            if (storeUser == null && phone != null) {
+                LambdaQueryWrapper<StoreUser> queryWrapper = new LambdaQueryWrapper<>();
+                queryWrapper.eq(StoreUser::getPhone, phone);
+                storeUser = storeUserMapper.selectOne(queryWrapper);
+            }
+
+            if (storeUser == null || storeUser.getStoreId() == null) {
+                throw new RuntimeException("未找到用户门店信息");
+            }
+
+            Integer renovationStoreId = storeUser.getStoreId();
+            return recordInquiry(renovationStoreId, requirementId);
+        } catch (Exception e) {
+            log.error("记录咨询失败: {}", e.getMessage(), e);
+            throw new RuntimeException(e.getMessage());
+        }
+    }
+
+    @Override
+    public List<StoreRenovationBrowseRequirementDto> getBrowsedRequirementsByStoreTel(String renovationStoreTel, String normalStoreTel) {
+        log.info("StoreRenovationBrowseRecordServiceImpl.getBrowsedRequirementsByStoreTel?renovationStoreTel={}, normalStoreTel={}", renovationStoreTel, normalStoreTel);
+        try {
+            // 0. 通过门店电话查询门店ID
+            LambdaQueryWrapper<StoreInfo> renovationStoreWrapper = new LambdaQueryWrapper<>();
+            renovationStoreWrapper.eq(StoreInfo::getStoreTel, renovationStoreTel);
+            renovationStoreWrapper.eq(StoreInfo::getDeleteFlag, 0);
+            StoreInfo renovationStoreInfo = storeInfoMapper.selectOne(renovationStoreWrapper);
+            if (renovationStoreInfo == null) {
+                throw new RuntimeException("装修商铺不存在,门店电话: " + renovationStoreTel);
+            }
+            Integer renovationStoreId = renovationStoreInfo.getId();
+
+            LambdaQueryWrapper<StoreInfo> normalStoreWrapper = new LambdaQueryWrapper<>();
+            normalStoreWrapper.eq(StoreInfo::getStoreTel, normalStoreTel);
+            normalStoreWrapper.eq(StoreInfo::getDeleteFlag, 0);
+            StoreInfo normalStoreInfo = storeInfoMapper.selectOne(normalStoreWrapper);
+            if (normalStoreInfo == null) {
+                throw new RuntimeException("普通商铺不存在,门店电话: " + normalStoreTel);
+            }
+            Integer normalStoreId = normalStoreInfo.getId();
+
+            // 1. 查询普通商铺发布的所有装修需求ID
+            LambdaQueryWrapper<StoreRenovationRequirement> requirementWrapper = new LambdaQueryWrapper<>();
+            requirementWrapper.eq(StoreRenovationRequirement::getStoreId, normalStoreId);
+            requirementWrapper.eq(StoreRenovationRequirement::getDeleteFlag, 0);
+            List<StoreRenovationRequirement> requirements = requirementService.list(requirementWrapper);
+            
+            if (requirements.isEmpty()) {
+                return new ArrayList<>();
+            }
+            
+            List<Integer> requirementIds = requirements.stream()
+                    .map(StoreRenovationRequirement::getId)
+                    .collect(Collectors.toList());
+
+            // 2. 查询装修商铺浏览过哪些需求
+            LambdaQueryWrapper<StoreRenovationBrowseRecord> browseWrapper = new LambdaQueryWrapper<>();
+            browseWrapper.eq(StoreRenovationBrowseRecord::getRenovationStoreId, renovationStoreId);
+            browseWrapper.in(StoreRenovationBrowseRecord::getRequirementId, requirementIds);
+            browseWrapper.eq(StoreRenovationBrowseRecord::getDeleteFlag, 0);
+            browseWrapper.orderByDesc(StoreRenovationBrowseRecord::getBrowseTime);
+            List<StoreRenovationBrowseRecord> browseRecords = this.list(browseWrapper);
+
+            if (browseRecords.isEmpty()) {
+                return new ArrayList<>();
+            }
+
+            // 3. 创建需求ID到浏览记录的映射(取最新的浏览记录)
+            Map<Integer, StoreRenovationBrowseRecord> browseRecordMap = browseRecords.stream()
+                    .collect(Collectors.toMap(
+                            StoreRenovationBrowseRecord::getRequirementId,
+                            record -> record,
+                            (existing, replacement) -> existing.getBrowseTime().after(replacement.getBrowseTime()) ? existing : replacement
+                    ));
+
+            // 4. 创建需求ID到需求详情的映射
+            Map<Integer, StoreRenovationRequirement> requirementMap = requirements.stream()
+                    .collect(Collectors.toMap(StoreRenovationRequirement::getId, req -> req));
+
+            // 5. 组装返回数据(只返回浏览过的需求)
+            List<StoreRenovationBrowseRequirementDto> result = new ArrayList<>();
+            for (StoreRenovationBrowseRecord browseRecord : browseRecordMap.values()) {
+                StoreRenovationRequirement requirement = requirementMap.get(browseRecord.getRequirementId());
+                if (requirement != null) {
+                    StoreRenovationBrowseRequirementDto dto = new StoreRenovationBrowseRequirementDto();
+                    
+                    // 复制需求信息
+                    dto.setRequirementId(requirement.getId());
+                    dto.setRequirementTitle(requirement.getRequirementTitle());
+                    dto.setRenovationType(requirement.getRenovationType());
+                    dto.setHouseArea(requirement.getHouseArea());
+                    dto.setRenovationBudget(requirement.getRenovationBudget());
+                    dto.setDetailedRequirement(requirement.getDetailedRequirement());
+                    dto.setExpectedRenovationTime(requirement.getExpectedRenovationTime());
+                    dto.setContactName(requirement.getContactName());
+                    dto.setContactPhone(requirement.getContactPhone());
+                    dto.setCity(requirement.getCity());
+                    dto.setDetailedAddress(requirement.getDetailedAddress());
+                    dto.setCreatedTime(requirement.getCreatedTime());
+
+                    // 处理附件URL字符串:逗号拼接转List
+                    if (StringUtils.hasText(requirement.getAttachmentUrls())) {
+                        List<String> attachmentUrls = Arrays.asList(requirement.getAttachmentUrls().split(","));
+                        dto.setAttachmentUrls(attachmentUrls);
+                    } else {
+                        dto.setAttachmentUrls(new ArrayList<>());
+                    }
+
+                    // 复制浏览信息
+                    dto.setBrowseTime(browseRecord.getBrowseTime());
+                    dto.setIsContacted(browseRecord.getIsContacted());
+                    dto.setContactTime(browseRecord.getContactTime());
+
+                    result.add(dto);
+                }
+            }
+
+            // 按浏览时间倒序排序
+            result.sort((a, b) -> {
+                if (a.getBrowseTime() == null && b.getBrowseTime() == null) return 0;
+                if (a.getBrowseTime() == null) return 1;
+                if (b.getBrowseTime() == null) return -1;
+                return b.getBrowseTime().compareTo(a.getBrowseTime());
+            });
+
+            return result;
+        } catch (Exception e) {
+            log.error("查询浏览过的装修需求失败: {}", e.getMessage(), e);
+            throw new RuntimeException("查询浏览过的装修需求失败: " + e.getMessage());
+        }
+    }
+}
+

+ 34 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreRenovationRequirementServiceImpl.java

@@ -11,8 +11,10 @@ import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.StringUtils;
 import org.springframework.util.StringUtils;
+import shop.alien.entity.store.StoreInfo;
 import shop.alien.entity.store.StoreRenovationRequirement;
 import shop.alien.entity.store.StoreRenovationRequirement;
 import shop.alien.entity.store.dto.StoreRenovationRequirementDto;
 import shop.alien.entity.store.dto.StoreRenovationRequirementDto;
+import shop.alien.mapper.StoreInfoMapper;
 import shop.alien.mapper.StoreRenovationRequirementMapper;
 import shop.alien.mapper.StoreRenovationRequirementMapper;
 import shop.alien.store.service.StoreRenovationRequirementService;
 import shop.alien.store.service.StoreRenovationRequirementService;
 import shop.alien.util.common.JwtUtil;
 import shop.alien.util.common.JwtUtil;
@@ -21,6 +23,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.Date;
 import java.util.List;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
 /**
 /**
@@ -35,6 +38,8 @@ import java.util.stream.Collectors;
 @Transactional(rollbackFor = Exception.class)
 @Transactional(rollbackFor = Exception.class)
 public class StoreRenovationRequirementServiceImpl extends ServiceImpl<StoreRenovationRequirementMapper, StoreRenovationRequirement> implements StoreRenovationRequirementService {
 public class StoreRenovationRequirementServiceImpl extends ServiceImpl<StoreRenovationRequirementMapper, StoreRenovationRequirement> implements StoreRenovationRequirementService {
 
 
+    private final StoreInfoMapper storeInfoMapper;
+
     @Override
     @Override
     public boolean saveOrUpdateRequirement(StoreRenovationRequirementDto dto) {
     public boolean saveOrUpdateRequirement(StoreRenovationRequirementDto dto) {
         log.info("StoreRenovationRequirementServiceImpl.saveOrUpdateRequirement?dto={}", dto);
         log.info("StoreRenovationRequirementServiceImpl.saveOrUpdateRequirement?dto={}", dto);
@@ -175,6 +180,35 @@ public class StoreRenovationRequirementServiceImpl extends ServiceImpl<StoreReno
             return dto;
             return dto;
         });
         });
 
 
+        // 批量查询商铺信息并填充到DTO
+        if (dtoPage.getRecords() != null && !dtoPage.getRecords().isEmpty()) {
+            // 收集所有的门店ID
+            List<Integer> storeIds = dtoPage.getRecords().stream()
+                    .map(StoreRenovationRequirementDto::getStoreId)
+                    .filter(id -> id != null)
+                    .distinct()
+                    .collect(Collectors.toList());
+
+            // 批量查询商铺信息
+            if (!storeIds.isEmpty()) {
+                List<StoreInfo> storeInfoList = storeInfoMapper.selectBatchIds(storeIds);
+                // 转换为Map,key为storeId,value为StoreInfo
+                Map<Integer, StoreInfo> storeInfoMap = storeInfoList.stream()
+                        .collect(Collectors.toMap(StoreInfo::getId, storeInfo -> storeInfo, (v1, v2) -> v1));
+
+                // 填充商铺信息到DTO
+                dtoPage.getRecords().forEach(dto -> {
+                    StoreInfo storeInfo = storeInfoMap.get(dto.getStoreId());
+                    if (storeInfo != null) {
+                        dto.setStoreName(storeInfo.getStoreName());
+                        dto.setStoreTel(storeInfo.getStoreTel());
+                        dto.setStoreAddress(storeInfo.getStoreAddress());
+                        dto.setStoreBlurb(storeInfo.getStoreBlurb());
+                    }
+                });
+            }
+        }
+
         return dtoPage;
         return dtoPage;
     }
     }