zepengdev
2025-07-14 43315000b2840313a5aff96bf314b3c061e4616d
feat: 增加店铺和商品审核功能
25 files added
19 files modified
1770 ■■■■■ changed files
oying-system/src/main/java/com/oying/modules/pc/common/core/constrant/AuditStatusEnum.java 86 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/common/core/constrant/Constants.java 10 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/domain/ProductAudit.java 59 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductAuditData.java 13 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductAuditQueryCriteria.java 29 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductRevisionRecord.java 32 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/domain/enums/ProductChangeTypeEnum.java 7 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/domain/enums/ProductStatusEnum.java 6 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/events/ProductAuditVerdictEvent.java 14 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/mapper/ProductAuditMapper.java 23 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/rest/ProductAuditController.java 72 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/service/ProductAuditService.java 76 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/service/ProductMerchantService.java 2 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductAuditServiceImpl.java 114 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductMerchantServiceImpl.java 62 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/Store.java 13 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/StoreAudit.java 60 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreAuditData.java 12 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreAuditQueryCriteria.java 29 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreFieldUpdateRequest.java 6 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreQueryCriteria.java 5 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreRevisionRecord.java 31 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/enums/StoreChangeTypeEnum.java 9 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/domain/enums/StoreStatusEnum.java 6 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/events/StoreAuditVerdictEvent.java 14 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/mapper/StoreAuditMapper.java 22 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/mapper/StoreMapper.java 4 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreAuditController.java 73 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreController.java 5 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreCustomerController.java 4 ●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreMerchantController.java 96 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/service/StoreAuditService.java 78 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/service/StoreMerchantService.java 16 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/service/StoreQualificationService.java 2 ●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/service/StoreService.java 17 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreAuditServiceImpl.java 122 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreCreateServiceImpl.java 6 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreMerchantServiceImpl.java 229 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreQualificationServiceImpl.java 2 ●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreQueryServiceImpl.java 25 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreServiceImpl.java 173 ●●●●● patch | view | raw | blame | history
oying-system/src/main/resources/mapper/pc/product/ProductAuditMapper.xml 37 ●●●●● patch | view | raw | blame | history
oying-system/src/main/resources/mapper/pc/store/StoreAuditMapper.xml 37 ●●●●● patch | view | raw | blame | history
oying-system/src/main/resources/mapper/pc/store/StoreMapper.xml 32 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/pc/common/core/constrant/AuditStatusEnum.java
New file
@@ -0,0 +1,86 @@
package com.oying.modules.pc.common.core.constrant;
import lombok.Getter;
import java.util.EnumSet;
import java.util.Optional;
@Getter
public enum AuditStatusEnum {
    DRAFT(1000, "草稿"),
    PENDING(1001, "待审核"),
    UNDER_REVIEW(1002, "审核中"),
    REJECTED(1003, "拒绝"),
    APPROVED(1004, "同意"),
    CANCELLED(1005, "取消");
    private final Integer value;
    private final String reasonPhrase;
    AuditStatusEnum(Integer value, String reasonPhrase) {
        this.value = value;
        this.reasonPhrase = reasonPhrase;
    }
    public static AuditStatusEnum get(Integer code) {
        return getOrDefault(code, null);
    }
    public static AuditStatusEnum getOrDefault(Integer code, AuditStatusEnum defaultStatus) {
        for (AuditStatusEnum value : values()) {
            if (value.value.equals(code)) {
                return value;
            }
        }
        return defaultStatus;
    }
    public static boolean isApproved(Integer code) {
        return Optional.ofNullable(get(code)).map(i -> i.equals(APPROVED)).orElse(false);
    }
    public static boolean isCompleted(Integer code) {
        return EnumSet.of(REJECTED, APPROVED, CANCELLED).contains(get(code));
    }
    public static AuditStatusEnum get(String name) {
        return AuditStatusEnum.valueOf(name);
    }
    public static EnumSet<AuditStatusEnum> getNext(AuditStatusEnum status) {
        switch (status) {
            case DRAFT:
                return EnumSet.of(PENDING);
            case PENDING:
                return EnumSet.of(UNDER_REVIEW, APPROVED, REJECTED, CANCELLED);
            case UNDER_REVIEW:
                return EnumSet.of(APPROVED, REJECTED);
            case APPROVED:
            case REJECTED:
            case CANCELLED:
            default:
                return EnumSet.noneOf(AuditStatusEnum.class);
        }
    }
    public static boolean hasNext(AuditStatusEnum status) {
        return !getNext(status).isEmpty();
    }
    public EnumSet<AuditStatusEnum> getNext() {
        return getNext(this);
    }
    public boolean nextContains(AuditStatusEnum status) {
        return getNext().contains(status);
    }
    public boolean nextContains(String status) {
        return nextContains(get(status));
    }
    public boolean hasNext() {
        return hasNext(this);
    }
}
oying-system/src/main/java/com/oying/modules/pc/common/core/constrant/Constants.java
@@ -47,4 +47,14 @@
     * https请求
     */
    public static final String HTTPS = "https://";
    /**
     * 否
     */
    public static final Integer NOT = 0;
    /**
     * 是
     */
    public static final Integer YES = 1;
}
oying-system/src/main/java/com/oying/modules/pc/product/domain/ProductAudit.java
New file
@@ -0,0 +1,59 @@
package com.oying.modules.pc.product.domain;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.oying.base.BaseEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Date;
/**
* @description /
* @author lzp
* @date 2025-07-01
**/
@Getter
@Setter
@TableName("pc_product_audit")
public class ProductAudit extends BaseEntity implements Serializable {
    @TableId(value = "audit_id", type = IdType.AUTO)
    @ApiModelProperty(value = "审核ID")
    private Long auditId;
    @NotNull
    @ApiModelProperty(value = "商品ID")
    private Long productId;
    @NotBlank
    @ApiModelProperty(value = "类型")
    private String type;
    @ApiModelProperty(value = "审核状态")
    private Integer status;
    @ApiModelProperty(value = "审核原因")
    private String reason;
    @ApiModelProperty(value = "审核人")
    private String auditor;
    @ApiModelProperty(value = "审核时间")
    private Date auditTime;
    @NotBlank
    @ApiModelProperty(value = "审核内容")
    private String data;
    public void copy(ProductAudit source){
        BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
    }
}
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductAuditData.java
New file
@@ -0,0 +1,13 @@
package com.oying.modules.pc.product.domain.dto;
import com.oying.modules.pc.store.domain.Store;
import com.oying.modules.pc.store.domain.dto.StoreRevisionRecord;
import lombok.Data;
@Data
public class ProductAuditData {
    private Store store;
    private StoreRevisionRecord revisionRecord;
}
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductAuditQueryCriteria.java
New file
@@ -0,0 +1,29 @@
package com.oying.modules.pc.product.domain.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @author lzp
* @date 2025-07-01
**/
@Data
public class ProductAuditQueryCriteria {
    @NotNull
    @ApiModelProperty(value = "商品ID")
    private Long productId;
    @NotBlank
    @ApiModelProperty(value = "类型")
    private String type;
    @ApiModelProperty(value = "页码", example = "1")
    private Integer page = 1;
    @ApiModelProperty(value = "每页数据量", example = "10")
    private Integer size = 10;
}
oying-system/src/main/java/com/oying/modules/pc/product/domain/dto/ProductRevisionRecord.java
New file
@@ -0,0 +1,32 @@
package com.oying.modules.pc.product.domain.dto;
import com.oying.modules.pc.store.domain.StoreQualification;
import com.oying.modules.pc.store.domain.dto.StoreQualificationUpdateRequest;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class ProductRevisionRecord {
    @ApiModelProperty(value = "店铺ID")
    private Long storeId;
    @ApiModelProperty(value = "店铺名称")
    private String storeName;
    @ApiModelProperty(value = "资质")
    private List<StoreQualification> qualifications = new ArrayList<>();
    @ApiModelProperty(value = "删除的资质")
    private List<Long> deletedQualificationIds = new ArrayList<>();
    @ApiModelProperty(value = "修改的资质")
    private List<StoreQualificationUpdateRequest> updatedQualifications = new ArrayList<>();
    @ApiModelProperty(value = "新增的资质")
    private List<StoreQualification> newQualifications = new ArrayList<>();
}
oying-system/src/main/java/com/oying/modules/pc/product/domain/enums/ProductChangeTypeEnum.java
New file
@@ -0,0 +1,7 @@
package com.oying.modules.pc.product.domain.enums;
public enum ProductChangeTypeEnum {
    PUT_ON_SHELF
}
oying-system/src/main/java/com/oying/modules/pc/product/domain/enums/ProductStatusEnum.java
@@ -23,11 +23,15 @@
    }
    public static ProductStatusEnum get(Integer code) {
        return getOrDefault(code, null);
    }
    public static ProductStatusEnum getOrDefault(Integer code, ProductStatusEnum productStatusEnum) {
        for (ProductStatusEnum value : values()) {
            if (value.value.equals(code)) {
                return value;
            }
        }
        return null;
        return productStatusEnum;
    }
}
oying-system/src/main/java/com/oying/modules/pc/product/events/ProductAuditVerdictEvent.java
New file
@@ -0,0 +1,14 @@
package com.oying.modules.pc.product.events;
import lombok.Data;
@Data
public class ProductAuditVerdictEvent {
    private Long auditId;
    public ProductAuditVerdictEvent(Long auditId) {
        this.auditId = auditId;
    }
}
oying-system/src/main/java/com/oying/modules/pc/product/mapper/ProductAuditMapper.java
New file
@@ -0,0 +1,23 @@
package com.oying.modules.pc.product.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.oying.modules.pc.product.domain.ProductAudit;
import com.oying.modules.pc.product.domain.dto.ProductAuditQueryCriteria;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * @author lzp
 * @date 2025-07-01
 **/
@Mapper
public interface ProductAuditMapper extends BaseMapper<ProductAudit> {
    IPage<ProductAudit> findAll(@Param("criteria") ProductAuditQueryCriteria criteria, Page<Object> page);
    List<ProductAudit> findAll(@Param("criteria") ProductAuditQueryCriteria criteria);
}
oying-system/src/main/java/com/oying/modules/pc/product/rest/ProductAuditController.java
New file
@@ -0,0 +1,72 @@
package com.oying.modules.pc.product.rest;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.oying.annotation.Log;
import com.oying.modules.pc.product.domain.ProductAudit;
import com.oying.modules.pc.product.domain.dto.ProductAuditQueryCriteria;
import com.oying.modules.pc.product.service.ProductAuditService;
import com.oying.utils.PageResult;
import com.oying.utils.R;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
 * @author lzp
 * @date 2025-07-01
 **/
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/pc/product/audit")
public class ProductAuditController {
    private final ProductAuditService auditService;
    @GetMapping
    @ApiOperation("查询审批信息")
    //@PreAuthorize("@el.check('productAudit:list')")
    public ResponseEntity<?> queryProductAudit(ProductAuditQueryCriteria criteria) {
        Page<Object> page = new Page<>(criteria.getPage(), criteria.getSize());
        return ResponseEntity.ok(R.success(auditService.queryAll(criteria, page)));
    }
    @PostMapping
    @Log("新增审批信息")
    @ApiOperation("新增审批信息")
    //@PreAuthorize("@el.check('productAudit:add')")
    public ResponseEntity<Object> createProductAudit(@Validated @RequestBody ProductAudit resources) {
        auditService.create(resources);
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }
    @PutMapping
    @Log("修改审批信息")
    @ApiOperation("修改审批信息")
    //@PreAuthorize("@el.check('productAudit:edit')")
    public ResponseEntity<Object> updateProductAudit(@Validated @RequestBody ProductAudit resources) {
        auditService.update(resources, false);
        return ResponseEntity.noContent().build();
    }
    @PostMapping(value = "/verdict")
    @ApiOperation("处理审核")
    public ResponseEntity<?> submitAuditVerdict(@RequestBody ProductAudit resources) {
        auditService.processVerdict(resources);
        return ResponseEntity.noContent().build();
    }
    @DeleteMapping
    @Log("删除审批信息")
    @ApiOperation("删除审批信息")
    //@PreAuthorize("@el.check('productAudit:del')")
    public ResponseEntity<Object> deleteProductAudit(@ApiParam(value = "传ID数组[]") @RequestBody List<Long> ids) {
        auditService.deleteAll(ids);
        return ResponseEntity.noContent().build();
    }
}
oying-system/src/main/java/com/oying/modules/pc/product/service/ProductAuditService.java
New file
@@ -0,0 +1,76 @@
package com.oying.modules.pc.product.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.oying.modules.pc.product.domain.ProductAudit;
import com.oying.modules.pc.product.domain.dto.ProductAuditQueryCriteria;
import com.oying.utils.PageResult;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
 * @author lzp
 * @description 服务接口
 * @date 2025-07-01
 **/
public interface ProductAuditService extends IService<ProductAudit> {
    /**
     * 查询数据分页
     *
     * @param criteria 条件
     * @param page     分页参数
     * @return PageResult
     */
    PageResult<ProductAudit> queryAll(ProductAuditQueryCriteria criteria, Page<Object> page);
    /**
     * 查询所有数据不分页
     *
     * @param criteria 条件参数
     * @return List<StoreRevisionDto>
     */
    List<ProductAudit> queryAll(ProductAuditQueryCriteria criteria);
    /**
     * 商品ID查询修订数据,
     *
     * @param productId 必须的参数,不能为null
     * @return 查询到的结果
     */
    ProductAudit getByProductId(Long productId);
    /**
     * 创建
     *
     * @param resources /
     */
    void create(ProductAudit resources);
    /**
     * 编辑
     *
     * @param resources /
     */
    void update(ProductAudit resources, boolean isDirectUpdate);
    /**
     * 多选删除
     *
     * @param ids /
     */
    void deleteAll(List<Long> ids);
    /**
     * 导出数据
     *
     * @param all      待导出的数据
     * @param response /
     * @throws IOException /
     */
    void download(List<ProductAudit> all, HttpServletResponse response) throws IOException;
    void processVerdict(ProductAudit resources);
}
oying-system/src/main/java/com/oying/modules/pc/product/service/ProductMerchantService.java
@@ -2,6 +2,7 @@
import com.oying.modules.pc.product.domain.dto.ProductMerchantCreateRequest;
import com.oying.modules.pc.product.domain.dto.ProductMerchantUpdateRequest;
import com.oying.modules.pc.product.events.ProductAuditVerdictEvent;
import java.util.List;
@@ -14,5 +15,6 @@
    void batchDelete(List<Long> ids);
    void putOnShelf(Long productId);
    void takeOffShelf(Long productId);
    void handleAuditVerdictEvent(ProductAuditVerdictEvent event);
}
oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductAuditServiceImpl.java
New file
@@ -0,0 +1,114 @@
package com.oying.modules.pc.product.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.oying.exception.BadRequestException;
import com.oying.modules.pc.common.core.constrant.AuditStatusEnum;
import com.oying.modules.pc.product.domain.ProductAudit;
import com.oying.modules.pc.product.domain.dto.ProductAuditQueryCriteria;
import com.oying.modules.pc.product.events.ProductAuditVerdictEvent;
import com.oying.modules.pc.product.mapper.ProductAuditMapper;
import com.oying.modules.pc.product.service.ProductAuditService;
import com.oying.utils.FileUtil;
import com.oying.utils.PageResult;
import com.oying.utils.PageUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
 * @author lzp
 * @description 服务实现
 * @date 2025-07-01
 **/
@Service
@RequiredArgsConstructor
public class ProductAuditServiceImpl extends ServiceImpl<ProductAuditMapper, ProductAudit> implements ProductAuditService {
    private final ApplicationEventPublisher eventPublisher;
    private final ProductAuditMapper productAuditMapper;
    @Override
    public PageResult<ProductAudit> queryAll(ProductAuditQueryCriteria criteria, Page<Object> page) {
        return PageUtil.toPage(productAuditMapper.findAll(criteria, page));
    }
    @Override
    public List<ProductAudit> queryAll(ProductAuditQueryCriteria criteria) {
        return productAuditMapper.findAll(criteria);
    }
    @Override
    public ProductAudit getByProductId(Long productId) {
        LambdaQueryWrapper<ProductAudit> wrapper = new LambdaQueryWrapper<ProductAudit>()
                .eq(ProductAudit::getProductId, productId);
        return productAuditMapper.selectOne(wrapper);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(ProductAudit resources) {
        productAuditMapper.insert(resources);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(ProductAudit resources, boolean isDirectUpdate) {
        if (isDirectUpdate) {
            productAuditMapper.updateById(resources);
        } else {
            ProductAudit existingRevision = this.getById(resources.getAuditor());
            existingRevision.copy(resources);
            productAuditMapper.updateById(existingRevision);
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void processVerdict(ProductAudit resources) {
        AuditStatusEnum newAuditStatus = AuditStatusEnum.get(resources.getStatus());
        ProductAudit existingAudit = this.getById(resources.getAuditId());
        AuditStatusEnum existingAuditStatus = AuditStatusEnum.getOrDefault(existingAudit.getStatus(), AuditStatusEnum.DRAFT);
        if (!existingAuditStatus.nextContains(newAuditStatus)) {
            throw new BadRequestException("审核状态错误");
        }
        existingAudit.copy(resources);
        productAuditMapper.updateById(existingAudit);
        eventPublisher.publishEvent(new ProductAuditVerdictEvent(existingAudit.getAuditId()));
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteAll(List<Long> ids) {
        productAuditMapper.deleteBatchIds(ids);
    }
    @Override
    public void download(List<ProductAudit> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (ProductAudit productAudit : all) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("审核ID", productAudit.getAuditId());
            map.put("商品ID", productAudit.getProductId());
            map.put("审批ID", productAudit.getAuditId());
            map.put("类型", productAudit.getType());
            map.put("新数据", productAudit.getData());
            map.put("创建人", productAudit.getCreateBy());
            map.put("创建时间", productAudit.getCreateTime());
            map.put("修改人", productAudit.getUpdateBy());
            map.put("修改时间", productAudit.getUpdateTime());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }
}
oying-system/src/main/java/com/oying/modules/pc/product/service/impl/ProductMerchantServiceImpl.java
@@ -3,32 +3,36 @@
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.oying.exception.EntityExistException;
import com.oying.exception.BadRequestException;
import com.oying.exception.EntityNotFoundException;
import com.oying.modules.pc.common.core.constrant.AuditStatusEnum;
import com.oying.modules.pc.product.converter.ProductImageAssembler;
import com.oying.modules.pc.product.converter.ProductLabelAssembler;
import com.oying.modules.pc.product.domain.Product;
import com.oying.modules.pc.product.domain.ProductAudit;
import com.oying.modules.pc.product.domain.ProductImage;
import com.oying.modules.pc.product.domain.ProductLabel;
import com.oying.modules.pc.product.domain.dto.ProductImageCreateRequest;
import com.oying.modules.pc.product.domain.dto.ProductLabelCreateRequest;
import com.oying.modules.pc.product.domain.dto.ProductMerchantCreateRequest;
import com.oying.modules.pc.product.domain.dto.ProductMerchantUpdateRequest;
import com.oying.modules.pc.product.domain.enums.ProductChangeTypeEnum;
import com.oying.modules.pc.product.domain.enums.ProductStatusEnum;
import com.oying.modules.pc.product.service.ProductImageService;
import com.oying.modules.pc.product.service.ProductLabelService;
import com.oying.modules.pc.product.service.ProductMerchantService;
import com.oying.modules.pc.product.service.ProductService;
import com.oying.modules.pc.product.events.ProductAuditVerdictEvent;
import com.oying.modules.pc.product.service.*;
import com.oying.modules.pc.utils.ImageUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class ProductMerchantServiceImpl implements ProductMerchantService {
@@ -36,6 +40,7 @@
    private final ProductService productService;
    private final ProductImageService productImageService;
    private final ProductLabelService productLabelService;
    private final ProductAuditService productAuditService;
    @Override
    @Transactional(rollbackFor = Exception.class)
@@ -71,6 +76,7 @@
    @Transactional(rollbackFor = Exception.class)
    public void update(ProductMerchantUpdateRequest request) {
        Product existingProduct = this.findOrThrow(request.getProductId());
        this.validateApprovedStatus(existingProduct.getStatus());
        this.processImagesUpdate(request);
        this.processLabelsUpdate(request);
        BeanUtils.copyProperties(request, existingProduct);
@@ -80,6 +86,8 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateImages(ProductMerchantUpdateRequest request) {
        Product existingProduct = this.findOrThrow(request.getProductId());
        this.validateApprovedStatus(existingProduct.getStatus());
        this.findOrThrow(request.getProductId());
        this.processImagesUpdate(request);
    }
@@ -87,6 +95,8 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateLabels(ProductMerchantUpdateRequest request) {
        Product existingProduct = this.findOrThrow(request.getProductId());
        this.validateApprovedStatus(existingProduct.getStatus());
        this.findOrThrow(request.getProductId());
        this.processImagesUpdate(request);
    }
@@ -99,9 +109,14 @@
    @Override
    public void putOnShelf(Long productId) {
        ProductAudit audit = new ProductAudit();
        audit.setProductId(productId);
        audit.setType(ProductChangeTypeEnum.PUT_ON_SHELF.name());
        audit.setStatus(AuditStatusEnum.PENDING.getValue());
        productAuditService.create(audit);
        LambdaUpdateWrapper<Product> wrapper = new LambdaUpdateWrapper<Product>()
                .eq(Product::getProductId, productId)
                .set(Product::getStatus, ProductStatusEnum.AVAILABLE.getValue());
                .set(Product::getStatus, ProductStatusEnum.PENDING.getValue());
        productService.update(wrapper);
    }
@@ -111,6 +126,21 @@
                .eq(Product::getProductId, productId)
                .set(Product::getStatus, ProductStatusEnum.NO_AVAILABLE.getValue());
        productService.update(wrapper);
    }
    @Override
    @EventListener
    @Transactional(rollbackFor = Exception.class)
    public void handleAuditVerdictEvent(ProductAuditVerdictEvent event) {
        try {
            ProductAudit audit = productAuditService.getById(event.getAuditId());
            ProductChangeTypeEnum changeType = ProductChangeTypeEnum.valueOf(audit.getType());
            if (changeType == ProductChangeTypeEnum.PUT_ON_SHELF) {
                this.handlePutOnShelfAuditEvent(audit);
            }
        } catch (Exception e) {
            log.error("处理商品审核结果异常", e);
        }
    }
    private Product findOrThrow(Long productId) {
@@ -146,4 +176,22 @@
            productLabelService.batchCreate(newLabels);
        }
    }
    private void handlePutOnShelfAuditEvent(ProductAudit audit) {
        AuditStatusEnum auditStatus = AuditStatusEnum.get(audit.getStatus());
        if (AuditStatusEnum.APPROVED.equals(auditStatus)) {
            Product existingProduct = productService.getById(audit.getProductId());
            existingProduct.setStatus(auditStatus.getValue());
            productService.updateById(existingProduct);
        }
    }
    private void validateApprovedStatus(Integer status) {
        Set<ProductStatusEnum> statusEnumSet = CollectionUtil.newHashSet(ProductStatusEnum.PENDING,
                ProductStatusEnum.UNDER_REVIEW, ProductStatusEnum.AVAILABLE);
        ProductStatusEnum existingStatus = ProductStatusEnum.getOrDefault(status, ProductStatusEnum.DRAFT);
        if (statusEnumSet.contains(existingStatus)) {
            throw new BadRequestException("在售商品无法修改");
        }
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/domain/Store.java
@@ -18,6 +18,7 @@
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalTime;
import java.util.Date;
import java.util.List;
/**
@@ -92,6 +93,9 @@
    @ApiModelProperty(value = "营业结束时间")
    private LocalTime closeTime;
    @ApiModelProperty(value = "营业状态")
    private Integer businessStatus;
    @ApiModelProperty(value = "省级代码")
    private String provinceCode;
@@ -128,9 +132,8 @@
    @ApiModelProperty(value = "是否支持自提")
    private Integer selfPickup;
    @NotNull
    @ApiModelProperty(value = "版本号")
    private Long version;
    @ApiModelProperty(value = "审核状态")
    private Integer auditStatus;
    @TableField(exist = false)
    @ApiModelProperty(value = "评分")
@@ -152,6 +155,10 @@
    @ApiModelProperty(value = "月销售量")
    private Integer monthlyUnitsSold = 0;
    @NotNull
    @ApiModelProperty(value = "版本号")
    private Long version;
    @TableField(exist = false)
    @ApiModelProperty(value = "资质")
    private List<StoreQualification> qualifications;
oying-system/src/main/java/com/oying/modules/pc/store/domain/StoreAudit.java
New file
@@ -0,0 +1,60 @@
package com.oying.modules.pc.store.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.oying.base.BaseEntity;
import cn.hutool.core.bean.BeanUtil;
import io.swagger.annotations.ApiModelProperty;
import cn.hutool.core.bean.copier.CopyOptions;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;
/**
* @description /
* @author lzp
* @date 2025-07-01
**/
@Getter
@Setter
@TableName("pc_store_audit")
public class StoreAudit extends BaseEntity implements Serializable {
    @TableId(value = "audit_id", type = IdType.AUTO)
    @ApiModelProperty(value = "审核ID")
    private Long auditId;
    @NotNull
    @ApiModelProperty(value = "店铺ID")
    private Long storeId;
    @NotBlank
    @ApiModelProperty(value = "类型")
    private String type;
    @ApiModelProperty(value = "审核状态")
    private Integer status;
    @ApiModelProperty(value = "审核原因")
    private String reason;
    @ApiModelProperty(value = "审核人")
    private String auditor;
    @ApiModelProperty(value = "审核时间")
    private Date auditTime;
    @NotBlank
    @ApiModelProperty(value = "审核内容")
    private String data;
    public void copy(StoreAudit source){
        BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreAuditData.java
New file
@@ -0,0 +1,12 @@
package com.oying.modules.pc.store.domain.dto;
import com.oying.modules.pc.store.domain.Store;
import lombok.Data;
@Data
public class StoreAuditData {
    private Store store;
    private StoreRevisionRecord revisionRecord;
}
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreAuditQueryCriteria.java
New file
@@ -0,0 +1,29 @@
package com.oying.modules.pc.store.domain.dto;
import lombok.Data;
import io.swagger.annotations.ApiModelProperty;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @author lzp
* @date 2025-07-01
**/
@Data
public class StoreAuditQueryCriteria {
    @NotNull
    @ApiModelProperty(value = "店铺ID")
    private Long storeId;
    @NotBlank
    @ApiModelProperty(value = "类型")
    private String type;
    @ApiModelProperty(value = "页码", example = "1")
    private Integer page = 1;
    @ApiModelProperty(value = "每页数据量", example = "10")
    private Integer size = 10;
}
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreFieldUpdateRequest.java
@@ -34,11 +34,17 @@
    @NotNull(groups = UpdateStoreBusinessStatusGroup.class)
    private Integer businessStatus;
    @ApiModelProperty(value = "店铺logo图片")
    private Long logoImageId;
    public interface UpdateStoreLogoImageGroup{}
    @NotBlank(groups = UpdateStoreLogoImageGroup.class)
    private String logoImageUploadId;
    @ApiModelProperty(value = "店铺封面图")
    private Long coverImageId;
    public interface UpdateStoreCoverImageGroup{}
    @NotBlank(groups = UpdateStoreCoverImageGroup.class)
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreQueryCriteria.java
@@ -25,13 +25,15 @@
    private Integer status;
    private Integer businessStatus;
    private String blurry;
    private Double longitude; // 中心点经度
    private Double latitude;  // 中心点纬度
    private Integer radius = 10000; // 搜索半径(米)
    private Integer radius = 50000000; // 搜索半径(米)
    private Integer limit = 20; // 返回数量限制
@@ -48,6 +50,7 @@
        baseKeyJoiner.add("platformCategoryId=" + platformCategoryId);
        baseKeyJoiner.add("storeName=" + storeName);
        baseKeyJoiner.add("status=" + status);
        baseKeyJoiner.add("businessStatus=" + businessStatus);
        baseKeyJoiner.add("blurry=" + blurry);
        baseKeyJoiner.add("longitude=" + longitude);
        baseKeyJoiner.add("latitude=" + latitude);
oying-system/src/main/java/com/oying/modules/pc/store/domain/dto/StoreRevisionRecord.java
New file
@@ -0,0 +1,31 @@
package com.oying.modules.pc.store.domain.dto;
import com.oying.modules.pc.store.domain.StoreQualification;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class StoreRevisionRecord {
    @ApiModelProperty(value = "店铺ID")
    private Long storeId;
    @ApiModelProperty(value = "店铺名称")
    private String storeName;
    @ApiModelProperty(value = "资质")
    private List<StoreQualification> qualifications = new ArrayList<>();
    @ApiModelProperty(value = "删除的资质")
    private List<Long> deletedQualificationIds = new ArrayList<>();
    @ApiModelProperty(value = "修改的资质")
    private List<StoreQualificationUpdateRequest> updatedQualifications = new ArrayList<>();
    @ApiModelProperty(value = "新增的资质")
    private List<StoreQualification> newQualifications = new ArrayList<>();
}
oying-system/src/main/java/com/oying/modules/pc/store/domain/enums/StoreChangeTypeEnum.java
New file
@@ -0,0 +1,9 @@
package com.oying.modules.pc.store.domain.enums;
public enum StoreChangeTypeEnum {
    NEW_STORE,
    STORE_UPDATE,
    STORE_NAME_UPDATE
}
oying-system/src/main/java/com/oying/modules/pc/store/domain/enums/StoreStatusEnum.java
@@ -23,11 +23,15 @@
    }
    public static StoreStatusEnum get(Integer code) {
        return getOrDefault(code, null);
    }
    public static StoreStatusEnum getOrDefault(Integer code, StoreStatusEnum defaultStatus) {
        for (StoreStatusEnum value : values()) {
            if (value.value.equals(code)) {
                return value;
            }
        }
        return null;
        return defaultStatus;
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/events/StoreAuditVerdictEvent.java
New file
@@ -0,0 +1,14 @@
package com.oying.modules.pc.store.events;
import lombok.Data;
@Data
public class StoreAuditVerdictEvent {
    private Long auditId;
    public StoreAuditVerdictEvent(Long auditId) {
        this.auditId = auditId;
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/mapper/StoreAuditMapper.java
New file
@@ -0,0 +1,22 @@
package com.oying.modules.pc.store.mapper;
import com.oying.modules.pc.store.domain.StoreAudit;
import com.oying.modules.pc.store.domain.dto.StoreAuditQueryCriteria;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
/**
* @author lzp
* @date 2025-07-01
**/
@Mapper
public interface StoreAuditMapper extends BaseMapper<StoreAudit> {
    IPage<StoreAudit> findAll(@Param("criteria") StoreAuditQueryCriteria criteria, Page<Object> page);
    List<StoreAudit> findAll(@Param("criteria") StoreAuditQueryCriteria criteria);
}
oying-system/src/main/java/com/oying/modules/pc/store/mapper/StoreMapper.java
@@ -10,6 +10,8 @@
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
/**
@@ -35,4 +37,6 @@
    List<Store> queryUserStores(@Param("userId") Long userId);
    List<Store> queryByIds(@Param("ids") Collection<? extends Serializable> idList);
}
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreAuditController.java
New file
@@ -0,0 +1,73 @@
package com.oying.modules.pc.store.rest;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.oying.annotation.Log;
import com.oying.modules.pc.store.domain.StoreAudit;
import com.oying.modules.pc.store.domain.dto.StoreAuditQueryCriteria;
import com.oying.modules.pc.store.service.StoreAuditService;
import com.oying.utils.PageResult;
import com.oying.utils.R;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
 * @author lzp
 * @date 2025-07-01
 **/
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/pc/store/audit")
public class StoreAuditController {
    private final StoreAuditService auditService;
    @GetMapping
    @ApiOperation("查询审批信息")
    //@PreAuthorize("@el.check('storeAudit:list')")
    public ResponseEntity<?> queryStoreAudit(StoreAuditQueryCriteria criteria) {
        Page<Object> page = new Page<>(criteria.getPage(), criteria.getSize());
        return ResponseEntity.ok(R.success(auditService.queryAll(criteria, page)));
    }
    @PostMapping
    @Log("新增审批信息")
    @ApiOperation("新增审批信息")
    //@PreAuthorize("@el.check('storeAudit:add')")
    public ResponseEntity<Object> createStoreAudit(@Validated @RequestBody StoreAudit resources) {
        auditService.create(resources);
        return new ResponseEntity<>(HttpStatus.CREATED);
    }
    @PutMapping
    @Log("修改审批信息")
    @ApiOperation("修改审批信息")
    //@PreAuthorize("@el.check('storeAudit:edit')")
    public ResponseEntity<Object> updateStoreAudit(@Validated @RequestBody StoreAudit resources) {
        auditService.update(resources, false);
        return ResponseEntity.noContent().build();
    }
    @PutMapping(value = "/verdict")
    @ApiOperation("处理审核")
    //@PreAuthorize("@el.check('merchant:store:create')")
    public ResponseEntity<?> submitAuditVerdict(@RequestBody StoreAudit resources) {
        auditService.processVerdict(resources);
        return ResponseEntity.noContent().build();
    }
    @DeleteMapping
    @Log("删除审批信息")
    @ApiOperation("删除审批信息")
    //@PreAuthorize("@el.check('storeAudit:del')")
    public ResponseEntity<Object> deleteStoreAudit(@ApiParam(value = "传ID数组[]") @RequestBody List<Long> ids) {
        auditService.deleteAll(ids);
        return ResponseEntity.noContent().build();
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreController.java
@@ -54,6 +54,11 @@
        return ResponseEntity.ok(R.success(storeService.getById(storeId)));
    }
    @GetMapping(value = "/{storeId}/name-check")
    public ResponseEntity<?> checkStoreName(@RequestParam String storeName) {
        return ResponseEntity.ok(R.success(storeService.existsStoreName(storeName)));
    }
    @PostMapping
    @ApiOperation("创建店铺")
    public ResponseEntity<?> create(@RequestBody StoreCreateRequest request) {
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreCustomerController.java
@@ -7,6 +7,7 @@
import com.oying.modules.pc.product.service.ProductService;
import com.oying.modules.pc.store.domain.Store;
import com.oying.modules.pc.store.domain.dto.StoreQueryCriteria;
import com.oying.modules.pc.store.domain.enums.StoreStatusEnum;
import com.oying.modules.pc.store.service.StoreQualificationService;
import com.oying.modules.pc.store.service.StoreQueryService;
import com.oying.modules.pc.store.service.StoreService;
@@ -47,6 +48,7 @@
    @ApiOperation("查询店铺")
    public ResponseEntity<?> getStoresByPage(StoreQueryCriteria criteria) {
        criteria.setLimit(1000);
        criteria.setBusinessStatus(StoreStatusEnum.OPEN.getValue());
        PageResult<Store> pagedStores = storeQueryService.findPagedStores(criteria);
        pagedStores.getContent().forEach(store -> store.setProducts(this.getProductsByStoreId(store.getStoreId())));
        return ResponseEntity.ok(R.success(pagedStores));
@@ -67,7 +69,7 @@
    public ResponseEntity<?> getStoreDetailsById(@PathVariable("storeId") Long storeId) {
        Store store = storeService.getById(storeId);
        if (ObjUtil.isNotEmpty(store)) {
            store.setQualifications(storeQualificationService.queryByStoreId(storeId));
            store.setQualifications(storeQualificationService.getByStoreId(storeId));
        }
        return ResponseEntity.ok(R.success(store));
    }
oying-system/src/main/java/com/oying/modules/pc/store/rest/StoreMerchantController.java
@@ -2,20 +2,18 @@
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import com.oying.modules.pc.store.domain.dto.StoreUpdateRequest;
import com.oying.modules.pc.store.service.StoreQualificationService;
import com.oying.utils.R;
import com.oying.exception.BadRequestException;
import com.oying.modules.pc.store.domain.Store;
import com.oying.modules.pc.store.domain.dto.StoreCreateRequest;
import com.oying.modules.pc.store.domain.dto.StoreFieldUpdateRequest;
import com.oying.modules.pc.store.domain.dto.StoreQueryCriteria;
import com.oying.modules.pc.store.service.StoreCreateService;
import com.oying.modules.pc.store.service.StoreService;
import com.oying.modules.pc.store.domain.dto.*;
import com.oying.modules.pc.store.domain.enums.StoreStatusEnum;
import com.oying.modules.pc.store.service.*;
import com.oying.modules.pc.store.view.StoreMerchantView;
import com.oying.modules.pc.store.view.StoreSimpleView;
import com.oying.utils.R;
import com.oying.utils.SecurityUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -23,12 +21,12 @@
import org.springframework.beans.BeanUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -46,12 +44,14 @@
    private final StoreService storeService;
    private final StoreCreateService storeCreateService;
    private final StoreQualificationService storeQualificationService;
    private final StoreQueryService storeQueryService;
    private final StoreMerchantService storeMerchantService;
    @GetMapping(value = "/list")
    @ApiOperation("查询所有店铺")
    //@PreAuthorize("@el.check('merchant:store:list')")
    public ResponseEntity<?> getList() {
        List<Store> stores = Optional.ofNullable(storeService.queryUserStores(SecurityUtils.getCurrentUserId())).orElse(ListUtil.empty());
        List<Store> stores = Optional.ofNullable(storeService.getUserStores(SecurityUtils.getCurrentUserId())).orElse(ListUtil.empty());
        return ResponseEntity.ok(R.success(stores));
    }
@@ -59,7 +59,7 @@
    @ApiOperation("查询所有店铺")
    //@PreAuthorize("@el.check('merchant:store:list')")
    public ResponseEntity<?> getSimpleStores() {
        List<Store> stores = Optional.ofNullable(storeService.queryUserStores(SecurityUtils.getCurrentUserId())).orElse(ListUtil.empty());
        List<Store> stores = Optional.ofNullable(storeService.getUserStores(SecurityUtils.getCurrentUserId())).orElse(ListUtil.empty());
        List<StoreSimpleView> storeViewList = stores.stream().map(i -> {
            StoreSimpleView view = new StoreSimpleView();
            BeanUtil.copyProperties(i, view, CopyOptions.create().setIgnoreNullValue(true));
@@ -84,7 +84,7 @@
    public ResponseEntity<?> getStoreDetailsById(@PathVariable Long storeId) {
        Store store = storeService.getById(storeId);
        if (ObjUtil.isNotEmpty(store)) {
            store.setQualifications(storeQualificationService.queryByStoreId(storeId));
            store.setQualifications(storeQualificationService.getByStoreId(storeId));
        }
        return ResponseEntity.ok(R.success(store));
    }
@@ -93,19 +93,27 @@
    @ApiOperation("创建店铺")
    //@PreAuthorize("@el.check('merchant:store:create')")
    public ResponseEntity<?> create(@RequestBody StoreCreateRequest request) {
        return ResponseEntity.status(HttpStatus.CREATED).body(storeCreateService.create(request));
        return ResponseEntity.status(HttpStatus.CREATED).body(storeMerchantService.createStore(request));
    }
    @PostMapping(value = "/{storeId}/audit/submit")
    @ApiOperation("提交审核")
    //@PreAuthorize("@el.check('merchant:store:create')")
    public ResponseEntity<?> submitAudit(@PathVariable Long storeId) {
        storeMerchantService.submitStoreAudit(storeId);
        return ResponseEntity.noContent().build();
    }
    /**
     * 修改店铺信息
     */
    @PostMapping(value = "/{storeId}")
    @PutMapping(value = "/{storeId}")
    @ApiOperation("修改店铺")
    //@PreAuthorize("@el.check('merchant:store:update')" +
    //        " and @storeMerchantOwnershipService.check(#storeId)")
    public ResponseEntity<?> update(@PathVariable("storeId") Long storeId, @RequestBody StoreUpdateRequest request) {
        request.setStoreId(ObjectUtil.defaultIfNull(request.getStoreId(), storeId));
        storeService.update(request);
        storeMerchantService.updateStore(request);
        return ResponseEntity.noContent().build();
    }
@@ -119,7 +127,8 @@
    public ResponseEntity<?> updateLogo(@PathVariable("storeId") Long storeId,
                           @Validated(value = StoreFieldUpdateRequest.UpdateStoreLogoImageGroup.class)
                           @RequestBody StoreFieldUpdateRequest request) {
        storeService.updateLogo(storeId, request.getLogoImageUploadId(), request.getVersion());
        Long logeImageId = Optional.ofNullable(request.getLogoImageId()).orElse(Long.getLong(request.getLogoImageUploadId()));
        storeService.updateLogo(storeId, logeImageId, request.getVersion());
        return ResponseEntity.noContent().build();
    }
@@ -133,7 +142,11 @@
    public ResponseEntity<?> updateName(@PathVariable("storeId") Long storeId,
                           @Validated(value = StoreFieldUpdateRequest.UpdateStoreNameGroup.class)
                           @RequestBody StoreFieldUpdateRequest request) {
        storeService.updateName(storeId, request.getStoreName(), request.getVersion());
        StoreUpdateRequest updateRequest = new StoreUpdateRequest();
        updateRequest.setStoreId(storeId);
        updateRequest.setStoreName(request.getStoreName());
        storeMerchantService.updateStore(updateRequest);
        //storeMerchantService.saveRevisionStore(request, StoreRevisionTypeEnum.NAME_UPDATE);
        return ResponseEntity.noContent().build();
    }
@@ -156,8 +169,8 @@
     */
    @PatchMapping(value = "/{storeId}/contactPhone")
    @ApiOperation("修改店铺联系电话")
    @PreAuthorize("@el.check('merchant:store:list')" +
            " and @storeMerchantOwnershipService.check(#storeId)")
    /*@PreAuthorize("@el.check('merchant:store:list')" +
            " and @storeMerchantOwnershipService.check(#storeId)")*/
    public ResponseEntity<?> updateContactPhone(@PathVariable("storeId") Long storeId,
                                   @Validated(value = StoreFieldUpdateRequest.UpdateStoreContactPhoneGroup.class)
                                   @RequestBody StoreFieldUpdateRequest request) {
@@ -170,8 +183,8 @@
     */
    @PatchMapping(value = "/{storeId}/address")
    @ApiOperation("修改店铺地址")
    @PreAuthorize("@el.check('merchant:store:address')" +
            " and @storeMerchantOwnershipService.check(#storeId)")
    /*@PreAuthorize("@el.check('merchant:store:address')" +
            " and @storeMerchantOwnershipService.check(#storeId)")*/
    public ResponseEntity<?> updateAddress(@PathVariable("storeId") Long storeId,
                              @Validated(value = StoreFieldUpdateRequest.UpdateStoreAddressGroup.class)
                              @RequestBody StoreFieldUpdateRequest request) {
@@ -184,8 +197,8 @@
     */
    @PatchMapping(value = "/{storeId}/location")
    @ApiOperation("修改店铺坐标")
    @PreAuthorize("@el.check('merchant:store:location')" +
            " and @storeMerchantOwnershipService.check(#storeId)")
    /*@PreAuthorize("@el.check('merchant:store:location')" +
            " and @storeMerchantOwnershipService.check(#storeId)")*/
    public ResponseEntity<?> updateLocation(@PathVariable("storeId") Long storeId,
                               @Validated(value = StoreFieldUpdateRequest.UpdateStoreLocationGroup.class)
                               @RequestBody StoreFieldUpdateRequest request) {
@@ -198,8 +211,8 @@
     */
    @PatchMapping(value = "/{storeId}/radius")
    @ApiOperation("修改店铺配送范围")
    @PreAuthorize("@el.check('merchant:store:radius')" +
            " and @storeMerchantOwnershipService.check(#storeId)")
    /*@PreAuthorize("@el.check('merchant:store:radius')" +
            " and @storeMerchantOwnershipService.check(#storeId)")*/
    public ResponseEntity<?> updateRadius(@PathVariable("storeId") Long storeId,
                             @Validated(value = StoreFieldUpdateRequest.UpdateStoreRadiusGroup.class)
                             @RequestBody StoreFieldUpdateRequest request) {
@@ -212,8 +225,8 @@
     */
    @PatchMapping(value = "/{storeId}/platformCategory")
    @ApiOperation("修改店铺绑定的经营类目")
    @PreAuthorize("@el.check('merchant:store:platformCategory')" +
            " and @storeMerchantOwnershipService.check(#storeId)")
    /*@PreAuthorize("@el.check('merchant:store:platformCategory')" +
            " and @storeMerchantOwnershipService.check(#storeId)")*/
    public ResponseEntity<?> updatePlatformCategory(@PathVariable("storeId") Long storeId,
                                       @Validated(value = StoreFieldUpdateRequest.UpdateStorePlatformCategoryGroup.class)
                                       @RequestBody StoreFieldUpdateRequest request) {
@@ -226,8 +239,8 @@
     */
    @PatchMapping(value = "/{storeId}/businessHours")
    @ApiOperation("修改店铺营业时间")
    @PreAuthorize("@el.check('merchant:store:businessHours')" +
            " and @storeMerchantOwnershipService.check(#storeId)")
    /*@PreAuthorize("@el.check('merchant:store:businessHours')" +
            " and @storeMerchantOwnershipService.check(#storeId)")*/
    public ResponseEntity<?> updateBusinessHours(@PathVariable("storeId") Long storeId,
                                    @Validated(value = StoreFieldUpdateRequest.UpdateStoreBusinessHoursGroup.class)
                                    @RequestBody StoreFieldUpdateRequest request) {
@@ -240,8 +253,8 @@
     */
    @PatchMapping(value = "/{storeId}/deliveryMinimum")
    @ApiOperation("修改起送金额")
    @PreAuthorize("@el.check('merchant:store:deliveryMinimum')" +
            " and @storeMerchantOwnershipService.check(#storeId)")
    /*@PreAuthorize("@el.check('merchant:store:deliveryMinimum')" +
            " and @storeMerchantOwnershipService.check(#storeId)")*/
    public ResponseEntity<?> updateDeliveryMinimum(@PathVariable("storeId") Long storeId,
                                      @Validated(value = StoreFieldUpdateRequest.UpdateStoreDeliveryMinimumGroup.class)
                                      @RequestBody StoreFieldUpdateRequest request) {
@@ -254,8 +267,8 @@
     */
    @PatchMapping(value = "/{storeId}/deliveryFee")
    @ApiOperation("修改配送费用")
    @PreAuthorize("@el.check('merchant:store:deliveryFee')" +
            " and @storeMerchantOwnershipService.check(#storeId)")
    /*@PreAuthorize("@el.check('merchant:store:deliveryFee')" +
            " and @storeMerchantOwnershipService.check(#storeId)")*/
    public ResponseEntity<?> updateDeliveryFee(@PathVariable("storeId") Long storeId,
                                  @Validated(value = StoreFieldUpdateRequest.UpdateStoreDeliveryFeeGroup.class)
                                  @RequestBody StoreFieldUpdateRequest request) {
@@ -267,13 +280,22 @@
     * 修改状态
     */
    @PatchMapping(value = "/{storeId}/businessStatus")
    @ApiOperation("修改状态")
    @PreAuthorize("@el.check('merchant:store:businessStatus')" +
            " and @storeMerchantOwnershipService.check(#storeId)")
    @ApiOperation("修改营业状态")
    /*@PreAuthorize("@el.check('merchant:store:businessStatus')" +
            " and @storeMerchantOwnershipService.check(#storeId)")*/
    public ResponseEntity<?> businessStatus(@PathVariable("storeId") Long storeId,
                               @Validated(value = StoreFieldUpdateRequest.UpdateStoreBusinessStatusGroup.class)
                               @RequestBody StoreFieldUpdateRequest request) {
        storeService.updateStatus(storeId, request.getBusinessStatus(), request.getVersion());
        //storeService.updateStatus(storeId, request.getBusinessStatus(), request.getVersion());
        Store store = new Store();
        store.setStoreId(storeId);
        store.setStatus(request.getBusinessStatus());
        store.setBusinessStatus(request.getBusinessStatus());
        Set<Integer> statusEnumSet = CollectionUtil.newHashSet(StoreStatusEnum.OPEN.getValue(), StoreStatusEnum.CLOSED.getValue());
        if (!statusEnumSet.contains(request.getBusinessStatus())) {
            throw new BadRequestException("状态错误");
        }
        storeMerchantService.updateBusinessStatus(store);
        return ResponseEntity.noContent().build();
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/service/StoreAuditService.java
New file
@@ -0,0 +1,78 @@
package com.oying.modules.pc.store.service;
import com.oying.modules.pc.store.domain.StoreAudit;
import com.oying.modules.pc.store.domain.dto.StoreAuditQueryCriteria;
import java.util.List;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.oying.utils.PageResult;
/**
* @description 服务接口
* @author lzp
* @date 2025-07-01
**/
public interface StoreAuditService extends IService<StoreAudit> {
    /**
    * 查询数据分页
    * @param criteria 条件
    * @param page 分页参数
    * @return PageResult
    */
    PageResult<StoreAudit> queryAll(StoreAuditQueryCriteria criteria, Page<Object> page);
    /**
    * 查询所有数据不分页
    * @param criteria 条件参数
    * @return List<StoreRevisionDto>
    */
    List<StoreAudit> queryAll(StoreAuditQueryCriteria criteria);
    /**
     * 店铺ID查询修订数据,
     *
     * @param storeId 必须的参数,不能为null
     * @return 查询到的结果
     */
    StoreAudit getByStoreId(Long storeId);
    /**
     * 是否有待审记录
     *
     * @param storeId 必须的参数,不能为null
     * @return 查询到的结果
     */
    boolean hasPendingByStoreId(Long storeId);
    /**
    * 创建
    * @param resources /
    */
    void create(StoreAudit resources);
    /**
    * 编辑
    * @param resources /
    */
    void update(StoreAudit resources, boolean isDirectUpdate);
    /**
    * 多选删除
    * @param ids /
    */
    void deleteAll(List<Long> ids);
    /**
    * 导出数据
    * @param all 待导出的数据
    * @param response /
    * @throws IOException /
    */
    void download(List<StoreAudit> all, HttpServletResponse response) throws IOException;
    void processVerdict(StoreAudit resources);
}
oying-system/src/main/java/com/oying/modules/pc/store/service/StoreMerchantService.java
New file
@@ -0,0 +1,16 @@
package com.oying.modules.pc.store.service;
import com.oying.modules.pc.store.domain.Store;
import com.oying.modules.pc.store.domain.dto.StoreCreateRequest;
import com.oying.modules.pc.store.domain.dto.StoreUpdateRequest;
import com.oying.modules.pc.store.events.StoreAuditVerdictEvent;
public interface StoreMerchantService {
    Store createStore(StoreCreateRequest request);
    void updateStore(StoreUpdateRequest request);
    void updateBusinessStatus(Store resources);
    void submitStoreAudit(Long storeId);
    void handleStoreAudit(StoreAuditVerdictEvent event);
}
oying-system/src/main/java/com/oying/modules/pc/store/service/StoreQualificationService.java
@@ -44,7 +44,7 @@
     * @param storeId 条件参数
     * @return List<StoreQualification>
     */
    List<StoreQualification> queryByStoreId(Long storeId);
    List<StoreQualification> getByStoreId(Long storeId);
    /**
    * 创建
oying-system/src/main/java/com/oying/modules/pc/store/service/StoreService.java
@@ -10,6 +10,7 @@
import java.math.BigDecimal;
import java.time.LocalTime;
import java.util.List;
import java.util.Set;
/**
 * 店铺基础信息Service接口
@@ -23,15 +24,25 @@
    List<Store> queryAll(StoreQueryCriteria criteria);
    List<Long> queryStoreIds(StoreQueryCriteria criteria);
    Store getMerchantStore(Long merchantId);
    List<Store> queryUserStores(Long userId);
    List<Store> getStoresByIds(Set<Long> ids);
    List<Store> getUserStores(Long userId);
    Store getOrThrow(Long storeId, Long version);
    void create(Store resources);
    Store create(StoreCreateRequest request);
    boolean update(Store resources, boolean isDirectUpdate);
    boolean update(StoreUpdateRequest request);
    boolean updateLogo(Long storeId, String logo, Long version);
    boolean updateLogo(Long storeId, Long logoImageId, Long version);
    boolean updateName(Long storeId, String storeName, Long version);
@@ -58,4 +69,6 @@
    boolean existsByIdAndMerchantId(Long storeId, Long merchantId);
    boolean existsStoreName(String storeName);
    boolean bindUser(Long store);
}
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreAuditServiceImpl.java
New file
@@ -0,0 +1,122 @@
package com.oying.modules.pc.store.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.oying.exception.BadRequestException;
import com.oying.modules.pc.common.core.constrant.AuditStatusEnum;
import com.oying.modules.pc.store.domain.StoreAudit;
import com.oying.modules.pc.store.domain.dto.StoreAuditQueryCriteria;
import com.oying.modules.pc.store.events.StoreAuditVerdictEvent;
import com.oying.modules.pc.store.mapper.StoreAuditMapper;
import com.oying.modules.pc.store.service.StoreAuditService;
import com.oying.utils.FileUtil;
import com.oying.utils.PageResult;
import com.oying.utils.PageUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
 * @author lzp
 * @description 服务实现
 * @date 2025-07-01
 **/
@Service
@RequiredArgsConstructor
public class StoreAuditServiceImpl extends ServiceImpl<StoreAuditMapper, StoreAudit> implements StoreAuditService {
    private final ApplicationEventPublisher eventPublisher;
    private final StoreAuditMapper storeAuditMapper;
    @Override
    public PageResult<StoreAudit> queryAll(StoreAuditQueryCriteria criteria, Page<Object> page) {
        return PageUtil.toPage(storeAuditMapper.findAll(criteria, page));
    }
    @Override
    public List<StoreAudit> queryAll(StoreAuditQueryCriteria criteria) {
        return storeAuditMapper.findAll(criteria);
    }
    @Override
    public StoreAudit getByStoreId(Long storeId) {
        LambdaQueryWrapper<StoreAudit> wrapper = new LambdaQueryWrapper<StoreAudit>()
                .eq(StoreAudit::getStoreId, storeId);
        return storeAuditMapper.selectOne(wrapper);
    }
    @Override
    public boolean hasPendingByStoreId(Long storeId) {
        LambdaQueryWrapper<StoreAudit> wrapper = new LambdaQueryWrapper<StoreAudit>()
                .eq(StoreAudit::getStoreId, storeId)
                .eq(StoreAudit::getStatus, AuditStatusEnum.PENDING.getValue());
        return storeAuditMapper.selectCount(wrapper) > 0;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(StoreAudit resources) {
        storeAuditMapper.insert(resources);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(StoreAudit resources, boolean isDirectUpdate) {
        if (isDirectUpdate) {
            storeAuditMapper.updateById(resources);
        } else {
            StoreAudit existingRevision = this.getById(resources.getAuditor());
            existingRevision.copy(resources);
            storeAuditMapper.updateById(existingRevision);
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void processVerdict(StoreAudit resources) {
        AuditStatusEnum newAuditStatus = AuditStatusEnum.get(resources.getStatus());
        StoreAudit existingAudit = this.getById(resources.getAuditId());
        AuditStatusEnum existingAuditStatus = AuditStatusEnum.getOrDefault(existingAudit.getStatus(), AuditStatusEnum.DRAFT);
        if (!existingAuditStatus.nextContains(newAuditStatus)) {
            throw new BadRequestException("审核状态错误");
        }
        existingAudit.copy(resources);
        storeAuditMapper.updateById(existingAudit);
        eventPublisher.publishEvent(new StoreAuditVerdictEvent(existingAudit.getAuditId()));
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteAll(List<Long> ids) {
        storeAuditMapper.deleteBatchIds(ids);
    }
    @Override
    public void download(List<StoreAudit> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (StoreAudit storeRevision : all) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("审核ID", storeRevision.getAuditId());
            map.put("店铺ID", storeRevision.getStoreId());
            map.put("审批ID", storeRevision.getAuditId());
            map.put("类型", storeRevision.getType());
            map.put("新数据", storeRevision.getData());
            map.put("创建人", storeRevision.getCreateBy());
            map.put("创建时间", storeRevision.getCreateTime());
            map.put("修改人", storeRevision.getUpdateBy());
            map.put("修改时间", storeRevision.getUpdateTime());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreCreateServiceImpl.java
@@ -3,7 +3,7 @@
import com.oying.modules.pc.store.domain.Store;
import com.oying.modules.pc.store.domain.dto.StoreCreateRequest;
import com.oying.modules.pc.store.service.StoreCreateService;
import com.oying.modules.pc.store.service.StoreService;
import com.oying.modules.pc.store.service.StoreMerchantService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -14,10 +14,10 @@
@RequiredArgsConstructor
public class StoreCreateServiceImpl implements StoreCreateService {
    private final StoreService storeService;
    private final StoreMerchantService storeMerchantService;
    @Transactional(rollbackFor = Exception.class)
    public Store create(StoreCreateRequest request) {
        return storeService.create(request);
        return storeMerchantService.createStore(request);
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreMerchantServiceImpl.java
New file
@@ -0,0 +1,229 @@
package com.oying.modules.pc.store.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.oying.exception.BadRequestException;
import com.oying.modules.pc.common.ValueUpdate;
import com.oying.modules.pc.common.core.constrant.AuditStatusEnum;
import com.oying.modules.pc.store.converter.StoreAssembler;
import com.oying.modules.pc.store.converter.StoreQualificationAssembler;
import com.oying.modules.pc.store.domain.Store;
import com.oying.modules.pc.store.domain.StoreAudit;
import com.oying.modules.pc.store.domain.StoreQualification;
import com.oying.modules.pc.store.domain.dto.*;
import com.oying.modules.pc.store.domain.enums.StoreChangeTypeEnum;
import com.oying.modules.pc.store.domain.enums.StoreStatusEnum;
import com.oying.modules.pc.store.events.StoreAuditVerdictEvent;
import com.oying.modules.pc.store.service.StoreAuditService;
import com.oying.modules.pc.store.service.StoreMerchantService;
import com.oying.modules.pc.store.service.StoreQualificationService;
import com.oying.modules.pc.store.service.StoreService;
import com.oying.service.BucketStorageService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class StoreMerchantServiceImpl implements StoreMerchantService {
    private final BucketStorageService bucketStorageService;
    private final StoreService storeService;
    private final StoreQualificationService qualificationService;
    private final StoreAuditService auditService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Store createStore(StoreCreateRequest request) {
        Store store = StoreAssembler.to(request);
        storeService.create(store);
        this.processQualificationCreate(store, request.getQualificationList());
        storeService.bindUser(store.getStoreId());
        return store;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStore(StoreUpdateRequest request) {
        Store existingStore = storeService.getOrThrow(request.getStoreId(), request.getVersion());
        AuditStatusEnum existingStatus = AuditStatusEnum.getOrDefault(existingStore.getAuditStatus(), AuditStatusEnum.DRAFT);
        if (AuditStatusEnum.DRAFT.equals(existingStatus)) {
            this.processStoreImagesUpdate(request, existingStore);
            this.processQualificationUpdate(request.getDeletedQualificationIds(), request.getUpdatedQualifications(), request.getNewQualifications());
            BeanUtil.copyProperties(request, existingStore, CopyOptions.create().setIgnoreNullValue(true));
            storeService.update(existingStore, true);
        } else if (AuditStatusEnum.APPROVED.equals(existingStatus)) {
            Store newStore = new Store();
            newStore.setQualifications(ListUtil.of());
            BeanUtil.copyProperties(request, newStore, CopyOptions.create().setIgnoreNullValue(true));
            List<StoreQualification> existingQualifications = qualificationService.getByStoreId(request.getStoreId());
            if (CollectionUtil.isNotEmpty(request.getDeletedQualificationIds())) {
                List<StoreQualification> filteredQualifications = existingQualifications.stream()
                        .filter(qualification -> !request.getDeletedQualificationIds().contains(qualification.getQualificationId()))
                        .collect(Collectors.toList());
                newStore.getQualifications().addAll(filteredQualifications);
            }
            List<StoreQualification> updatedQualifications = ListUtil.of();
            if (CollectionUtil.isNotEmpty(request.getUpdatedQualifications())) {
                Set<Long> updatedQualificationIdSet = request.getUpdatedQualifications().stream().map(StoreQualificationUpdateRequest::getQualificationId).collect(Collectors.toSet());
                List<StoreQualification> filteredQualifications = newStore.getQualifications().stream()
                        .filter(qualification -> !updatedQualificationIdSet.contains(qualification.getQualificationId()))
                        .collect(Collectors.toList());
                newStore.setQualifications(filteredQualifications);
                request.getUpdatedQualifications().forEach(i -> {
                    StoreQualification qualificationUpdate = new StoreQualification();
                    BeanUtil.copyProperties(i, qualificationUpdate, CopyOptions.create().setIgnoreNullValue(true));
                    newStore.getQualifications().add(qualificationUpdate);
                    updatedQualifications.add(qualificationUpdate);
                });
            }
            if (CollectionUtil.isNotEmpty(request.getNewQualifications())) {
                newStore.getQualifications().addAll(request.getNewQualifications());
            }
            StoreRevisionRecord revisionRecord = new StoreRevisionRecord();
            BeanUtil.copyProperties(request, revisionRecord, CopyOptions.create().setIgnoreNullValue(true));
            StoreAuditData auditData = new StoreAuditData();
            auditData.setStore(newStore);
            auditData.setRevisionRecord(revisionRecord);
            this.createLatestModification(request.getStoreId(), StoreChangeTypeEnum.STORE_UPDATE, auditData);
        }
    }
    @Override
    public void updateBusinessStatus(Store resources) {
        StoreStatusEnum businessStatus = StoreStatusEnum.getOrDefault(resources.getBusinessStatus(), StoreStatusEnum.DRAFT);
        Store existingStore = storeService.getOrThrow(resources.getStoreId(), resources.getVersion());
        AuditStatusEnum existingStatus = AuditStatusEnum.getOrDefault(existingStore.getAuditStatus(), AuditStatusEnum.DRAFT);
        if (StoreStatusEnum.OPEN.equals(businessStatus) && !AuditStatusEnum.APPROVED.equals(existingStatus)) {
            throw new BadRequestException("店铺未审核");
        }
        storeService.updateStatus(existingStore.getStoreId(), businessStatus.getValue(), existingStore.getVersion());
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitStoreAudit(Long storeId) {
        Store existingStore = storeService.getById(storeId);
        // 更新店铺状态
        existingStore.setStatus(AuditStatusEnum.PENDING.getValue());
        existingStore.setAuditStatus(AuditStatusEnum.PENDING.getValue());
        storeService.update(existingStore, true);
        // 创建新的审核
        existingStore.setQualifications(qualificationService.getByStoreId(existingStore.getStoreId()));
        StoreRevisionRecord revisionRecord = new StoreRevisionRecord();
        BeanUtil.copyProperties(existingStore, revisionRecord, CopyOptions.create().setIgnoreNullValue(true));
        StoreAuditData auditData = new StoreAuditData();
        auditData.setStore(existingStore);
        auditData.setRevisionRecord(revisionRecord);
        this.createLatestModification(existingStore.getStoreId(), StoreChangeTypeEnum.NEW_STORE, auditData);
    }
    @Override
    @EventListener
    @Transactional(rollbackFor = Exception.class)
    public void handleStoreAudit(StoreAuditVerdictEvent event) {
        try {
            StoreAudit audit = auditService.getById(event.getAuditId());
            StoreChangeTypeEnum changeType = StoreChangeTypeEnum.valueOf(audit.getType());
            switch (changeType) {
                case NEW_STORE:
                    this.handleNewStoreAuditEvent(audit);
                    return;
                case STORE_UPDATE:
                    this.handleStoreUpdateAuditEvent(audit);
            }
        } catch (Exception e) {
            log.error("处理店铺审核结果异常", e);
        }
    }
    private void processQualificationCreate(Store store, List<StoreQualificationCreateRequest> qualificationRequests) {
        if (CollectionUtil.isNotEmpty(qualificationRequests)) {
            List<StoreQualification> storeQualificationList = qualificationRequests.stream().map(i -> {
                i.setStoreId(store.getStoreId());
                return StoreQualificationAssembler.to(i);
            }).collect(Collectors.toList());
            storeQualificationList.forEach(i -> i.setStoreId(store.getStoreId()));
            qualificationService.batchCreate(storeQualificationList);
            store.setQualifications(storeQualificationList);
        }
    }
    private List<Long> getDeleteImageIds(List<ValueUpdate<Long>> imageValues) {
        List<Long> deleteImageIds = new ArrayList<>();
        for (ValueUpdate<Long> imageValue : imageValues) {
            if (imageValue.isChangeAndOldValueNotEmpty()) {
                deleteImageIds.add(imageValue.getOldValue());
            }
        }
        return deleteImageIds;
    }
    private void processStoreImagesUpdate(StoreUpdateRequest request, Store existingStore) {
        List<ValueUpdate<Long>> imageValues = ListUtil.toList(
                new ValueUpdate<>(request.getLogoImageId(), existingStore.getLogoImageId()),
                new ValueUpdate<>(request.getCoverImageId(), existingStore.getCoverImageId())
        );
        List<Long> ids = this.getDeleteImageIds(imageValues);
        if (CollectionUtil.isNotEmpty(ids)) {
            bucketStorageService.deleteAll(ids);
        }
    }
    private void processQualificationUpdate(List<Long> ids, List<StoreQualificationUpdateRequest> updateRequests,
                                            List<StoreQualification> newQualifications) {
        if (CollectionUtil.isNotEmpty(ids)) {
            qualificationService.deleteAll(ids);
        }
        if (CollectionUtil.isNotEmpty(updateRequests)) {
            qualificationService.batchUpdate(updateRequests);
        }
        if (CollectionUtil.isNotEmpty(newQualifications)) {
            qualificationService.batchCreate(newQualifications);
        }
    }
    private void createLatestModification(Long storeId, StoreChangeTypeEnum changeType, StoreAuditData auditData) {
        if (auditService.hasPendingByStoreId(storeId)) {
            throw new BadRequestException("已有审核的内容");
        }
        StoreAudit storeAudit = new StoreAudit();
        storeAudit.setStoreId(storeId);
        storeAudit.setType(changeType.name());
        storeAudit.setData(JSON.toJSONString(auditData));
        storeAudit.setStatus(AuditStatusEnum.PENDING.getValue());
        auditService.create(storeAudit);
    }
    private void handleNewStoreAuditEvent(StoreAudit audit) {
        Store existingStore = storeService.getById(audit.getStoreId());
        existingStore.setStatus(audit.getStatus());
        existingStore.setAuditStatus(audit.getStatus());
        storeService.updateById(existingStore);
    }
    private void handleStoreUpdateAuditEvent(StoreAudit audit) {
        AuditStatusEnum auditStatus = AuditStatusEnum.get(audit.getStatus());
        if (AuditStatusEnum.APPROVED.equals(auditStatus)) {
            StoreAuditData data = JSONObject.parseObject(audit.getData(), StoreAuditData.class);
            Store existingStore = storeService.getById(audit.getStoreId());
            StoreRevisionRecord revisionRecord = data.getRevisionRecord();
            BeanUtil.copyProperties(revisionRecord, existingStore, CopyOptions.create().setIgnoreNullValue(true));
            storeService.updateById(existingStore);
            this.processQualificationUpdate(revisionRecord.getDeletedQualificationIds(), revisionRecord.getUpdatedQualifications(),
                    revisionRecord.getNewQualifications());
        }
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreQualificationServiceImpl.java
@@ -57,7 +57,7 @@
    }
    @Override
    public List<StoreQualification> queryByStoreId(Long storeId) {
    public List<StoreQualification> getByStoreId(Long storeId) {
        LambdaQueryWrapper<StoreQualification> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StoreQualification::getStoreId, storeId);
        return storeQualificationMapper.selectList(wrapper);
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreQueryServiceImpl.java
@@ -1,10 +1,9 @@
package com.oying.modules.pc.store.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.NumberUtil;
import com.oying.modules.pc.store.domain.Store;
import com.oying.modules.pc.store.domain.dto.StoreQueryCriteria;
import com.oying.modules.pc.store.mapper.StoreMapper;
import com.oying.modules.pc.store.service.StoreQueryService;
import com.oying.modules.pc.store.service.StoreService;
import com.oying.utils.PageResult;
@@ -27,7 +26,6 @@
    private final RedisTemplate<Object, Object> redisTemplate;
    private final StoreService storeService;
    private final StoreMapper storeMapper;
    @Override
    public PageResult<Store> findPagedStores(StoreQueryCriteria criteria) {
@@ -39,7 +37,7 @@
        Long total = redisTemplate.opsForZSet().size(setKey);
        if (total == null || total == 0) {
            // 初始化缓存
            List<Long> ids = storeMapper.queryStoreIds(criteria);
            List<Long> ids = storeService.queryStoreIds(criteria);
            int index = 0;
            for (Long id : ids) {
                redisTemplate.opsForZSet().add(setKey, id, index++);
@@ -56,17 +54,32 @@
        Set<Long> storeIds = Optional.ofNullable(redisTemplate.opsForZSet().range(setKey, offset, end))
                .orElse(Collections.emptySet())
                .stream()
                .map(i-> (Long) i)
                .map(i -> (Long) i)
                .collect(Collectors.toSet());
        // 3. 获取详情数据]
        List<Store> stores = Optional.of(storeIds)
                .filter(CollectionUtil::isNotEmpty)
                .map(storeService::listByIds)
                .map(storeService::getStoresByIds)
                .orElse(ListUtil.toList());
        stores.forEach(u -> u.setDeliveryDuration(distanceCalculation(criteria.getLatitude(), criteria.getLongitude(), u.getLatitude(), u.getLongitude())));
        page.setContent(stores);
        page.setTotalElements(total);
        return page;
    }
    private Integer distanceCalculation(double lat1, double lon1, double lat2, double lon2) {
        // 地球半径(千米)
        final int R = 6371;
        // 将纬度、经度从度转换为弧度
        double latDistance = Math.toRadians(lat2 - lat1);
        double lonDistance = Math.toRadians(lon2 - lon1);
        double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
                + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
                * Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return new Double(R * c * 1000).intValue();
    }
}
oying-system/src/main/java/com/oying/modules/pc/store/service/impl/StoreServiceImpl.java
@@ -1,7 +1,5 @@
package com.oying.modules.pc.store.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@@ -9,21 +7,15 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.oying.exception.BadRequestException;
import com.oying.exception.EntityNotFoundException;
import com.oying.modules.pc.common.ValueUpdate;
import com.oying.modules.pc.store.converter.StoreAssembler;
import com.oying.modules.pc.store.converter.StoreQualificationAssembler;
import com.oying.modules.pc.store.domain.Store;
import com.oying.modules.pc.store.domain.StoreQualification;
import com.oying.modules.pc.store.domain.StoreUser;
import com.oying.modules.pc.store.domain.dto.StoreCreateRequest;
import com.oying.modules.pc.store.domain.dto.StoreQualificationCreateRequest;
import com.oying.modules.pc.store.domain.dto.StoreQueryCriteria;
import com.oying.modules.pc.store.domain.dto.StoreUpdateRequest;
import com.oying.modules.pc.store.mapper.StoreMapper;
import com.oying.modules.pc.store.mapper.StoreUserMapper;
import com.oying.modules.pc.store.service.StoreQualificationService;
import com.oying.modules.pc.store.service.StoreService;
import com.oying.service.BucketStorageService;
import com.oying.utils.PageResult;
import com.oying.utils.PageUtil;
import com.oying.utils.SecurityUtils;
@@ -35,11 +27,10 @@
import java.math.BigDecimal;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.Set;
/**
 * 店铺Service业务层处理
@@ -54,8 +45,6 @@
    private final StoreMapper storeMapper;
    private final StoreUserMapper storeUserMapper;
    private final StoreQualificationService qualificationService;
    private final BucketStorageService bucketStorageService;
    @Override
    public PageResult<Store> queryByPage(StoreQueryCriteria criteria) {
@@ -69,13 +58,44 @@
    }
    @Override
    public List<Long> queryStoreIds(StoreQueryCriteria criteria) {
        return storeMapper.queryStoreIds(criteria);
    }
    @Override
    public Store getMerchantStore(Long merchantId) {
        return storeMapper.selectStoreByMerchantId(merchantId);
    }
    @Override
    public List<Store> queryUserStores(Long userId) {
    public List<Store> getStoresByIds(Set<Long> ids) {
        return storeMapper.queryByIds(ids);
    }
    @Override
    public List<Store> getUserStores(Long userId) {
        return storeMapper.queryUserStores(userId);
    }
    @Override
    public Store getOrThrow(Long storeId, Long version) {
        Objects.requireNonNull(storeId, String.format("方法 '%s' 调用失败:参数 'storeId'(店铺ID)不能为 null", "findOrThrow"));
        LambdaQueryWrapper<Store> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Store::getStoreId, storeId);
        if (ObjUtil.isNotEmpty(version)) {
            wrapper.eq(Store::getVersion, version);
        }
        Store existingStore = storeMapper.selectOne(wrapper);
        if (ObjUtil.isEmpty(existingStore)) {
            throw new EntityNotFoundException(Store.class, "storeId", storeId.toString());
        }
        return existingStore;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(Store resources) {
        storeMapper.insert(resources);
    }
    @Override
@@ -88,17 +108,26 @@
        // 创建店铺
        Store store = StoreAssembler.to(request);
        storeMapper.insert(store);
        this.processQualificationCreate(store, request.getQualificationList());
        this.bindUser(store.getStoreId());
        return store;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean update(Store resources, boolean isDirectUpdate) {
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(resources.getStoreId(), resources.getVersion());
        if (isDirectUpdate) {
            return this.update(resources, wrapper);
        } else {
            Store existingStore = this.getById(resources.getStoreId());
            existingStore.copy(resources);
            return this.update(existingStore, wrapper);
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean update(StoreUpdateRequest request) {
        Store existingStore = this.findOrThrow(request);
        this.processImagesUpdate(request, existingStore);
        this.processQualificationUpdate(request);
        Store existingStore = this.getOrThrow(request.getStoreId(), request.getVersion());
        existingStore.copy(StoreAssembler.to(request));
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(existingStore.getStoreId(), existingStore.getVersion());
        return this.update(existingStore, wrapper);
@@ -106,16 +135,16 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateLogo(Long storeId, String logo, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
    public boolean updateLogo(Long storeId, Long logoImageId, Long version) {
        Store existingStore = this.getOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getLogoImageId, logo);
                .set(Store::getLogoImageId, logoImageId);
        return update(wrapper);
    }
    @Override
    public boolean updateName(Long storeId, String storeName, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        Store existingStore = this.getOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getStoreName, storeName);
        return update(wrapper);
@@ -123,7 +152,7 @@
    @Override
    public boolean updateDescription(Long storeId, String description, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        Store existingStore = this.getOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getDescription, description);
        return update(wrapper);
@@ -131,7 +160,7 @@
    @Override
    public boolean updateContactPhone(Long storeId, String contactPhone, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        Store existingStore = this.getOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getContactPhone, contactPhone);
        return update(wrapper);
@@ -139,7 +168,7 @@
    @Override
    public boolean updateAddress(Long storeId, String address, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        Store existingStore = this.getOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getAddress, address);
        return update(wrapper);
@@ -147,7 +176,7 @@
    @Override
    public boolean updateLocation(Long storeId, Double longitude, Double latitude, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        Store existingStore = this.getOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getLongitude, longitude)
                .set(Store::getLatitude, latitude);
@@ -156,7 +185,7 @@
    @Override
    public boolean updateRadius(Long storeId, Integer radius, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        Store existingStore = this.getOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getRadius, radius);
        return update(wrapper);
@@ -164,7 +193,7 @@
    @Override
    public boolean updatePlatformCategory(Long storeId, Long platformCategory, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        Store existingStore = this.getOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getPlatformCategoryId, platformCategory);
        return update(wrapper);
@@ -172,7 +201,7 @@
    @Override
    public boolean updateBusinessHours(Long storeId, LocalTime openTime, LocalTime endTime, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        Store existingStore = this.getOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getOpenTime, openTime)
                .set(Store::getCloseTime, endTime);
@@ -181,7 +210,7 @@
    @Override
    public boolean updateDeliveryMinimum(Long storeId, BigDecimal deliveryMinimum, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        Store existingStore = this.getOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getDeliveryMinimum, deliveryMinimum);
        return update(wrapper);
@@ -189,7 +218,7 @@
    @Override
    public boolean updateDeliveryFee(Long storeId, BigDecimal deliveryFee, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        Store existingStore = this.getOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getDeliveryFee, deliveryFee);
        return update(wrapper);
@@ -197,7 +226,7 @@
    @Override
    public boolean updateStatus(Long storeId, Integer status, Long version) {
        Store existingStore = this.findOrThrow(storeId, version);
        Store existingStore = this.getOrThrow(storeId, version);
        LambdaUpdateWrapper<Store> wrapper = this.createLambdaUpdateWrapper(storeId, existingStore.getVersion())
                .set(Store::getStatus, status);
        return update(wrapper);
@@ -214,32 +243,23 @@
    }
    @Override
    public boolean existsByIdAndMerchantId(Long storeId, Long merchantId) {
        if (storeId == null || merchantId == null) {
            return false;
        }
        Store store = getById(storeId);
        return Optional.ofNullable(store).map(i -> merchantId.equals(i.getMerchantId())).orElse(false);
    }
    private void processQualificationCreate(Store store, List<StoreQualificationCreateRequest> qualificationRequests) {
        if (CollectionUtil.isNotEmpty(qualificationRequests)) {
            List<StoreQualification> storeQualificationList = qualificationRequests.stream().map(i -> {
                i.setStoreId(store.getStoreId());
                return StoreQualificationAssembler.to(i);
            }).collect(Collectors.toList());
            storeQualificationList.forEach(i -> i.setStoreId(store.getStoreId()));
            qualificationService.batchCreate(storeQualificationList);
        }
    }
    private void bindUser(Long storeId) {
    public boolean bindUser(Long storeId) {
        StoreUser storeUser = new StoreUser();
        storeUser.setStoreId(storeId);
        storeUser.setUserId(SecurityUtils.getCurrentUserId());
        storeUser.setRoleType("");
        storeUser.setPermissions("");
        storeUserMapper.insert(storeUser);
        return true;
    }
    @Override
    public boolean existsByIdAndMerchantId(Long storeId, Long merchantId) {
        if (storeId == null || merchantId == null) {
            return false;
        }
        Store store = getById(storeId);
        return Optional.ofNullable(store).map(i -> merchantId.equals(i.getMerchantId())).orElse(false);
    }
    private LambdaUpdateWrapper<Store> createLambdaUpdateWrapper(Long storeId, Long version) {
@@ -250,55 +270,6 @@
            updateWrapper.eq(Store::getVersion, version);
            updateWrapper.set(Store::getVersion, version + 1);
        }
        updateWrapper.set(Store::getUpdateBy, SecurityUtils.getCurrentUserId());
        return updateWrapper;
    }
    private Store findOrThrow(StoreUpdateRequest request) {
        return this.findOrThrow(request.getStoreId(), request.getVersion());
    }
    private Store findOrThrow(Long storeId, Long version) {
        Objects.requireNonNull(storeId, String.format("方法 '%s' 调用失败:参数 'storeId'(店铺ID)不能为 null", "findOrThrow"));
        LambdaQueryWrapper<Store> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Store::getStoreId, storeId);
        if (ObjUtil.isNotEmpty(version)) {
            wrapper.eq(Store::getVersion, version);
        }
        Store existingStore = storeMapper.selectOne(wrapper);
        if (ObjUtil.isEmpty(existingStore)) {
            throw new EntityNotFoundException(Store.class, "storeId", storeId.toString());
        }
        return existingStore;
    }
    private List<Long> getDeleteImageIds(List<ValueUpdate<Long>> imageValues) {
        List<Long> deleteImageIds = new ArrayList<>();
        for (ValueUpdate<Long> imageValue : imageValues) {
            if (imageValue.isChangeAndOldValueNotEmpty()) {
                deleteImageIds.add(imageValue.getOldValue());
            }
        }
        return deleteImageIds;
    }
    private void processImagesUpdate(StoreUpdateRequest request, Store existingStore) {
        List<ValueUpdate<Long>> imageValues = ListUtil.toList(
                new ValueUpdate<>(request.getLogoImageId(), existingStore.getLogoImageId()),
                new ValueUpdate<>(request.getCoverImageId(), existingStore.getCoverImageId())
        );
        bucketStorageService.deleteAll(this.getDeleteImageIds(imageValues));
    }
    private void processQualificationUpdate(StoreUpdateRequest request) {
        if (CollectionUtil.isNotEmpty(request.getDeletedQualificationIds())) {
            qualificationService.deleteAll(request.getDeletedQualificationIds());
        }
        if (CollectionUtil.isNotEmpty(request.getUpdatedQualifications())) {
            qualificationService.batchUpdate(request.getUpdatedQualifications());
        }
        if (CollectionUtil.isNotEmpty(request.getNewQualifications())) {
            qualificationService.batchCreate(request.getNewQualifications());
        }
    }
}
oying-system/src/main/resources/mapper/pc/product/ProductAuditMapper.xml
New file
@@ -0,0 +1,37 @@
<?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.oying.modules.pc.product.mapper.ProductAuditMapper">
    <resultMap id="BaseResultMap" type="com.oying.modules.pc.product.domain.ProductAudit">
        <id column="audit_id" property="auditId"/>
        <result column="product_id" property="productId"/>
        <result column="type" property="type"/>
        <result column="status" property="status"/>
        <result column="data" property="data"/>
        <result column="reason" property="reason"/>
        <result column="auditor" property="auditor"/>
        <result column="audit_time" property="auditTime"/>
        <result column="create_by" property="createBy"/>
        <result column="create_time" property="createTime"/>
        <result column="update_by" property="updateBy"/>
        <result column="update_time" property="updateTime"/>
    </resultMap>
    <sql id="Base_Column_List">
        audit_id, product_id, type, status, reason, auditor, audit_time, data, create_by, create_time, update_by, update_time
    </sql>
    <select id="findAll" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from pc_product_audit
        <where>
            <if test="criteria.productId != null">
                and product_id = #{criteria.productId}
            </if>
            <if test="criteria.type != null and criteria.type != ''">
                and type = #{criteria.type}
            </if>
        </where>
        order by audit_id desc
    </select>
</mapper>
oying-system/src/main/resources/mapper/pc/store/StoreAuditMapper.xml
New file
@@ -0,0 +1,37 @@
<?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.oying.modules.pc.store.mapper.StoreAuditMapper">
    <resultMap id="BaseResultMap" type="com.oying.modules.pc.store.domain.StoreAudit">
        <id column="audit_id" property="auditId"/>
        <result column="store_id" property="storeId"/>
        <result column="type" property="type"/>
        <result column="status" property="status"/>
        <result column="data" property="data"/>
        <result column="reason" property="reason"/>
        <result column="auditor" property="auditor"/>
        <result column="audit_time" property="auditTime"/>
        <result column="create_by" property="createBy"/>
        <result column="create_time" property="createTime"/>
        <result column="update_by" property="updateBy"/>
        <result column="update_time" property="updateTime"/>
    </resultMap>
    <sql id="Base_Column_List">
        audit_id, store_id, type, status, reason, auditor, audit_time, data, create_by, create_time, update_by, update_time
    </sql>
    <select id="findAll" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from pc_store_audit
        <where>
            <if test="criteria.storeId != null">
                and store_id = #{criteria.storeId}
            </if>
            <if test="criteria.type != null and criteria.type != ''">
                and type = #{criteria.type}
            </if>
        </where>
        order by audit_id desc
    </select>
</mapper>
oying-system/src/main/resources/mapper/pc/store/StoreMapper.xml
@@ -13,8 +13,12 @@
        <result property="storeName" column="store_name"/>
        <result property="businessScope" column="business_scope"/>
        <result property="status" column="status"/>
        <result property="businessStatus" column="business_status"/>
        <result property="auditStatus" column="audit_status"/>
        <result property="logoImageId" column="logo_image_id"/>
        <result property="logoImageUrl" column="logo_image_url"/>
        <result property="coverImageId" column="cover_image_id"/>
        <result property="coverImageUrl" column="cover_image_url"/>
        <result property="description" column="description"/>
        <result property="tags" column="tags"/>
        <result property="deliveryFee" column="delivery_fee"/>
@@ -28,25 +32,25 @@
        <result property="geoHash" column="geo_hash"/>
        <result property="coordinateSystem" column="coordinate_system"/>
        <result property="radius" column="radius"/>
        <result column="returns" property="returns"/>
        <result column="self_pickup" property="selfPickup"/>
        <result property="returns" column="returns" />
        <result property="selfPickup" column="self_pickup"/>
        <result property="createBy" column="create_by"/>
        <result property="createTime" column="create_time"/>
        <result property="updateBy" column="update_by"/>
        <result property="updateTime" column="update_time"/>
        <result column="version" property="version"/>
        <result property="version" column="version"/>
    </resultMap>
    <sql id="store_column_list">
        store_id, merchant_id, platform_category_id, store_type, store_code, store_name, business_scope, status, logo_image_id,
        cover_image_id, description, tags, delivery_fee, delivery_minimum, contact_phone, open_time, close_time,
        store_id, merchant_id, platform_category_id, store_type, store_code, store_name, business_scope, status, business_status, audit_status, logo_image_id, logo_image_url,
        cover_image_id, cover_image_url, description, tags, delivery_fee, delivery_minimum, contact_phone, open_time, close_time,
        address, longitude, latitude, geo_hash, geo_point, coordinate_system, radius, returns, self_pickup, create_by, create_time, update_by, update_time, version
    </sql>
    <sql id="selectStoreVo">
        select
        store_id, merchant_id, platform_category_id, store_type, store_code, store_name, business_scope, status, logo_image_id,
        cover_image_id, description, tags, delivery_fee, delivery_minimum, contact_phone, open_time, close_time,
        store_id, merchant_id, platform_category_id, store_type, store_code, store_name, business_scope, status, business_status, audit_status, logo_image_id, logo_image_url,
        cover_image_id, cover_image_url, description, tags, delivery_fee, delivery_minimum, contact_phone, open_time, close_time,
        address, longitude, latitude, geo_hash, geo_point, coordinate_system, radius, returns, self_pickup, create_by, create_time, update_by, update_time, version from pc_store
    </sql>
@@ -126,8 +130,8 @@
            </if>
            <!-- 营业状态 -->
            <if test="criteria.status != null">
                AND s.status = #{criteria.status}
            <if test="criteria.businessStatus != null">
                AND s.business_status = #{criteria.businessStatus}
            </if>
            <!-- 类目ID -->
@@ -140,6 +144,16 @@
        </if>
    </select>
    <select id="queryByIds" resultMap="StoreResult">
        SELECT
            <include refid="store_column_list"/>
        FROM pc_store
        WHERE store_id IN
        <foreach collection="ids" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </select>
    <select id="queryUserStores" parameterType="java.lang.Long" resultMap="StoreResult">
        SELECT s.*
        FROM sys_user_store u INNER JOIN pc_store s ON u.store_id = s.store_id